diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/MixinTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/MixinTests.java
new file mode 100644
index 0000000000..0765ec4cea
--- /dev/null
+++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/MixinTests.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2009-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.jdt.groovy.core.tests.xform;
+
+import org.eclipse.jdt.groovy.core.tests.basic.GroovyCompilerTestSuite;
+import org.junit.Test;
+
+/**
+ * Test cases for {@link groovy.lang.Mixin}.
+ */
+public final class MixinTests extends GroovyCompilerTestSuite {
+
+ @Test
+ public void testMixin1() {
+ //@formatter:off
+ String[] sources = {
+ "Main.groovy",
+ "class A {\n" +
+ " def getB() {\n" +
+ " 'works'\n" +
+ " }\n" +
+ "}\n" +
+ "@Mixin(A)\n" +
+ "class C {\n" +
+ " void test() {\n" +
+ " print b\n" +
+ " }\n" +
+ "}\n" +
+ "new C().test()\n",
+ };
+ //@formatter:on
+
+ runConformTest(sources, "works");
+ }
+
+ @Test // GROOVY-10200
+ public void testMixin2() {
+ //@formatter:off
+ String[] sources = {
+ "Main.groovy",
+ "class A {\n" +
+ " def getB() {\n" +
+ " 'works'\n" +
+ " }\n" +
+ "}\n" +
+ "class C {\n" +
+ " @Mixin(A)\n" +
+ " static class D {\n" +
+ " void test() {\n" +
+ " print b\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ "new C.D().test()\n",
+ };
+ //@formatter:on
+
+ runConformTest(sources, "works");
+ }
+}
diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
index 3038cd3f71..e0f4746f37 100644
--- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
+++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java
@@ -2893,11 +2893,6 @@ public void testCompileStatic9043_staticInnerToPackage2() {
"1. ERROR in Main.groovy (at line 6)\n" +
"\tprint value\n" +
"\t ^^^^^\n" +
- "Groovy:Apparent variable 'value' was found in a static scope but doesn't refer to a local variable, static field or class.\n" +
- "----------\n" +
- "2. ERROR in Main.groovy (at line 6)\n" +
- "\tprint value\n" +
- "\t ^^^^^\n" +
"Groovy:[Static type checking] - The variable [value] is undeclared.\n" +
"----------\n");
}
diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/package.html b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/package.html
index 7cc69c8527..26f0063395 100644
--- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/package.html
+++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/package.html
@@ -12,7 +12,7 @@
@groovy.lang.Grapes
@groovy.lang.Lazy
- @groovy.lang.Mixin
+ @groovy.lang.Mixin ✓
@groovy.lang.Newify
@groovy.lang.Singleton ✓
diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index ed1238c176..87a6d3ac41 100644
--- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -233,6 +233,7 @@ private Variable findVariableDeclaration(final String name) {
ClassNode node = scope.getClassScope();
if (node != null) {
Variable member = findClassMember(node, name);
+ /* GRECLIPSE edit -- GROOVY-5364, GROOVY-10200
while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
crossingStaticContext = (crossingStaticContext || isStatic(node.getModifiers()));
member = findClassMember((node = node.getOuterClass()), name);
@@ -240,16 +241,24 @@ private Variable findVariableDeclaration(final String name) {
if (member != null) {
boolean staticScope = (crossingStaticContext || inSpecialConstructorCall), staticMember = member.isInStaticContext();
// prevent a static context (e.g. a static method) from accessing a non-static variable (e.g. a non-static field)
- /* GRECLIPSE edit -- GROOVY-5364
if (!(staticScope && !staticMember)) {
- */
- if (!(staticScope ? !staticMember : node.isScript())) {
- // GRECLIPSE end
variable = member;
}
}
- // GROOVY-5961
- if (!isAnonymous(scope.getClassScope())) break;
+ */
+ boolean requireStatic = (crossingStaticContext || inSpecialConstructorCall);
+ while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
+ requireStatic = requireStatic || isStatic(node.getModifiers());
+ member = findClassMember((node = node.getOuterClass()), name);
+ }
+ if (member != null) {
+ // prevent a static context (e.g. a static method) from accessing a non-static member (e.g. a non-static field)
+ if (requireStatic ? member.isInStaticContext() : !node.isScript()) {
+ variable = member;
+ }
+ }
+ // GRECLIPSE end
+ if (!isAnonymous(scope.getClassScope())) break; // GROOVY-5961
}
scope = scope.getParent();
}
diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index 282b8536f3..6594205583 100644
--- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -236,6 +236,7 @@ private Variable findVariableDeclaration(final String name) {
ClassNode node = scope.getClassScope();
if (node != null) {
Variable member = findClassMember(node, name);
+ /* GRECLIPSE edit -- GROOVY-5364, GROOVY-10200
while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
crossingStaticContext = (crossingStaticContext || isStatic(node.getModifiers()));
member = findClassMember((node = node.getOuterClass()), name);
@@ -243,16 +244,24 @@ private Variable findVariableDeclaration(final String name) {
if (member != null) {
boolean staticScope = (crossingStaticContext || inSpecialConstructorCall), staticMember = member.isInStaticContext();
// prevent a static context (e.g. a static method) from accessing a non-static variable (e.g. a non-static field)
- /* GRECLIPSE edit -- GROOVY-5364
if (!(staticScope && !staticMember)) {
- */
- if (!(staticScope ? !staticMember : node.isScript())) {
- // GRECLIPSE end
variable = member;
}
}
- // GROOVY-5961
- if (!isAnonymous(scope.getClassScope())) break;
+ */
+ boolean requireStatic = (crossingStaticContext || inSpecialConstructorCall);
+ while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
+ requireStatic = requireStatic || isStatic(node.getModifiers());
+ member = findClassMember((node = node.getOuterClass()), name);
+ }
+ if (member != null) {
+ // prevent a static context (e.g. a static method) from accessing a non-static member (e.g. a non-static field)
+ if (requireStatic ? member.isInStaticContext() : !node.isScript()) {
+ variable = member;
+ }
+ }
+ // GRECLIPSE end
+ if (!isAnonymous(scope.getClassScope())) break; // GROOVY-5961
}
scope = scope.getParent();
}
diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
index 282b8536f3..6594205583 100644
--- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
+++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -236,6 +236,7 @@ private Variable findVariableDeclaration(final String name) {
ClassNode node = scope.getClassScope();
if (node != null) {
Variable member = findClassMember(node, name);
+ /* GRECLIPSE edit -- GROOVY-5364, GROOVY-10200
while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
crossingStaticContext = (crossingStaticContext || isStatic(node.getModifiers()));
member = findClassMember((node = node.getOuterClass()), name);
@@ -243,16 +244,24 @@ private Variable findVariableDeclaration(final String name) {
if (member != null) {
boolean staticScope = (crossingStaticContext || inSpecialConstructorCall), staticMember = member.isInStaticContext();
// prevent a static context (e.g. a static method) from accessing a non-static variable (e.g. a non-static field)
- /* GRECLIPSE edit -- GROOVY-5364
if (!(staticScope && !staticMember)) {
- */
- if (!(staticScope ? !staticMember : node.isScript())) {
- // GRECLIPSE end
variable = member;
}
}
- // GROOVY-5961
- if (!isAnonymous(scope.getClassScope())) break;
+ */
+ boolean requireStatic = (crossingStaticContext || inSpecialConstructorCall);
+ while (member == null && node.getOuterClass() != null && !isAnonymous(node)) {
+ requireStatic = requireStatic || isStatic(node.getModifiers());
+ member = findClassMember((node = node.getOuterClass()), name);
+ }
+ if (member != null) {
+ // prevent a static context (e.g. a static method) from accessing a non-static member (e.g. a non-static field)
+ if (requireStatic ? member.isInStaticContext() : !node.isScript()) {
+ variable = member;
+ }
+ }
+ // GRECLIPSE end
+ if (!isAnonymous(scope.getClassScope())) break; // GROOVY-5961
}
scope = scope.getParent();
}
diff --git a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy
index 163d493a5c..90d46d6024 100644
--- a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy
+++ b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy
@@ -50,6 +50,7 @@ import org.junit.runners.Suite
org.eclipse.jdt.groovy.core.tests.xform.ImmutableTests,
org.eclipse.jdt.groovy.core.tests.xform.InheritConstructorsTests,
org.eclipse.jdt.groovy.core.tests.xform.LoggingTests,
+ org.eclipse.jdt.groovy.core.tests.xform.MixinTests,
org.eclipse.jdt.groovy.core.tests.xform.NamedVariantTests,
org.eclipse.jdt.groovy.core.tests.xform.NullCheckTests,
org.eclipse.jdt.groovy.core.tests.xform.PackageScopeTests,