diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java index 4688c0f0fd..a1d50b8233 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java @@ -1472,6 +1472,151 @@ public void testSuperPropertyReference5() { assertUnknown(contents, "value"); } + @Test + public void testSuperPropertyReference6() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " boolean isValue() {}\n" + + " boolean getValue() {}\n" + + "}\n" + + "class B extends A {\n" + + " void test() {\n" + + " " + qual + "value\n" + + " }\n" + + "}"; + int offset = contents.lastIndexOf("value"); + assertDeclaration(contents, offset, offset + 5, "A", "getValue", DeclarationKind.METHOD); + } + } + + @Test // GROOVY-1736 + public void testSuperPropertyReference6a() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " boolean isValue() {}\n" + + "}\n" + + "class B extends A {\n" + + " boolean getValue() {}\n" + + " void test() {\n" + + " " + qual + "value\n" + + " }\n" + + "}"; + int offset = contents.lastIndexOf("value"); + if (qual.startsWith("super")) { + assertUnknownConfidence(contents, offset, offset + 5); + } else { + assertDeclaration(contents, offset, offset + 5, "B", "getValue", DeclarationKind.METHOD); + } + } + } + + @Test + public void testSuperPropertyReference6b() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " boolean value\n" + + "}\n" + + "class B extends A {\n" + + " boolean getValue() {}\n" + + " void test() {\n" + + " " + qual + "value\n" + + " }\n" + + "}"; + int offset = contents.lastIndexOf("value"); + if (qual.startsWith("super")) { + assertDeclaration(contents, offset, offset + 5, "A", "value", DeclarationKind.PROPERTY); + } else { + assertDeclaration(contents, offset, offset + 5, "B", "getValue", DeclarationKind.METHOD); + } + } + } + + @Test // isX only applies to [Bb]oolean + public void testSuperPropertyReference7() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " Number isValue() {}\n" + + " Number getValue() {}\n" + + "}\n" + + "class B extends A {\n" + + " void test() {\n" + + " " + qual + "value\n" + + " }\n" + + "}"; + int offset = contents.lastIndexOf("value"); + assertDeclaration(contents, offset, offset + 5, "A", "getValue", DeclarationKind.METHOD); + } + } + + @Test + public void testSuperPropertyReference8() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " boolean value\n" + + " boolean getValue() {}\n" + // no isValue() generated + "}\n" + + "class B extends A {\n" + + " void test() {\n" + + " " + qual + "value\n" + + " " + qual + "isValue()\n" + + " }\n" + + "}"; + assertUnknown(contents, "isValue"); + int offset = contents.lastIndexOf("value"); + assertDeclaration(contents, offset, offset + 5, "A", "getValue", DeclarationKind.METHOD); + } + } + + @Test // GROOVY-6097 + public void testSuperPropertyReference9() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " boolean value\n" + + " boolean isValue() {}\n" + // no getValue() generated + "}\n" + + "class B extends A {\n" + + " void test() {\n" + + " " + qual + "value\n" + + " " + qual + "getValue()\n" + + " }\n" + + "}"; + assertUnknown(contents, "getValue"); + if (qual.startsWith("super")) { + assertUnknown(contents, "value"); + } else { + int offset = contents.lastIndexOf("value"); + assertDeclaration(contents, offset, offset + 5, "A", "isValue", DeclarationKind.METHOD); + } + } + } + + @Test + public void testSuperPropertyReference9a() { + for (String qual : new String[] {"", "this.", "super."}) { + String contents = + "class A {\n" + + " Boolean value\n" + + " Boolean isValue() {}\n" + // getValue() generated + "}\n" + + "class B extends A {\n" + + " void test() {\n" + + " " + qual + "value\n" + + " " + qual + "getValue()\n" + + " }\n" + + "}"; + int offset = contents.lastIndexOf("value"); + assertDeclaration(contents, offset, offset + 5, "A", "value", DeclarationKind.PROPERTY); + /**/offset = contents.lastIndexOf("getValue"); + assertDeclaration(contents, offset, offset + 8, "A", "getValue", DeclarationKind.METHOD); + } + } + @Test public void testSuperClassMethod1() { String contents = diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java index 9e409d5d98..d4f1756376 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java @@ -5074,19 +5074,17 @@ public void testExtendingGroovyObjects_clinit() { public void testGroovyPropertyAccessors1() { //@formatter:off String[] sources = { - "p/C.java", - "package p;\n" + + "C.java", "public class C {\n" + " public static void main(String[] argv) {\n" + - " G o = new G();\n" + - " System.out.print(o.isB());\n" + - " System.out.print(o.getB());\n" + + " G pogo = new G();\n" + + " System.out.print(pogo.isB());\n" + + " System.out.print(pogo.getB());\n" + " }\n" + "}\n", - "p/G.groovy", - "package p;\n" + - "public class G {\n" + + "G.groovy", + "class G {\n" + " boolean b\n" + "}\n", }; @@ -5099,20 +5097,18 @@ public void testGroovyPropertyAccessors1() { public void testGroovyPropertyAccessors2() { //@formatter:off String[] sources = { - "p/C.java", - "package p;\n" + + "C.java", "public class C {\n" + " public static void main(String[] argv) {\n" + - " G o = new G();\n" + - " System.out.print(o.getB());\n" + - " o.setB(true);\n" + - " System.out.print(o.getB());\n" + + " G pogo = new G();\n" + + " System.out.print(pogo.getB());\n" + + " pogo.setB(true);\n" + + " System.out.print(pogo.getB());\n" + " }\n" + "}\n", - "p/G.groovy", - "package p;\n" + - "public class G {\n" + + "G.groovy", + "class G {\n" + " boolean b\n" + "}\n", }; @@ -5121,12 +5117,71 @@ public void testGroovyPropertyAccessors2() { runConformTest(sources, "falsetrue"); } - @Test // @Deprecated should be propagated to accessors + @Test public void testGroovyPropertyAccessors3() { //@formatter:off String[] sources = { - "p/G.groovy", - "package p;\n" + + "C.java", + "public class C {\n" + + " public static void main(String[] argv) {\n" + + " G pogo = new G();\n" + + " System.out.print(pogo.isB());\n" + + " System.out.print(pogo.getB());\n" + + " }\n" + + "}\n", + + "G.groovy", + "class G {\n" + + " boolean b\n" + + " boolean isB() {}\n" + + "}\n", + }; + //@formatter:on + + runNegativeTest(sources, + "----------\n" + + "1. ERROR in C.java (at line 5)\n" + + "\tSystem.out.print(pogo.getB());\n" + + "\t ^^^^\n" + + "The method getB() is undefined for the type G\n" + + "----------\n"); + } + + @Test + public void testGroovyPropertyAccessors4() { + //@formatter:off + String[] sources = { + "C.java", + "public class C {\n" + + " public static void main(String[] argv) {\n" + + " G pogo = new G();\n" + + " System.out.print(pogo.isB());\n" + + " System.out.print(pogo.getB());\n" + + " }\n" + + "}\n", + + "G.groovy", + "class G {\n" + + " boolean b\n" + + " boolean getB() {}\n" + + "}\n", + }; + //@formatter:on + + runNegativeTest(sources, + "----------\n" + + "1. ERROR in C.java (at line 4)\n" + + "\tSystem.out.print(pogo.isB());\n" + + "\t ^^^\n" + + "The method isB() is undefined for the type G\n" + + "----------\n"); + } + + @Test // @Deprecated should be propagated to accessors + public void testGroovyPropertyAccessors5() { + //@formatter:off + String[] sources = { + "G.groovy", "class G {\n" + " @Deprecated\n" + " boolean flag\n" + @@ -5136,19 +5191,19 @@ public void testGroovyPropertyAccessors3() { runNegativeTest(sources, ""); - checkDisassemblyFor("p/G.class", + checkDisassemblyFor("G.class", " @java.lang.Deprecated\n" + " private boolean flag;\n"); - checkDisassemblyFor("p/G.class", + checkDisassemblyFor("G.class", " @java.lang.Deprecated\n @groovy.transform.Generated\n" + " public boolean isFlag();\n"); - checkDisassemblyFor("p/G.class", + checkDisassemblyFor("G.class", " @java.lang.Deprecated\n @groovy.transform.Generated\n" + " public boolean getFlag();\n"); - checkDisassemblyFor("p/G.class", + checkDisassemblyFor("G.class", " @java.lang.Deprecated\n @groovy.transform.Generated\n" + " public void setFlag(boolean arg0);\n"); } diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyClassScope.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyClassScope.java index 0f6109631b..1717a28f53 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyClassScope.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyClassScope.java @@ -129,10 +129,12 @@ protected MethodBinding[] augmentMethodBindings(final MethodBinding[] methodBind String capitalizedName = MetaClassHelper.capitalize(property.getName()); if (property.getType().equals(ClassHelper.boolean_TYPE)) { + if (!createGetterMethod(property, "get" + capitalizedName, modifiers, methodBindings).isPresent()) { + continue; // only generate accessor method(s) if one or both are not explicitly declared + } createGetterMethod(property, "is" + capitalizedName, modifiers, methodBindings) .ifPresent(binding -> { groovyMethods.add(binding); - // GROOVY-9382: no getter generated if isser declared createGetterMethod(property, "get" + capitalizedName, modifiers, methodBindings) .ifPresent(groovyMethods::add); }); diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java index 33c71c69b3..22cfe93efb 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/JDTClassNode.java @@ -302,14 +302,16 @@ private void initializeMembers() { for (PropertyNode pNode : getProperties()) { String pName = pNode.getName(), capitalizedName = MetaClassHelper.capitalize(pName); int mMods = Flags.AccPublic | (pNode.getModifiers() & Flags.AccStatic); - if (ClassHelper.boolean_TYPE.equals(pNode.getType())) { + if (pNode.getType().equals(ClassHelper.boolean_TYPE)) { + // only generate accessor method(s) if one or both are not explicitly declared + if (getDeclaredMethod("get" + capitalizedName, Parameter.EMPTY_ARRAY) != null) continue; + MethodNode mNode = addMethod("is" + capitalizedName, mMods, pNode.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); if (!(mNode instanceof JDTNode)) { mNode.setNameStart(pNode.getField().getNameStart()); mNode.setNameEnd(pNode.getField().getNameEnd()); mNode.setSynthetic(true); - // GROOVY-9382: include "getter" if "isser" was not declared mNode = addMethod("get" + capitalizedName, mMods, pNode.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null); if (!(mNode instanceof JDTNode)) { mNode.setNameStart(pNode.getField().getNameStart()); diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java index bcee907b93..56399fdd58 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java @@ -421,6 +421,10 @@ protected TypeLookupResult findTypeForNameWithKnownObjectExpression(final String } else if (method.isPrivate() && isThisObjectExpression(scope) && isNotThisOrOuterClass(declaringType, resolvedDeclaringType)) { // "this.method()" reference to private method of super class yields MissingMethodException; "super.method()" is okay confidence = TypeConfidence.UNKNOWN; + } else if (method.getName().startsWith("is") && !name.startsWith("is") && isSuperObjectExpression(scope)) { + // GROOVY-1736, GROOVY-6097: "super.name" => "super.getName()" in AsmClassGenerator + confidence = TypeConfidence.UNKNOWN; + declaration = null; } else if (isLooseMatch(scope.getMethodCallArgumentTypes(), method.getParameters()) && !(isStaticObjectExpression && isStaticReferenceToUnambiguousMethod(scope, name, declaringType)) && !(AccessorSupport.isGetter(method) && !scope.isMethodCall() && scope.getEnclosingNode() instanceof PropertyExpression)) {