You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/01/31 21:07:59 UTC
[groovy] 01/06: GROOVY-9890: STC: always search for default methods
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit e542f9f54b381ed682072176a85e8c5545a443f9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Jan 17 17:58:13 2021 -0600
GROOVY-9890: STC: always search for default methods
2_5_X backport
Conflicts:
src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
.../java/org/codehaus/groovy/ast/MethodNode.java | 7 +-
.../transform/stc/StaticTypeCheckingVisitor.java | 30 +++--
.../groovy/transform/stc/MethodCallsSTCTest.groovy | 150 +++++++++++++++++----
3 files changed, 149 insertions(+), 38 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/MethodNode.java b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
index c4289b3..8ce5415 100644
--- a/src/main/java/org/codehaus/groovy/ast/MethodNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
@@ -148,6 +148,11 @@ public class MethodNode extends AnnotatedNode implements Opcodes {
return (modifiers & ACC_ABSTRACT) != 0;
}
+ public boolean isDefault() {
+ return (modifiers & (ACC_ABSTRACT | ACC_PUBLIC | ACC_STATIC)) == ACC_PUBLIC
+ && getDeclaringClass() != null && getDeclaringClass().isInterface();
+ }
+
public boolean isStatic() {
return (modifiers & ACC_STATIC) != 0;
}
@@ -260,7 +265,7 @@ public class MethodNode extends AnnotatedNode implements Opcodes {
/**
* Provides a nicely formatted string of the method definition. For simplicity, generic types on some of the elements
- * are not displayed.
+ * are not displayed.
* @return
* string form of node with some generic elements suppressed
*/
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 f505312..e0cad0c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2409,7 +2409,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
candidates = findMethodsWithGenerated(receiverType, nameText);
- collectAllInterfaceMethodsByName(receiverType, nameText, candidates);
if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
candidates.addAll(findDGMMethodsForClassNode(getTransformLoader(), receiverType, nameText));
candidates = filterMethodsByVisibility(candidates);
@@ -4462,10 +4461,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
*/
protected List<MethodNode> findMethodsWithGenerated(ClassNode receiver, String name) {
List<MethodNode> methods = receiver.getMethods(name);
- if (methods.isEmpty() || receiver.isResolved()) return methods;
- List<MethodNode> result = addGeneratedMethods(receiver, methods);
+ if (receiver.isAbstract()) {
+ collectAllInterfaceMethodsByName(receiver, name, methods);
+ } else { // GROOVY-9890: always search for default methods
+ List<MethodNode> interfaceMethods = new ArrayList<>();
+ collectAllInterfaceMethodsByName(receiver, name, interfaceMethods);
+ for (MethodNode method : interfaceMethods) {
+ if (method.isDefault()) methods.add(method);
+ }
+ }
+ if (receiver.isInterface()) {
+ methods.addAll(OBJECT_TYPE.getMethods(name));
+ }
- return result;
+ if (methods.isEmpty() || receiver.isResolved()) {
+ return methods;
+ }
+ return addGeneratedMethods(receiver, methods);
}
private static List<MethodNode> addGeneratedMethods(final ClassNode receiver, final List<MethodNode> methods) {
@@ -4543,10 +4555,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
} else {
methods = findMethodsWithGenerated(receiver, name);
- 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
@@ -4614,12 +4622,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
- if (methods.isEmpty()) {
- // look at the interfaces, there's a chance that a method is not implemented and we should not hide the
- // error from the compiler
- collectAllInterfaceMethodsByName(receiver, name, methods);
- }
-
if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
// lookup in DGM methods too
findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), receiver, name, args, methods);
diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
index 6971243..2f61d0c 100644
--- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -18,8 +18,8 @@
*/
package groovy.transform.stc
-import org.codehaus.groovy.control.customizers.ImportCustomizer;
-import static org.codehaus.groovy.control.CompilerConfiguration.DEFAULT as config
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
/**
* Unit tests for static type checking : method calls.
@@ -34,7 +34,6 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
} catch (ClassNotFoundException e) {
IS_PRE_8 = true
}
-
}
@Override
@@ -439,7 +438,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
assertScript '''
1.with { obj ->
if (obj instanceof String) {
- obj.toUpperCase()
+ obj.toUpperCase()
}
}
'''
@@ -450,11 +449,11 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
public interface SAM {
boolean run(String var1, Thread th);
}
-
+
static boolean foo(SAM sam) {
sam.run("foo", new Thread())
}
-
+
static def callSAM() {
foo { str, th ->
str.toUpperCase().equals(th.getName())
@@ -468,11 +467,11 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
assertScript '''
import java.util.function.Predicate
-
+
static boolean foo(Predicate<? super String> p) {
p.test("foo")
}
-
+
static def testPredicate() {
foo { it ->
it.toUpperCase()
@@ -501,9 +500,9 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
filter { s -> s.length() < 10 }.
toArray()
}
-
- final words = ["orange", "sit", "test", "flabbergasted", "honorific"]
-
+
+ final words = ["orange", "sit", "test", "flabbergasted", "honorific"]
+
println doIt(words)
'''
}
@@ -552,7 +551,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
}
'''
}
-
+
void testOneDefaultParam() {
assertScript '''
String m(String val = 'hello') {
@@ -590,7 +589,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
m('test', new Object())
''', 'm(java.lang.String, java.lang.Object)'
}
-
+
void testMultipleDefaultArgs() {
assertScript '''
String m(String first = 'first', String second, String third = 'third') {
@@ -996,17 +995,122 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
Test.test(null)
'''
}
+
void testShouldFindInheritedInterfaceMethod() {
assertScript '''
- interface Top { void close() }
+ interface Top { void foo() }
interface Middle extends Top {}
interface Bottom extends Middle {}
- void foo(Bottom obj) {
- obj.close()
+
+ void test(Bottom b) {
+ b.foo()
+ }
+ '''
+ }
+
+ void testShouldFindInheritedInterfaceMethod2() {
+ assertScript '''
+ interface Top { int foo(int i) }
+ interface Middle extends Top { int foo(String s) }
+ interface Bottom extends Middle {}
+
+ void test(Bottom b) {
+ b.foo(123)
+ }
+ '''
+ }
+
+ void testShouldFindInheritedInterfaceMethod3() {
+ assertScript '''
+ interface Top { int foo(int i) }
+ interface Middle extends Top { }
+ interface Bottom extends Middle { int foo(String s) }
+
+ void test(Bottom b) {
+ b.foo(123)
+ }
+ '''
+ }
+
+ void testShouldFindInheritedInterfaceMethod4() {
+ assertScript '''
+ interface Top { int foo(int i) }
+ interface Middle extends Top { int foo(String s) }
+ abstract class Bottom implements Middle {}
+
+ int test(Bottom b) {
+ b.foo(123)
}
+ def bot = new Bottom() {
+ int foo(int i) { 1 }
+ int foo(String s) { 2 }
+ }
+ assert test(bot) == 1
+ '''
+ }
+
+ void testShouldFindInheritedInterfaceMethod5() {
+ assertScript '''
+ interface Top { int foo(int i) }
+ interface Middle extends Top { }
+ abstract class Bottom implements Middle { abstract int foo(String s) }
+
+ int test(Bottom b) {
+ b.foo(123)
+ }
+ def bot = new Bottom() {
+ int foo(int i) { 1 }
+ int foo(String s) { 2 }
+ }
+ assert test(bot) == 1
'''
}
+ // GROOVY-9890
+ void testShouldFindInheritedInterfaceDefaultMethod() {
+ if (IS_PRE_8) return
+
+ def parentDir = File.createTempDir()
+ config.targetDirectory = File.createTempDir()
+ config.jointCompilationOptions = [stubDir: File.createTempDir()]
+ try {
+ def a = new File(parentDir, 'Face.java')
+ a.write '''
+ public interface Face {
+ default Object foo(long n) {
+ return n;
+ }
+ Object foo(String s);
+ }
+ '''
+ def b = new File(parentDir, 'Impl.groovy')
+ b.write '''
+ class Impl implements Face {
+ @Override def foo(String s) {
+ return s
+ }
+ // def foo(long n)
+ }
+ '''
+ def c = new File(parentDir, 'Main.groovy')
+ c.write '''
+ def result = new Impl().foo(42L)
+ assert result.class == Long.class
+ '''
+
+ def loader = new GroovyClassLoader(this.class.classLoader)
+ def cu = new JavaAwareCompilationUnit(config, loader)
+ cu.addSources(a, b, c)
+ cu.compile()
+
+ loader.loadClass('Main').main()
+ } finally {
+ parentDir.deleteDir()
+ config.targetDirectory.deleteDir()
+ config.jointCompilationOptions.stubDir.deleteDir()
+ }
+ }
+
// GROOVY-5743
void testClosureAsParameter() {
assertScript '''
@@ -1115,7 +1219,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
assert b.overload('a','b') == 2
'''
}
-
+
// GROOVY-5883 and GROOVY-6270
void testClosureUpperBound() {
assertScript '''
@@ -1148,7 +1252,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
String.doSomething()
''', 'Cannot find matching method java.lang.String#doSomething()'
}
-
+
// GROOVY-6646
void testNPlusVargsCallInOverloadSituation() {
assertScript '''
@@ -1166,7 +1270,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
assert foo("2","1") == "Strings"
'''
}
-
+
//GROOVY-6776
void testPrimtiveParameterAndNullArgument() {
shouldFailWithMessages '''
@@ -1252,13 +1356,13 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
import groovy.transform.CompileStatic
import java.util.stream.Collectors
import java.util.stream.Stream
-
+
@CompileStatic
public class Test1 {
public static void main(String[] args) {
p();
}
-
+
public static void p() {
assert 13 == [1, 2, 3].stream().reduce(7, {Integer r, Integer e -> r + e});
}
@@ -1272,9 +1376,9 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
assertScript '''
import groovy.transform.CompileStatic
-
+
import static java.util.stream.Collectors.toList
-
+
@CompileStatic
class Test {
static void main(String[] args) {