Skip to content

Commit b8a747f

Browse files
committed
GROOVY-7890
1 parent 8144a70 commit b8a747f

File tree

4 files changed

+74
-24
lines changed

4 files changed

+74
-24
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+47
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,53 @@ public void testTypeChecked7804() {
11731173
runConformTest(sources, "foo");
11741174
}
11751175

1176+
@Test
1177+
public void testTypeChecked7890() {
1178+
//@formatter:off
1179+
String[] sources = {
1180+
"Main.groovy",
1181+
"@groovy.transform.TypeChecked\n" +
1182+
"class C {\n" +
1183+
" List<String> replace\n" +
1184+
" static String m(String s) {\n" + // static seems like an accident
1185+
" s.collectReplacements {\n" +
1186+
" (it in replace) ? 'o' : null\n" +
1187+
" }\n" +
1188+
" }\n" +
1189+
"}\n",
1190+
};
1191+
//@formatter:on
1192+
1193+
runNegativeTest(sources,
1194+
"----------\n" +
1195+
"1. ERROR in Main.groovy (at line 6)\n" +
1196+
"\t(it in replace) ? 'o' : null\n" +
1197+
"\t ^^^^^^^\n" +
1198+
"Groovy:[Static type checking] - The variable [replace] is undeclared.\n" +
1199+
"----------\n");
1200+
}
1201+
1202+
@Test
1203+
public void testTypeChecked7890a() {
1204+
//@formatter:off
1205+
String[] sources = {
1206+
"Main.groovy",
1207+
"@groovy.transform.TypeChecked\n" +
1208+
"class C {\n" +
1209+
" List<String> replace\n" +
1210+
" String m(String s) {\n" +
1211+
" s.collectReplacements {\n" +
1212+
" (it in replace) ? 'o' : null\n" +
1213+
" }\n" +
1214+
" }\n" +
1215+
"}\n" +
1216+
"print(new C(replace:['a','b','c']).m('foobar'))",
1217+
};
1218+
//@formatter:on
1219+
1220+
runConformTest(sources, "foooor");
1221+
}
1222+
11761223
@Test
11771224
public void testTypeChecked7945() {
11781225
//@formatter:off

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1856,6 +1856,10 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
18561856
boolean staticOnly;
18571857
if (isClassClassNodeWrappingConcreteType(current)) {
18581858
staticOnly = false;
1859+
// GRECLIPSE add -- GROOVY-7890
1860+
} else if (receiver.getData() != null) {
1861+
staticOnly = false;
1862+
// GRECLIPSE end
18591863
} else {
18601864
staticOnly = staticOnlyAccess;
18611865
}
@@ -3018,8 +3022,9 @@ private static boolean noneMatch(final List<ClassNode> types, final ClassNode ty
30183022
@Override
30193023
public void visitClosureExpression(final ClosureExpression expression) {
30203024
boolean oldStaticContext = typeCheckingContext.isInStaticContext;
3025+
/* GRECLIPSE add -- GROOVY-7890
30213026
typeCheckingContext.isInStaticContext = false;
3022-
3027+
*/
30233028
// collect every variable expression used in the loop body
30243029
final Map<VariableExpression, ClassNode> varOrigType = new HashMap<VariableExpression, ClassNode>();
30253030
Statement code = expression.getCode();
@@ -3238,7 +3243,7 @@ protected void startMethodInference(final MethodNode node, ErrorCollector collec
32383243

32393244
final boolean osc = typeCheckingContext.isInStaticContext;
32403245
try {
3241-
typeCheckingContext.isInStaticContext = node.isStatic();
3246+
typeCheckingContext.isInStaticContext = node.getOriginal().isStatic();
32423247
super.visitMethod(node);
32433248
/* GRECLIPSE edit -- GROOVY-6851, GROOVY-9151, GROOVY-10104
32443249
for (Parameter parameter : node.getParameters()) {
@@ -4248,6 +4253,8 @@ public void visitMethodCallExpression(MethodCallExpression call) {
42484253
// if we are not in a static context but the current receiver is a static class, we must
42494254
// ensure that all methods are either static or declared by the current receiver or a superclass
42504255
if (!mn.isEmpty()
4256+
// GRECLIPSE add -- GROOVY-7890
4257+
&& currentReceiver.getData() == null
42514258
&& (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0)
42524259
&& (call.isImplicitThis() || (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()))) {
42534260
// we create separate method lists just to be able to print out

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,10 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
17841784
boolean staticOnly;
17851785
if (isClassClassNodeWrappingConcreteType(current)) {
17861786
staticOnly = false;
1787+
// GRECLIPSE add -- GROOVY-7890
1788+
} else if (receiver.getData() != null) {
1789+
staticOnly = false;
1790+
// GRECLIPSE end
17871791
} else {
17881792
staticOnly = staticOnlyAccess;
17891793
}
@@ -2716,8 +2720,9 @@ private ClassNode getInferredTypeFromTempInfo(final Expression expression, final
27162720
@Override
27172721
public void visitClosureExpression(final ClosureExpression expression) {
27182722
boolean oldStaticContext = typeCheckingContext.isInStaticContext;
2723+
/* GRECLIPSE add -- GROOVY-7890
27192724
typeCheckingContext.isInStaticContext = false;
2720-
2725+
*/
27212726
// collect every variable expression used in the closure body
27222727
Map<VariableExpression, ClassNode> varTypes = new HashMap<>();
27232728
expression.getCode().visit(new VariableExpressionTypeMemoizer(varTypes, true));
@@ -2954,7 +2959,7 @@ protected void startMethodInference(final MethodNode node, final ErrorCollector
29542959
typeCheckingContext.pushErrorCollector(collector);
29552960
boolean osc = typeCheckingContext.isInStaticContext;
29562961
try {
2957-
typeCheckingContext.isInStaticContext = node.isStatic();
2962+
typeCheckingContext.isInStaticContext = node.getOriginal().isStatic();
29582963

29592964
super.visitMethod(node);
29602965
} finally {
@@ -3924,6 +3929,8 @@ public void visitMethodCallExpression(final MethodCallExpression call) {
39243929
// if we are not in a static context but the current receiver is a static class, we must
39253930
// ensure that all methods are either static or declared by the current receiver or a superclass
39263931
if (!mn.isEmpty()
3932+
// GRECLIPSE add -- GROOVY-7890
3933+
&& currentReceiver.getData() == null
39273934
&& (call.isImplicitThis() || isThisExpression(objectExpression))
39283935
&& (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0)) {
39293936
// we create separate method lists just to be able to print out

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+9-20
Original file line numberDiff line numberDiff line change
@@ -1555,15 +1555,10 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
15551555
Collections.addAll(queue, current.getInterfaces());
15561556
}
15571557

1558-
// in case of a lookup on Class we look for instance methods on Class
1559-
// as well, since in case of a static property access we have the class
1560-
// itself in the list of receivers already;
1561-
boolean staticOnly;
1562-
if (isClassClassNodeWrappingConcreteType(current)) {
1563-
staticOnly = false;
1564-
} else {
1565-
staticOnly = staticOnlyAccess;
1566-
}
1558+
boolean staticOnly = (receiver.getData() == null ? staticOnlyAccess : false);
1559+
// in case of a lookup on java.lang.Class, look for instance methods on Class
1560+
// as well; in case of static property access Class<Type> and Type are listed
1561+
if (isClassClassNodeWrappingConcreteType(current)) staticOnly = false;
15671562

15681563
field = allowStaticAccessToMember(field, staticOnly);
15691564

@@ -2389,9 +2384,6 @@ private ClassNode getInferredTypeFromTempInfo(final Expression expression, final
23892384

23902385
@Override
23912386
public void visitClosureExpression(final ClosureExpression expression) {
2392-
boolean oldStaticContext = typeCheckingContext.isInStaticContext;
2393-
typeCheckingContext.isInStaticContext = false;
2394-
23952387
// collect every variable expression used in the closure body
23962388
Map<VariableExpression, ClassNode> varTypes = new HashMap<>();
23972389
expression.getCode().visit(new VariableExpressionTypeMemoizer(varTypes, true));
@@ -2439,7 +2431,6 @@ public void visitClosureExpression(final ClosureExpression expression) {
24392431

24402432
// restore original metadata
24412433
restoreVariableExpressionMetadata(variableMetadata);
2442-
typeCheckingContext.isInStaticContext = oldStaticContext;
24432434
for (Parameter parameter : getParametersSafe(expression)) {
24442435
typeCheckingContext.controlStructureVariables.remove(parameter);
24452436
// GROOVY-10072: visit param default argument expression if present
@@ -2596,7 +2587,7 @@ protected void startMethodInference(final MethodNode node, final ErrorCollector
25962587
typeCheckingContext.pushErrorCollector(collector);
25972588
boolean osc = typeCheckingContext.isInStaticContext;
25982589
try {
2599-
typeCheckingContext.isInStaticContext = node.isStatic();
2590+
typeCheckingContext.isInStaticContext = node.getOriginal().isStatic();
26002591

26012592
super.visitMethod(node);
26022593
} finally {
@@ -3426,12 +3417,10 @@ public void visitMethodCallExpression(final MethodCallExpression call) {
34263417
ClassNode receiverType = currentReceiver.getType();
34273418
mn = findMethod(receiverType, name, args);
34283419

3429-
// if the receiver is "this" or "implicit this", then we must make sure that the compatible
3430-
// methods are only static if we are in a static context
3431-
// if we are not in a static context but the current receiver is a static class, we must
3432-
// ensure that all methods are either static or declared by the current receiver or a superclass
3433-
if (!mn.isEmpty()
3434-
&& (isThisObjectExpression || call.isImplicitThis())
3420+
// if receiver is "this" in a static context then only static methods are compatible
3421+
// if not in a static context but the current receiver is a static class ensure that
3422+
// all methods are either static or declared by the current receiver or a superclass
3423+
if (!mn.isEmpty() && currentReceiver.getData() == null && (isThisObjectExpression || call.isImplicitThis())
34353424
&& (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0)) {
34363425
// we create separate method lists just to be able to print out
34373426
// a nice error message to the user

0 commit comments

Comments
 (0)