Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error compiling class that implements trait (binary reference from grails-testing-support) #714

Closed
AntonPanikov opened this issue Sep 12, 2018 · 7 comments
Assignees
Labels
Milestone

Comments

@AntonPanikov
Copy link

I am using Grails framework (3.3.x and 3.2.x) and all grails applications is always producing some strange compile error, while application is always compile fine with gradle and internal eclipse compiler. So I can run an app as regular spring boot app and debug it. The issue is if I have some gradle sub-project then any project that depends on it will be not compiled at all by internal eclipse compiler, so will be not runnable and I can not debug it, while gradle still can compile and run it.

I am using:
Eclipse Oxygen.3a Release (4.7.3a)
Groovy-Eclipse 3.1.0.xx-201808241936-e47

You can easily create sample app with sdkman or use attached one below:

  1. Install sdkman:
    curl -s "https://get.sdkman.io" | bash
  2. Source or open new shell:
    source "$HOME/.sdkman/bin/sdkman-init.sh"
  3. Install grails:
    sdk i grails
  4. Create sample app in some folder (grails-example):
    grails create-app --profile web --inplace

Import it into eclipse and add gradle nature, you will get:

The project was not built since its build path is incomplete. Cannot find the class file for grails.testing.services.ServiceUnitTest$Trait$FieldHelper$1. Fix the build path then try building this project

Based on source you can get different variations of this error referencing different classes from grail, like:

The project was not built since its build path is incomplete. Cannot find the class file for grails.artefact.Controller$Trait$FieldHelper$1. Fix the build path then try building this project

While project is actually compile fine and running.
grails-sample.zip

@eric-milles
Copy link
Member

eric-milles commented Sep 12, 2018

There are two facets to this issue:

  1. Groovy-Eclipse is performing very eager resolution of member types when trying to resolve names in a compilation unit.
  2. Grails is not including the trait member types in some of their distributions. For example, grails.testing.services.ServiceUnitTest is a trait, but only the outer class ServiceUnitTest is included in the grails-testing-support artifact. The class files list member types such as ServiceUnitTest$Trait$FieldHelper$1, so it is only logical that an IDE might try to resolve them at some point.

@eric-milles
Copy link
Member

Here is the source of the error during compilation:

		// walk all visible interfaces to find ambiguous references
		if (interfacesToVisit != null) {
			ProblemReferenceBinding ambiguous = null;
			done : for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding anInterface = interfacesToVisit[i];
				unitScope.recordReference(anInterface, typeName);
				if ((memberType = anInterface.getMemberType(typeName)) != null) {

The interface being examined is ServiceUnitTest and the typeName string is "java".

@eric-milles
Copy link
Member

Even if inner class resolve can be deferred, compilation against a trait requires resolution of its inner class "helper" types:

        addPhaseOperation(new PrimaryClassNodeOperation() {
            public void call(SourceUnit source, GeneratorContext context,
                             ClassNode classNode) throws CompilationFailedException {
                TraitComposer.doExtendTraits(classNode, source, CompilationUnit.this);
            }
        }, Phases.CANONICALIZATION);
// From TraitComposer:
    public static void doExtendTraits(final ClassNode cNode, final SourceUnit unit, final CompilationUnit cu) {
        if (cNode.isInterface()) return;
        boolean isItselfTrait = Traits.isTrait(cNode);
        SuperCallTraitTransformer superCallTransformer = new SuperCallTraitTransformer(unit);
        if (isItselfTrait) {
            checkTraitAllowed(cNode, unit);
            return;
        }
        if (!cNode.getNameWithoutPackage().endsWith(Traits.TRAIT_HELPER)) {
            List<ClassNode> traits = findTraits(cNode);
            for (ClassNode trait : traits) {
                TraitHelpersTuple helpers = Traits.findHelpers(trait); // this line tries to resolve trait helpers
                applyTrait(trait, cNode, helpers);
                superCallTransformer.visitClass(cNode);
                if (unit!=null) {
                    ASTTransformationCollectorCodeVisitor collector = new ASTTransformationCollectorCodeVisitor(unit, cu.getTransformLoader());
                    collector.visitClass(cNode);
                }
            }
        }
    }

@eric-milles eric-milles self-assigned this Sep 14, 2018
@eric-milles
Copy link
Member

Seems like this was reported to the Grails project as well: grails/grails-testing-support#31

@eric-milles eric-milles added this to the v3.1.0 milestone Sep 14, 2018
@eric-milles eric-milles changed the title Sample grails app is always producing compile error Error compiling class that implements trait (binary reference from grails-testing-support) Sep 14, 2018
@eric-milles
Copy link
Member

Ready to test

@AntonPanikov
Copy link
Author

It is working right now, this type of compilation is gone, but I am getting:

The method cannot be declared static; static methods can only be declared in a static or top level type

for something like this:

class Test {

    class TestInner {
        static {
        }

    }
}

I am on 3.1.0.xx-201809141915-e47

@eric-milles
Copy link
Member

Can you file a separate issue? The original issue has been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants