diff --git a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java index 7a08905b87c..e4f28ec869e 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java @@ -410,6 +410,12 @@ public void visitMethodCallExpression(final MethodCallExpression call) { public void visitConstructorCallExpression(final ConstructorCallExpression call) { super.visitConstructorCallExpression(call); + if (call.isUsingAnonymousInnerClass() && call.getType().getNodeMetaData(StaticTypeCheckingVisitor.class) != null) { + ClassNode anonType = call.getType(); + anonType.putNodeMetaData(STATIC_COMPILE_NODE, anonType.getEnclosingMethod().getNodeMetaData(STATIC_COMPILE_NODE)); + anonType.putNodeMetaData(WriterControllerFactory.class, anonType.getOuterClass().getNodeMetaData(WriterControllerFactory.class)); + } + MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); if (target == null && call.getLineNumber() > 0) { addError("Target constructor for constructor call expression hasn't been set", call); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 2bacc9da1a4..1b06c8c44c3 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -435,7 +435,7 @@ protected ClassNode[] getTypeCheckingAnnotations() { } protected boolean shouldSkipClassNode(final ClassNode node) { - return isSkipMode(node); + return Boolean.TRUE.equals(node.getNodeMetaData(StaticTypeCheckingVisitor.class)) || isSkipMode(node); } public boolean isSkipMode(final AnnotatedNode node) { @@ -2212,6 +2212,20 @@ && findMethod(receiver, "", args).isEmpty()) { if (node != null) storeTargetMethod(call, node); } + // GROOVY-9327: check for AIC in STC method with non-STC enclosing class + if (call.isUsingAnonymousInnerClass()) { + Set methods = typeCheckingContext.methodsToBeVisited; + if (!methods.isEmpty()) { // indicates specific methods have STC + typeCheckingContext.methodsToBeVisited = Collections.emptySet(); + + ClassNode anonType = call.getType(); + visitClass(anonType); // visit anon. inner class inline with method + anonType.putNodeMetaData(StaticTypeCheckingVisitor.class, Boolean.TRUE); + + typeCheckingContext.methodsToBeVisited = methods; + } + } + extension.afterMethodCall(call); } diff --git a/src/test/groovy/bugs/Groovy9327.groovy b/src/test/groovy/bugs/Groovy9327.groovy new file mode 100644 index 00000000000..7112f3feecc --- /dev/null +++ b/src/test/groovy/bugs/Groovy9327.groovy @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 groovy.bugs + +import groovy.transform.CompileStatic +import org.junit.Test + +import static groovy.test.GroovyAssert.shouldFail + +@CompileStatic +final class Groovy9327 { + + @Test + void testCheckedAIC() { + def err = shouldFail ''' + @groovy.transform.TypeChecked + void test() { + def runner = new Runnable() { + @Override + void run() { + unknownReference + } + } + } + ''' + assert err.message.contains('The variable [unknownReference] is undeclared.') + } + + @Test + void testCompiledAIC() { + def err = shouldFail ''' + @groovy.transform.CompileStatic + void test() { + def runner = new Runnable() { + @Override + void run() { + unknownReference + } + } + } + ''' + assert err.message.contains('The variable [unknownReference] is undeclared.') + } +} diff --git a/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy b/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy index 28a170352de..5ad7019a1b1 100644 --- a/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy +++ b/src/test/groovy/transform/stc/AnonymousInnerClassSTCTest.groovy @@ -122,9 +122,10 @@ class AnonymousInnerClassSTCTest extends StaticTypeCheckingTestCase { } } s.size() - }''' + } + ''' } - + void testAICInAICInStaticMethod() { assertScript ''' class A {