diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java index 4c7ade06d96..032c796ed12 100644 --- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java +++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java @@ -108,7 +108,9 @@ public void visitClass(final ClassNode node) { classNode = node; thisField = null; - if (node.isEnum() || node.isInterface() || isTrait(node.getOuterClass())) return; + // GROOVY-8587: trait helpers must not get the standard MOP methods, but + // GROOVY-12226: classes declared within a trait are ordinary inner classes + if (node.isEnum() || node.isInterface() || isTraitHelper(node)) return; // if the class has an inner class, add methods to support private member access if (node.getInnerClasses().hasNext()) { @@ -149,6 +151,17 @@ public void visitConstructor(final ConstructorNode node) { } } + /** + * Tests for one of the classes generated for a trait, i.e. the helper, the + * field helper or the static field helper. Classes declared within a trait + * are not included. + */ + private static boolean isTraitHelper(final ClassNode node) { + ClassNode outerClass = node.getOuterClass(); + return isTrait(outerClass) && (node.getModifiers() & ACC_SYNTHETIC) != 0 + && node.getName().startsWith(outerClass.getName() + "$Trait$"); + } + private static void makeBridgeConstructor(final ClassNode c, final Parameter[] p) { Parameter[] newP = new Parameter[p.length + 1]; for (int i = 0; i < p.length; i += 1) { diff --git a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy index 72c17cb0137..730ded1ebb0 100644 --- a/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy +++ b/src/test/groovy/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy @@ -1048,6 +1048,54 @@ final class TraitASTTransformationTest { ''' } + // GROOVY-12226 + @Test + void testStaticInnerClassInTrait2() { + assertScript shell, ''' + trait T { + static class Outer { + def outerMethod() { 'foo' } + def outerProperty = 'bar' + class Inner { + def callOuter() { outerMethod() } + def readOuter() { outerProperty } + } + def viaInner() { + def inner = new Inner() + inner.callOuter() + inner.readOuter() + } + } + } + class Foo implements T { + } + assert new T.Outer().viaInner() == 'foobar' + ''' + } + + // GROOVY-12226: class name collides with the trait helper prefix + @Test + void testStaticInnerClassInTrait3() { + assertScript shell, ''' + trait T { + static class Trait$Outer { + def outerMethod() { 'foo' } + def outerProperty = 'bar' + class Inner { + def callOuter() { outerMethod() } + def readOuter() { outerProperty } + } + def viaInner() { + def inner = new Inner() + inner.callOuter() + inner.readOuter() + } + } + } + class Foo implements T { + } + assert new T.Trait$Outer().viaInner() == 'foobar' + ''' + } + @Test void testNonStaticInnerClassInTrait() { shouldFail shell, '''