GROOVY-7952: Property expressions for extension methods starting with 'is' fail STC (closes #436) * Add support for 'is' getter method variants of GROOVY-5580
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/c504e645 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/c504e645 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/c504e645 Branch: refs/heads/parrot Commit: c504e645521315a3eee9bf07e6254e19ef3d2a0a Parents: f29962c Author: Shil Sinha <shil.si...@gmail.com> Authored: Tue Oct 4 17:08:20 2016 -0400 Committer: Shil Sinha <shil.si...@gmail.com> Committed: Wed Oct 5 20:10:00 2016 -0400 ---------------------------------------------------------------------- src/main/org/codehaus/groovy/ast/ClassNode.java | 4 ++- .../asm/sc/StaticTypesCallSiteWriter.java | 18 ++++++++--- .../stc/StaticTypeCheckingVisitor.java | 3 ++ .../stc/DefaultGroovyMethodsSTCTest.groovy | 7 +++++ .../transform/stc/MethodCallsSTCTest.groovy | 33 ++++++++++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/c504e645/src/main/org/codehaus/groovy/ast/ClassNode.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/ast/ClassNode.java b/src/main/org/codehaus/groovy/ast/ClassNode.java index fee628f..9b885fd 100644 --- a/src/main/org/codehaus/groovy/ast/ClassNode.java +++ b/src/main/org/codehaus/groovy/ast/ClassNode.java @@ -1092,10 +1092,12 @@ public class ClassNode extends AnnotatedNode implements Opcodes { public MethodNode getGetterMethod(String getterName, boolean searchSuperClasses) { MethodNode getterMethod = null; + boolean booleanReturnOnly = getterName.startsWith("is"); for (MethodNode method : getDeclaredMethods(getterName)) { if (getterName.equals(method.getName()) && ClassHelper.VOID_TYPE!=method.getReturnType() - && method.getParameters().length == 0) { + && method.getParameters().length == 0 + && (!booleanReturnOnly || ClassHelper.Boolean_TYPE.equals(ClassHelper.getWrapper(method.getReturnType())))) { // GROOVY-7363: There can be multiple matches for a getter returning a generic parameter type, due to // the generation of a bridge method. The real getter is really the non-bridge, non-synthetic one as it // has the most specific and exact return type of the two. Picking the bridge method results in loss of http://git-wip-us.apache.org/repos/asf/groovy/blob/c504e645/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java index e955efc..9a62622 100644 --- a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java +++ b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java @@ -158,19 +158,24 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes // GROOVY-5580, it is still possible that we're calling a superinterface property String getterName = "get" + MetaClassHelper.capitalize(methodName); + String altGetterName = "is" + MetaClassHelper.capitalize(methodName); if (receiverType.isInterface()) { Set<ClassNode> allInterfaces = receiverType.getAllInterfaces(); MethodNode getterMethod = null; for (ClassNode anInterface : allInterfaces) { getterMethod = anInterface.getGetterMethod(getterName); - if (getterMethod!=null) break; + if (getterMethod == null) getterMethod = anInterface.getGetterMethod(altGetterName); + if (getterMethod != null) break; } // GROOVY-5585 - if (getterMethod==null) { + if (getterMethod == null) { getterMethod = OBJECT_TYPE.getGetterMethod(getterName); } + if (getterMethod == null) { + getterMethod = OBJECT_TYPE.getGetterMethod(altGetterName); + } - if (getterMethod!=null) { + if (getterMethod != null) { MethodCallExpression call = new MethodCallExpression( receiver, getterName, @@ -188,13 +193,16 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes // GROOVY-5568, we would be facing a DGM call, but instead of foo.getText(), have foo.text List<MethodNode> methods = findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), receiverType, getterName, ClassNode.EMPTY_ARRAY); + for (MethodNode m: findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), receiverType, altGetterName, ClassNode.EMPTY_ARRAY)) { + if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m); + } if (!methods.isEmpty()) { List<MethodNode> methodNodes = chooseBestMethod(receiverType, methods, ClassNode.EMPTY_ARRAY); - if (methodNodes.size()==1) { + if (methodNodes.size() == 1) { MethodNode getter = methodNodes.get(0); MethodCallExpression call = new MethodCallExpression( receiver, - getterName, + getter.getName(), ArgumentListExpression.EMPTY_ARGUMENTS ); call.setMethodTarget(getter); http://git-wip-us.apache.org/repos/asf/groovy/blob/c504e645/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 1ca5429..df128eb 100644 --- a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -1315,6 +1315,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { } // GROOVY-5568, the property may be defined by DGM List<MethodNode> methods = findDGMMethodsByNameAndArguments(getTransformLoader(), testClass, "get" + capName, ClassNode.EMPTY_ARRAY); + for (MethodNode m: findDGMMethodsByNameAndArguments(getTransformLoader(), testClass, "is" + capName, ClassNode.EMPTY_ARRAY)) { + if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m); + } if (!methods.isEmpty()) { List<MethodNode> methodNodes = chooseBestMethod(testClass, methods, ClassNode.EMPTY_ARRAY); if (methodNodes.size() == 1) { http://git-wip-us.apache.org/repos/asf/groovy/blob/c504e645/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy index 3ea6a4d..a8a7531 100644 --- a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy +++ b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy @@ -144,5 +144,12 @@ class DefaultGroovyMethodsSTCTest extends StaticTypeCheckingTestCase { assert list[0] == 3 && list[0].class == Long ''' } + + // GROOVY-7952 + void testIsGetterMethodAsProperty() { + assertScript ''' + assert !'abc'.allWhitespace + ''' + } } http://git-wip-us.apache.org/repos/asf/groovy/blob/c504e645/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy index aa37ecf..d7c6573 100644 --- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy +++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy @@ -690,6 +690,39 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase { ''' } + void testIsGetterAsPropertyFromSuperInterface() { + assertScript '''interface Upper { boolean isBar() } + interface Lower extends Upper {} + boolean foo(Lower impl) { + impl.bar // isBar() called with the property notation + } + assert foo({ true } as Lower) + ''' + } + + void testIsGetterAsPropertyFromSuperInterfaceUsingConcreteImpl() { + assertScript '''interface Upper { boolean isBar() } + interface Lower extends Upper {} + class Foo implements Lower { boolean isBar() { true } } + boolean foo(Foo impl) { + impl.bar // isBar() called with the property notation + } + assert foo(new Foo()) + ''' + } + + void testIsGetterAsPropertyFromSuperInterfaceUsingConcreteImplSubclass() { + assertScript '''interface Upper { boolean isBar() } + interface Lower extends Upper {} + class Foo implements Lower { boolean isBar() { true } } + class Bar extends Foo {} + boolean foo(Bar impl) { + impl.bar // isBar() called with the property notation + } + assert foo(new Bar()) + ''' + } + // GROOVY-5580: getName variant void testGetNameFromSuperInterface() { assertScript '''interface Upper { String getName() }