Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-8145

Cross-project @Log + static import issue

    XMLWordPrintableJSON

Details

    Description

      I have discovered a situation in which doing a static import of all members of a class causes a compilation error as the compiler incorrectly infers that the code is attempting to use a private static member on the imported class.

      There appears to be a very specific combination of factors that causes this to occur. Specifically, the class that's doing the static import must be statically compiled using the @CompileStatic annotation, and both classes must share a logger variable with the same name from @Log (or @Slf4j, etc.). Furthermore, the classes cannot be compiled at the same time (for instance, the one could be in a jar or a sub-build in Gradle).

      The following is a specific Gradle example that produces the issue.

      First, the build structure:

      import-static-issue
        src/main/groovy/test
          build.gradle
          settings.gradle
          Importer.groovy
          SameProjectImported.groovy
        subproject
          src/main/groovy/test/DifferentProjectImported.groovy
      

      And the relevant files:

      build.gradle
      group 'test'
      version '1.0-SNAPSHOT'
      
      allprojects {
        apply plugin: 'groovy'
        apply plugin: 'java'
      
        sourceCompatibility = 1.8
      
        repositories {
          mavenCentral()
        }
      
        dependencies {
          compile 'org.codehaus.groovy:groovy-all:2.4.10'
        }
      }
      
      dependencies {
        compile project(':subproject')
      }
      
      settings.gradle
      include 'subproject'
      
      Importer.groovy
      package test
      
      import groovy.transform.CompileStatic
      import groovy.util.logging.Log
      
      //import static test.SameProjectImported.* //Using this one instead won't trigger the issue
      import static test.DifferentProjectImported.*
      
      @Log
      @CompileStatic
      class Importer {
        static void main(String[] args) {
          log.info 'This will cause a compilation failure'
        }
      }
      
      SameProjectImported.groovy
      package test
      
      import groovy.util.logging.Log
      
      @Log
      class SameProjectImported {
      }
      
      DifferentProjectImported.groovy
      package test
      
      import groovy.util.logging.Log
      
      @Log
      class DifferentProjectImported {
        // private static java.util.logging.Logger log // Using this instead of @Log will also cause the error
      }
      

      Compiling this code via Gradle (e.g. "gradle compileGroovy") produces the following error:

      /[...]/import-static-issue/src/main/groovy/test/Importer.groovy: -1: Access to test.DifferentProjectImported#log is forbidden @ line -1, column -1.

      I have confirmed the issue in Groovy 2.4.10. I have also confirmed that this issue appears to have been present for the whole Groovy 2.x series.

      It doesn't occur if you manually create the private static field in the importing class instead of using the @Log AST transformation. It will occur if you manually create the static "log" field in the statically imported class but use @Log in the importing class. It doesn't occur if the importing class doesn't use @CompileStatic. It doesn't occur if the importing class and the imported class are in the same Gradle project. The issue also occurs if you statically import just the "log" field and not "*" (though practically speaking, you should never actually try to do that).

      I ran across this issue when I was migrating some code into a shared library and was statically importing a number of fields for a class that was being moved. Manually declaring the fields to be statically imported would work, but my IDE settings would fold it back into "*" if the imports are ever optimized for the class, so this isn't a workable solution for me (if nothing else, it would be supremely confusing for any developers down the road).

      Edit: Upon further investigation, there is a different but related issue if the importing class is not annotated with @CompileStatic. If it's not statically compiled, rather than throwing a compilation failure, it will instead use the imported private log variable, not its own.

      Attachments

        Issue Links

          Activity

            People

              emilles Eric Milles
              mjjustin M. Justin
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: