Skip to content

Commit

Permalink
GROOVY-8050
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Dec 14, 2021
1 parent 0873c66 commit 6a018e0
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2141,21 +2141,76 @@ public void testCompileStatic7996e() {
}

@Test
public void testCompileStatic8051() {
public void testCompileStatic8050() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"class Outer {\n" +
" class Inner {\n" +
" }\n" +
" def foo = 1\n" +
"}\n" +
"@groovy.transform.CompileStatic\n" +
"void test(Outer.Inner inner) {\n" +
" print inner.getFoo()\n" +
"}\n" +
"test(new Outer.Inner(new Outer()))\n",
};
//@formatter:on

runNegativeTest(sources,
"----------\n" +
"1. ERROR in Main.groovy (at line 9)\n" +
"\tprint inner.getFoo()\n" +
"\t ^^^^^^^^^^^^^^\n" +
"Groovy:[Static type checking] - Cannot find matching method Outer$Inner#getFoo(). Please check if the declared type is correct and if the method exists.\n" +
"----------\n");
}

@Test
public void testCompileStatic8050a() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"class Outer {\n" +
" class Inner {\n" +
" }\n" +
" def foo = 1\n" +
" Inner createInner() { new Inner() }\n" +
"}\n" +
"@groovy.transform.CompileStatic\n" +
"void test(Outer.Inner inner) {\n" +
" print inner.foo\n" +
"}\n" +
"test(new Outer.Inner(new Outer()))\n",
};
//@formatter:on

runNegativeTest(sources,
"----------\n" +
"1. ERROR in Main.groovy (at line 9)\n" +
"\tprint inner.foo\n" +
"\t ^^^^^^^^^\n" +
"Groovy:[Static type checking] - No such property: foo for class: Outer$Inner\n" +
"----------\n");
}

@Test
public void testCompileStatic8051() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"class Outer {\n" +
" class Inner {\n" +
" Closure createClosure() {\n" +
" return { foo }\n" +
" }\n" +
" }\n" +
" def foo = 1\n" +
"}\n" +
"def i = new Outer().createInner()\n" +
"def i = new Outer.Inner(new Outer())\n" +
"def c = i.createClosure()\n" +
"print c()\n",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5712,12 +5712,11 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
}
} else {
methods = findMethodsWithGenerated(receiver, name);
/* GRECLIPSE edit -- GROOVY-9890
/* GRECLIPSE edit -- GROOVY-8050, GROOVY-9890
if (receiver.isInterface()) {
collectAllInterfaceMethodsByName(receiver, name, methods);
methods.addAll(OBJECT_TYPE.getMethods(name));
}
*/
// TODO: investigate the trait exclusion a bit further, needed otherwise
// CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying
// not static method can't be called from a static context
Expand All @@ -5729,6 +5728,15 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
methods.addAll(findMethodsWithGenerated(parent, name));
}
}
*/
if (!receiver.isStaticClass() && receiver.getOuterClass() != null
&& !receiver.getName().endsWith("$Trait$Helper") // GROOVY-7242
&& typeCheckingContext.getEnclosingClassNodes().contains(receiver)) {
ClassNode outer = receiver.getOuterClass();
do { methods.addAll(findMethodsWithGenerated(outer, name));
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
// GRECLIPSE end
if (methods.isEmpty()) {
addArrayMethods(methods, receiver, name, args);
}
Expand All @@ -5739,8 +5747,8 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
pname = extractPropertyNameFromMethodName("is", name);
}
if (pname != null) {
// we don't use property exists there because findMethod is called on super clases recursively
PropertyNode property = null;
/* GRECLIPSE edit -- GROOVY-8050
ClassNode curNode = receiver;
while (property == null && curNode != null) {
property = curNode.getProperty(pname);
Expand All @@ -5755,6 +5763,20 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
}
curNode = curNode.getSuperClass();
}
*/
outer_upper: // can't use existsProperty because it calls findMethod
for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
property = cn.getProperty(pname);
if (property != null) break outer_upper;
if (!cn.isStaticClass() && cn.getOuterClass() != null
&& typeCheckingContext.getEnclosingClassNodes().contains(cn)) {
ClassNode outer = cn.getOuterClass();
do { property = outer.getProperty(pname);
if (property != null) break outer_upper;
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
}
// GRECLIPSE end
if (property != null) {
int mods = Opcodes.ACC_PUBLIC | (property.isStatic() ? Opcodes.ACC_STATIC : 0);
MethodNode node = new MethodNode(name, mods, property.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5445,6 +5445,7 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
methods.add(callMethod);
}
}
/* GRECLIPSE edit -- GROOVY-8050
// TODO: investigate the trait exclusion a bit further, needed otherwise
// CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying
// not static method can't be called from a static context
Expand All @@ -5456,6 +5457,15 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
methods.addAll(findMethodsWithGenerated(parent, name));
}
}
*/
if (!receiver.isStaticClass() && receiver.getOuterClass() != null
&& !receiver.getName().endsWith("$Trait$Helper") // GROOVY-7242
&& typeCheckingContext.getEnclosingClassNodes().contains(receiver)) {
ClassNode outer = receiver.getOuterClass();
do { methods.addAll(findMethodsWithGenerated(outer, name));
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
// GRECLIPSE end
if (methods.isEmpty()) {
addArrayMethods(methods, receiver, name, args);
}
Expand All @@ -5466,8 +5476,8 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
pname = extractPropertyNameFromMethodName("is", name);
}
if (pname != null) {
// we don't use property exists there because findMethod is called on super clases recursively
PropertyNode property = null;
/* GRECLIPSE edit -- GROOVY-8050
ClassNode curNode = receiver;
while (property == null && curNode != null) {
property = curNode.getProperty(pname);
Expand All @@ -5482,6 +5492,20 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
}
curNode = curNode.getSuperClass();
}
*/
outer_upper: // can't use existsProperty because it calls findMethod
for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
property = cn.getProperty(pname);
if (property != null) break outer_upper;
if (!cn.isStaticClass() && cn.getOuterClass() != null
&& typeCheckingContext.getEnclosingClassNodes().contains(cn)) {
ClassNode outer = cn.getOuterClass();
do { property = outer.getProperty(pname);
if (property != null) break outer_upper;
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
}
// GRECLIPSE end
if (property != null) {
int mods = Opcodes.ACC_PUBLIC | (property.isStatic() ? Opcodes.ACC_STATIC : 0);
MethodNode node = new MethodNode(name, mods, property.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4770,13 +4770,14 @@ private static List<MethodNode> addGeneratedMethods(final ClassNode receiver, fi

protected List<MethodNode> findMethod(ClassNode receiver, final String name, final ClassNode... args) {
if (isPrimitiveType(receiver)) receiver = getWrapper(receiver);

List<MethodNode> methods;
if (!receiver.isInterface() && "<init>".equals(name)) {
if ("<init>".equals(name) && !receiver.isInterface()) {
methods = addGeneratedMethods(receiver, new ArrayList<>(receiver.getDeclaredConstructors()));
if (methods.isEmpty()) {
MethodNode node = new ConstructorNode(Opcodes.ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT);
node.setDeclaringClass(receiver);
methods = Collections.singletonList(node);
methods.add(node);
if (receiver.isArray()) {
// No need to check the arguments against an array constructor: it just needs to exist. The array is
// created through coercion or by specifying its dimension(s), anyway, and would not match an
Expand All @@ -4786,25 +4787,21 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
}
} else {
methods = findMethodsWithGenerated(receiver, name);
if (receiver.isInterface()) {
if ("call".equals(name) && isFunctionalInterface(receiver)) {
MethodNode sam = findSAM(receiver);
if ("call".equals(name) && receiver.isInterface()) {
MethodNode sam = findSAM(receiver);
if (sam != null) {
MethodNode callMethod = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode());
callMethod.setDeclaringClass(sam.getDeclaringClass());
callMethod.setSourcePosition(sam);
methods.add(callMethod);
}
}
// TODO: investigate the trait exclusion a bit further, needed otherwise
// CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying
// not static method can't be called from a static context
if (typeCheckingContext.getEnclosingClosure() == null || (receiver.getOuterClass() != null && !receiver.getName().endsWith("$Trait$Helper"))) {
// not in a closure or within an inner class
ClassNode parent = receiver;
while (parent.getOuterClass() != null && !parent.isStaticClass()) {
parent = parent.getOuterClass();
methods.addAll(findMethodsWithGenerated(parent, name));
}
if (!receiver.isStaticClass() && receiver.getOuterClass() != null
&& !receiver.getName().endsWith("$Trait$Helper") // GROOVY-7242
&& typeCheckingContext.getEnclosingClassNodes().contains(receiver)) {
ClassNode outer = receiver.getOuterClass();
do { methods.addAll(findMethodsWithGenerated(outer, name));
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
if (methods.isEmpty()) {
addArrayMethods(methods, receiver, name, args);
Expand All @@ -4817,33 +4814,28 @@ protected List<MethodNode> findMethod(ClassNode receiver, final String name, fin
}
PropertyNode property = null;
if (pname != null) {
// we don't use property exists there because findMethod is called on super clases recursively
ClassNode curNode = receiver;
while (property == null && curNode != null) {
property = curNode.getProperty(pname);
ClassNode svCur = curNode;
while (property == null && svCur.getOuterClass() != null && !svCur.isStaticClass()) {
svCur = svCur.getOuterClass();
property = svCur.getProperty(pname);
if (property != null) {
receiver = svCur;
break;
}
outer_upper: // can't use existsProperty because it calls findMethod
for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
property = cn.getProperty(pname);
if (property != null) break outer_upper;
if (!cn.isStaticClass() && cn.getOuterClass() != null
&& typeCheckingContext.getEnclosingClassNodes().contains(cn)) {
ClassNode outer = cn.getOuterClass();
do {
property = outer.getProperty(pname);
if (property != null) break outer_upper;
} while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
}
curNode = curNode.getSuperClass();
}
} else {
// look for a property with the getterName set since it may not match above
ClassNode curNode = receiver;
while (property == null && curNode != null && !curNode.isStaticClass()) {
for (PropertyNode p : curNode.getProperties()) {
if (name.equals(p.getGetterName())) {
property = p;
receiver = curNode;
break;
} else { // look for property via getGetterName() for non-canonical case
out:
for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
for (PropertyNode pn : cn.getProperties()) {
if (name.equals(pn.getGetterName())) {
property = pn;
break out;
}
}
curNode = curNode.getSuperClass();
}
}
if (property != null) {
Expand Down

0 comments on commit 6a018e0

Please sign in to comment.