Groovy
  1. Groovy
  2. GROOVY-4434

Java stub generator doesn't use FQN for annotation values

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: 1.7.5
    • Fix Version/s: 1.7.6, 1.8-beta-3
    • Component/s: Compiler
    • Labels:
      None

      Description

      After upgrading our app from 1.7.4 to 1.7.5, we got several (joint) compile errors for seemingly simple Groovy classes. Looking at the generated stub code, I found that imports for annotations were always missing. After debugging groovyc, I found the following:

      AntlrParserPlugin:333: addImport() is called with alias==null, which is correct since no import alias is used
      ASTHelper:131: if(aliasName==null) aliasName=name // now alias is non-null
      JavaStubGenerator:679: if (imp.getAlias() == null) imports.add(...); // because getAlias() is non-null, import isn't added

      Code example:

      import foo.Foo // details of Foo don't matter
         
      @Foo
      class Bar {} 
      

      For GMaven users this means that if just one Groovy class in the whole project contains an annotation, the project cannot be compiled.

      1. 4434_v18x.diff
        12 kB
        Roshan Dawrani

        Activity

        Hide
        Peter Niederwieser added a comment -

        Correction: The problem isn't the missing imports for the annotation types themselves (because the stub generator now uses fully qualified class names for them), but the missing imports for class names appearing in annotation attributes. Example:

        package bar
        
        import foo.Foo
        import other.MyEnum
        
        // generated stub code: @foo.Foo(MyEnum.MY_VALUE)
        @Foo(val = MyEnum.MY_VALUE)
        class Bar {}
        

        Same problem exists for variations like @Foo(val=MyClass) and @Foo(val=MyClass.SOME_CONST).

        Show
        Peter Niederwieser added a comment - Correction: The problem isn't the missing imports for the annotation types themselves (because the stub generator now uses fully qualified class names for them), but the missing imports for class names appearing in annotation attributes. Example: package bar import foo.Foo import other.MyEnum // generated stub code: @foo.Foo(MyEnum.MY_VALUE) @Foo(val = MyEnum.MY_VALUE) class Bar {} Same problem exists for variations like @Foo(val=MyClass) and @Foo(val=MyClass.SOME_CONST) .
        Hide
        Jochen Theodorou added a comment -

        shouldn't the stub generator use fully qualified names there too then?

        Show
        Jochen Theodorou added a comment - shouldn't the stub generator use fully qualified names there too then?
        Hide
        Peter Niederwieser added a comment -

        Maybe. I don't know the exact reason for the switch to fully qualified class names in 1.7.5, and if it's done for all class names.

        Show
        Peter Niederwieser added a comment - Maybe. I don't know the exact reason for the switch to fully qualified class names in 1.7.5, and if it's done for all class names.
        Hide
        Roshan Dawrani added a comment -

        Attaching a patch that fixes the 3 related issues highlighted in here.

        Show
        Roshan Dawrani added a comment - Attaching a patch that fixes the 3 related issues highlighted in here.
        Hide
        Guillaume Delcroix added a comment -

        Looking great!

        Show
        Guillaume Delcroix added a comment - Looking great!
        Hide
        Peter Niederwieser added a comment -

        I'm still getting the same problem with https://svn.codehaus.org/groovy/branches/GROOVY_1_7_Xtrunk, rev. 20934:

        foo/MyAnno.java:

        package foo;
        
        public @interface MyAnno {
          MyEnum val();
        }
        

        foo/MyEnum.java:

        package foo;
        
        public enum MyEnum { ONE, TWO }
        

        bar/MyUser.groovy:

        package bar
        
        import foo.MyAnno
        import foo.MyEnum
        
        @MyAnno(val = MyEnum.ONE)
        class MyUser {}                  
        

        Generated stub for class MyUser:

        package bar;
        
        import java.lang.*;
        import java.io.*;
        import java.net.*;
        import java.util.*;
        import groovy.lang.*;
        import groovy.util.*;
        
        @foo.MyAnno(val=MyEnum.ONE) public class MyUser
          extends java.lang.Object  implements
            groovy.lang.GroovyObject {
        public MyUser
        () {}
        public  groovy.lang.MetaClass getMetaClass() { return (groovy.lang.MetaClass)null;}
        public  void setMetaClass(groovy.lang.MetaClass mc) { }
        public  java.lang.Object invokeMethod(java.lang.String method, java.lang.Object arguments) { return null;}
        public  java.lang.Object getProperty(java.lang.String property) { return null;}
        public  void setProperty(java.lang.String property, java.lang.Object value) { }
        }
        

        Compile error:

        [ERROR] /swd/prj/joints/target/generated-sources/groovy-stubs/main/bar/MyUser.java:[10,16] cannot find symbol
        symbol: variable MyEnum
        @foo.MyAnno(val=MyEnum.ONE) public class MyUser
        
        [ERROR] /swd/prj/joints/target/generated-sources/groovy-stubs/main/bar/MyUser.java:[10,22] an enum annotation value must be an enum constant
        
        Show
        Peter Niederwieser added a comment - I'm still getting the same problem with https://svn.codehaus.org/groovy/branches/GROOVY_1_7_Xtrunk , rev. 20934: foo/MyAnno.java: package foo; public @ interface MyAnno { MyEnum val(); } foo/MyEnum.java: package foo; public enum MyEnum { ONE, TWO } bar/MyUser.groovy: package bar import foo.MyAnno import foo.MyEnum @MyAnno(val = MyEnum.ONE) class MyUser {} Generated stub for class MyUser: package bar; import java.lang.*; import java.io.*; import java.net.*; import java.util.*; import groovy.lang.*; import groovy.util.*; @foo.MyAnno(val=MyEnum.ONE) public class MyUser extends java.lang. Object implements groovy.lang.GroovyObject { public MyUser () {} public groovy.lang.MetaClass getMetaClass() { return (groovy.lang.MetaClass) null ;} public void setMetaClass(groovy.lang.MetaClass mc) { } public java.lang. Object invokeMethod(java.lang. String method, java.lang. Object arguments) { return null ;} public java.lang. Object getProperty(java.lang. String property) { return null ;} public void setProperty(java.lang. String property, java.lang. Object value) { } } Compile error: [ERROR] /swd/prj/joints/target/generated-sources/groovy-stubs/main/bar/MyUser.java:[10,16] cannot find symbol symbol: variable MyEnum @foo.MyAnno(val=MyEnum.ONE) public class MyUser [ERROR] /swd/prj/joints/target/generated-sources/groovy-stubs/main/bar/MyUser.java:[10,22] an enum annotation value must be an enum constant
        Hide
        Roshan Dawrani added a comment -

        I just ran the following test on trunk and stub gets generated just fine. You want to check what's wrong with your thing?

        package org.codehaus.groovy.tools.stubgenerator
        
        class Test4434Again extends StringSourcesStubTestCase {
        
            Map<String, String> provideSources() {
                debug = true
                delete = false
                [
                    'foo/MyAnno.java': '''
                        package foo;
        
                        public @interface MyAnno {
                          MyEnum val();
                        }
                    ''',
                    'foo/MyEnum.java': '''
                        package foo;
        
                        public enum MyEnum { ONE, TWO }
                    ''',
                    'bar/MyUser.groovy': '''
                        package bar
        
                        import foo.MyAnno
                        import foo.MyEnum
                        
                        @MyAnno(val = MyEnum.ONE)
                        class MyUser {}
                    '''
                ]
            }
        
            void verifyStubs() {
                // all is well
            }
        }
        

        Here is how the stub gets generated (notice the part - @foo.MyAnno(val=foo.MyEnum.ONE) )

        Sources compiled successfully
        >>> Stubs generated
         -> MyUser.java
        package bar;
        
        import java.lang.*;
        import java.io.*;
        import java.net.*;
        import java.util.*;
        import groovy.lang.*;
        import groovy.util.*;
        
        @foo.MyAnno(val=foo.MyEnum.ONE) public class MyUser
          extends java.lang.Object  implements
            groovy.lang.GroovyObject {
        public MyUser
        () {}
        public  groovy.lang.MetaClass getMetaClass() { return (groovy.lang.MetaClass)null;}
        public  void setMetaClass(groovy.lang.MetaClass mc) { }
        public  java.lang.Object invokeMethod(java.lang.String method, java.lang.Object arguments) { return null;}
        public  java.lang.Object getProperty(java.lang.String property) { return null;}
        public  void setProperty(java.lang.String property, java.lang.Object value) { }
        }
        
        Show
        Roshan Dawrani added a comment - I just ran the following test on trunk and stub gets generated just fine. You want to check what's wrong with your thing? package org.codehaus.groovy.tools.stubgenerator class Test4434Again extends StringSourcesStubTestCase { Map< String , String > provideSources() { debug = true delete = false [ 'foo/MyAnno.java': ''' package foo; public @ interface MyAnno { MyEnum val(); } ''', 'foo/MyEnum.java': ''' package foo; public enum MyEnum { ONE, TWO } ''', 'bar/MyUser.groovy': ''' package bar import foo.MyAnno import foo.MyEnum @MyAnno(val = MyEnum.ONE) class MyUser {} ''' ] } void verifyStubs() { // all is well } } Here is how the stub gets generated (notice the part - @foo.MyAnno(val=foo.MyEnum.ONE) ) Sources compiled successfully >>> Stubs generated -> MyUser.java package bar; import java.lang.*; import java.io.*; import java.net.*; import java.util.*; import groovy.lang.*; import groovy.util.*; @foo.MyAnno(val=foo.MyEnum.ONE) public class MyUser extends java.lang.Object implements groovy.lang.GroovyObject { public MyUser () {} public groovy.lang.MetaClass getMetaClass() { return (groovy.lang.MetaClass)null;} public void setMetaClass(groovy.lang.MetaClass mc) { } public java.lang.Object invokeMethod(java.lang.String method, java.lang.Object arguments) { return null;} public java.lang.Object getProperty(java.lang.String property) { return null;} public void setProperty(java.lang.String property, java.lang.Object value) { } }
        Hide
        Peter Niederwieser added a comment -

        No idea what's going on then. I'm definitely using the latest 1.7.6-SNAPSHOT. The only difference is that I'm building with GMaven because I don't know of a good way to diagnose joint compilation problems with groovyc (other than with a debugger).

        Show
        Peter Niederwieser added a comment - No idea what's going on then. I'm definitely using the latest 1.7.6-SNAPSHOT. The only difference is that I'm building with GMaven because I don't know of a good way to diagnose joint compilation problems with groovyc (other than with a debugger).
        Hide
        Roshan Dawrani added a comment -

        I have no idea about GMaven.

        The new joint compilation test infrastructure is one of my favorite pieces in groovy now!

        Please run that test I have provided and tell me how the stub gets generated. If that is fine, the GMaven bit needs to be taken forward outside this JIRA issue.

        Show
        Roshan Dawrani added a comment - I have no idea about GMaven. The new joint compilation test infrastructure is one of my favorite pieces in groovy now! Please run that test I have provided and tell me how the stub gets generated. If that is fine, the GMaven bit needs to be taken forward outside this JIRA issue.
        Hide
        Peter Niederwieser added a comment -

        All tests passed. No idea how I can find this test in the JUnit report because class names are missing in the report, but I assume it got run.

        Show
        Peter Niederwieser added a comment - All tests passed. No idea how I can find this test in the JUnit report because class names are missing in the report, but I assume it got run.
        Hide
        Roshan Dawrani added a comment -

        Which all tests? (if you mean the ones that are checked-in, of-course they pass!)

        If u run the one test I provided just a few minutes back, it is run with 2 flags (debug = true, delete = false), which means that it will output stub code created and also leave the stub file un-deleted, which you should be able to go and inspect.

        Both with trunk and 1.7.6, the code generated is fine.

        Show
        Roshan Dawrani added a comment - Which all tests? (if you mean the ones that are checked-in, of-course they pass!) If u run the one test I provided just a few minutes back, it is run with 2 flags (debug = true, delete = false), which means that it will output stub code created and also leave the stub file un-deleted, which you should be able to go and inspect. Both with trunk and 1.7.6, the code generated is fine.
        Hide
        Peter Niederwieser added a comment -

        > Which all tests?
        All existing tests, plus the one you provided. I just added it to the sources (locally).

        > If u run the one test I provided just a few minutes back
        How do I run a single test class in the Ant build? Running tests in the Groovy project from IDEA is always problematic.

        Show
        Peter Niederwieser added a comment - > Which all tests? All existing tests, plus the one you provided. I just added it to the sources (locally). > If u run the one test I provided just a few minutes back How do I run a single test class in the Ant build? Running tests in the Groovy project from IDEA is always problematic.
        Hide
        Roshan Dawrani added a comment - - edited

        > How do I run a single test class in the Ant build? Running tests in the Groovy project from IDEA is always problematic.

        Set testCase=<full test class name> in build.properties

        Show
        Roshan Dawrani added a comment - - edited > How do I run a single test class in the Ant build? Running tests in the Groovy project from IDEA is always problematic. Set testCase=<full test class name> in build.properties
        Hide
        Peter Niederwieser added a comment -

        Thanks for the Ant hint. I've meanwhile managed to run the test from IDEA and it passes. Output looks fine. Seems I'll have to debug GMaven again.

        Show
        Peter Niederwieser added a comment - Thanks for the Ant hint. I've meanwhile managed to run the test from IDEA and it passes. Output looks fine. Seems I'll have to debug GMaven again.
        Hide
        Peter Niederwieser added a comment -

        By the way, "ant test -DtestCase=org.codehaus.groovy.tools.stubgenerator.Test4434Again" also works.

        Show
        Peter Niederwieser added a comment - By the way, "ant test -DtestCase=org.codehaus.groovy.tools.stubgenerator.Test4434Again" also works.
        Hide
        Roshan Dawrani added a comment -

        Great. So I will mark this one back as Resolved since it's in GMaven domain now?

        Show
        Roshan Dawrani added a comment - Great. So I will mark this one back as Resolved since it's in GMaven domain now?
        Hide
        Guillaume Delcroix added a comment -

        Btw, for running a single test, on the command-line, you can do:

        ant -DtestCase=groovy.bugs.ABug test
        
        Show
        Guillaume Delcroix added a comment - Btw, for running a single test, on the command-line, you can do: ant -DtestCase=groovy.bugs.ABug test
        Hide
        Peter Niederwieser added a comment -

        > Great. So I will mark this one back as Resolved since it's in GMaven domain now?
        Do as you please. Depending on my findings, I will reopen, add a GMaven issue, or bang my head against the wall (if it's a problem with my setup).

        Show
        Peter Niederwieser added a comment - > Great. So I will mark this one back as Resolved since it's in GMaven domain now? Do as you please. Depending on my findings, I will reopen, add a GMaven issue, or bang my head against the wall (if it's a problem with my setup).
        Hide
        Roshan Dawrani added a comment -

        Reopen anytime you can be sure it is a core groovy issue, but don't just pick blindly from your 3 choices and reopen

        Show
        Roshan Dawrani added a comment - Reopen anytime you can be sure it is a core groovy issue, but don't just pick blindly from your 3 choices and reopen
        Hide
        Roshan Dawrani added a comment - - edited

        Not a core groovy issue - explained why/how.

        Show
        Roshan Dawrani added a comment - - edited Not a core groovy issue - explained why/how.
        Hide
        Peter Niederwieser added a comment - - edited

        Looking at this with the debugger, the only explanation I have is that GMaven's JavaStubCompilationUnit behaves slightly differently than groovyc's JavaAwareCompilationUnit (JavaStubCompilationUnit vs. JavaAwareCompilationUnit is the only difference in the stack trace). With groovyc, AST for annotation attribute value MyEnum.ONE looks like this:

        org.codehaus.groovy.ast.expr.PropertyExpression@4cdf4bfc[object: org.codehaus.groovy.ast.expr.ClassExpression@57f5b4d1[type: foo.MyEnum] property: ConstantExpression[ONE]]
        

        But with GMaven, it looks like this:

        org.codehaus.groovy.ast.expr.PropertyExpression@65f4ba51[object: org.codehaus.groovy.ast.expr.VariableExpression@25f45022[variable: MyEnum] property: ConstantExpression[ONE]]
        

        I've opened GMAVEN-84.

        Show
        Peter Niederwieser added a comment - - edited Looking at this with the debugger, the only explanation I have is that GMaven's JavaStubCompilationUnit behaves slightly differently than groovyc's JavaAwareCompilationUnit (JavaStubCompilationUnit vs. JavaAwareCompilationUnit is the only difference in the stack trace). With groovyc, AST for annotation attribute value MyEnum.ONE looks like this: org.codehaus.groovy.ast.expr.PropertyExpression@4cdf4bfc[object: org.codehaus.groovy.ast.expr.ClassExpression@57f5b4d1[type: foo.MyEnum] property: ConstantExpression[ONE]] But with GMaven, it looks like this: org.codehaus.groovy.ast.expr.PropertyExpression@65f4ba51[object: org.codehaus.groovy.ast.expr.VariableExpression@25f45022[variable: MyEnum] property: ConstantExpression[ONE]] I've opened GMAVEN-84.

          People

          • Assignee:
            Roshan Dawrani
            Reporter:
            Peter Niederwieser
          • Votes:
            1 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development