diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/ConstructorReferenceSearchTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/ConstructorReferenceSearchTests.java new file mode 100644 index 0000000000..f027d81395 --- /dev/null +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/ConstructorReferenceSearchTests.java @@ -0,0 +1,251 @@ +/* + * Copyright 2009-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.jdt.core.groovy.tests.search; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.core.search.SearchPattern; +import org.junit.Test; + +/** + * Tests for {@link org.eclipse.jdt.groovy.search.ConstructorReferenceSearchRequestor} + */ +public final class ConstructorReferenceSearchTests extends SearchTestSuite { + + @Test + public void testConstructorReferences1() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo() {\n" + + " new Foo()\n" + // yes + " }\n" + + " Foo(a) {\n" + + " new Foo(a)\n" + // no + " }\n" + + "}"); + createUnit("", "Other", "import p.Foo\n" + + "new Foo()\n" + // yes + "new Foo(a)\n" + // no + "new p.Foo()\n" + // yes + "new p.Foo(a)\n"); // no + + IMethod constructor = foo.getType("Foo").getMethods()[0]; + List matches = searchForReferences(constructor); + assertEquals("Incorrect number of matches;", 3, matches.size()); + + int fooCount = 0, otherCount = 0; + for (SearchMatch match : matches) { + if (match.getElement() instanceof IMethod) { + if (((IMethod) match.getElement()).getResource().getName().equals("Foo.groovy")) { + fooCount += 1; + } else if (((IMethod) match.getElement()).getResource().getName().equals("Other.groovy")) { + otherCount += 1; + } + } + } + assertEquals("Should have found 2 matches in Foo.groovy", 1, fooCount); + assertEquals("Should have found 4 matches in Other.groovy", 2, otherCount); + } + + @Test // https://github.com/groovy/groovy-eclipse/issues/765 + public void testConstructorReferences2() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " static class Bar {\n" + + " static class Baz {\n" + + " Baz() {\n" + + " this(null)\n" + // no + " }\n" + + " Baz(def arg) {\n" + + " super()\n" + // no + " }\n" + + " }\n" + + " }\n" + + "}"); + createUnit("", "Other", "import p.Foo.Bar.Baz\n" + + "new Baz()\n" + // yes + "new Baz(a)\n"); // no + + IMethod constructor = foo.getType("Foo").getType("Bar").getType("Baz").getMethods()[0]; + List matches = searchForReferences(constructor); + assertEquals("Incorrect number of matches;", 1, matches.size()); + + int fooCount = 0, otherCount = 0; + for (SearchMatch match : matches) { + if (match.getElement() instanceof IMethod) { + if (((IMethod) match.getElement()).getResource().getName().equals("Foo.groovy")) { + fooCount += 1; + } else if (((IMethod) match.getElement()).getResource().getName().equals("Other.groovy")) { + otherCount += 1; + } + } + } + assertEquals("Should have found 0 matches in Foo.groovy", 0, fooCount); + assertEquals("Should have found 2 matches in Other.groovy", 1, otherCount); + } + + @Test + public void testConstructorReferences3() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo(... args) {}\n" + // search for this + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "new Foo()\n" + // yes + "new Foo(a)\n" + // yes + "new Foo(a,b)\n"); // yes + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[0]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(3, ctorRefs); + } + + @Test + public void testConstructorReferences4() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo(int i) {}\n" + // search for this + " Foo(String s) {}\n" + + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "new Foo()\n" + // yes + "new Foo(0)\n" + // yes + "new Foo('')\n"); // no + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[0]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(2, ctorRefs); + } + + @Test + public void testConstructorReferences5() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo(int i) {}\n" + + " Foo(String s) {}\n" + // search for this + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "new Foo()\n" + // no -- associated with first declaration + "new Foo(0)\n" + // no + "new Foo('')\n"); // yes + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[1]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(1, ctorRefs); + } + + @Test + public void testConstructorReferences6() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo() {}\n" + // search for this + " Foo(a) {}\n" + + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "new Foo()\n" + // yes + "new Foo(a)\n" + // no + "new Foo(a,b)\n"); // yes + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[0]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(2, ctorRefs); + } + + @Test + public void testConstructorReferences7() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo() {}\n" + + " Foo(a) {}\n" + // search for this + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "new Foo()\n" + // no + "new Foo(a)\n" + // yes + "new Foo(a,b)\n"); // no -- associated with first declaration + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[1]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(1, ctorRefs); + } + + @Test + public void testConstructorReferences8() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo(int i) {}\n" + // search for this + " Foo(String s) {}\n" + + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "class Bar extends Foo {\n" + + " Bar() {\n" + + " super(0)\n" + // yes + " }\n" + + "}\n"); + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[0]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(1, ctorRefs); + } + + @Test + public void testConstructorReferences9() throws Exception { + GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + + "class Foo {\n" + + " Foo(int i) {}\n" + + " Foo(String s) {}\n" + // search for this + "}"); + createUnit("", "Bar", "import p.Foo\n" + + "class Bar extends Foo {\n" + + " Bar() {\n" + + " super(0)\n" + // no + " }\n" + + "}\n"); + + long ctorRefs = searchForReferences(foo.getType("Foo").getMethods()[1]).stream() + .filter(match -> ((IMethod) match.getElement()).getResource().getName().equals("Bar.groovy")) + .count(); + assertEquals(0, ctorRefs); + } + + //-------------------------------------------------------------------------- + + List searchForReferences(IMethod method) throws CoreException { + new SearchEngine().search( + SearchPattern.createPattern(method, IJavaSearchConstants.REFERENCES), + new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, + SearchEngine.createJavaSearchScope(new IJavaElement[] {JavaCore.create(project)}, false), + searchRequestor, new NullProgressMonitor()); + return searchRequestor.getMatches(); + } +} diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/MethodReferenceSearchTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/MethodReferenceSearchTests.java index cfb518e91d..f41509d488 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/MethodReferenceSearchTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/MethodReferenceSearchTests.java @@ -33,6 +33,9 @@ import org.eclipse.jdt.groovy.search.TypeRequestorFactory; import org.junit.Test; +/** + * Tests for {@link org.eclipse.jdt.groovy.search.MethodReferenceSearchRequestor} + */ public final class MethodReferenceSearchTests extends SearchTestSuite { @Test @@ -324,90 +327,6 @@ public void testMethodWithDefaultParameters2() throws Exception { false, 0, "xxx"); } - @Test - public void testConstructorReferenceSearch1() throws Exception { - GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + - "class Foo {\n" + - " Foo() {\n" + - " new Foo()\n" + - " }\n" + - " Foo(a) {\n" + - " new Foo(a)\n" + - " }\n" + - "}"); - createUnit("", "Other", "import p.Foo\n" + - "new Foo()\n" + - "new Foo(a)\n" + - "new p.Foo()\n" + - "new p.Foo(a)\n"); - - IMethod constructor = foo.getType("Foo").getMethods()[0]; - new SearchEngine().search( - SearchPattern.createPattern(constructor, IJavaSearchConstants.REFERENCES), - new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, - SearchEngine.createJavaSearchScope(new IJavaElement[] {foo.getPackageFragmentRoot()}, false), - searchRequestor, new NullProgressMonitor()); - - List matches = searchRequestor.getMatches(); - assertEquals("Incorrect number of matches;", 6, matches.size()); - - int fooCount = 0, otherCount = 0; - for (SearchMatch match : matches) { - if (match.getElement() instanceof IMethod) { - if (((IMethod) match.getElement()).getResource().getName().equals("Foo.groovy")) { - fooCount += 1; - } else if (((IMethod) match.getElement()).getResource().getName().equals("Other.groovy")) { - otherCount += 1; - } - } - } - assertEquals("Should have found 2 matches in Foo.groovy", 2, fooCount); - assertEquals("Should have found 4 matches in Other.groovy", 4, otherCount); - } - - @Test // https://github.com/groovy/groovy-eclipse/issues/765 - public void testConstructorReferenceSearch2() throws Exception { - GroovyCompilationUnit foo = createUnit("p", "Foo", "package p\n" + - "class Foo {\n" + - " static class Bar {\n" + - " static class Baz {\n" + - " Baz() {\n" + - " this(null)\n" + - " }\n" + - " Baz(def arg) {\n" + - " super()\n" + - " }\n" + - " }\n" + - " }\n" + - "}"); - createUnit("", "Other", "import p.Foo.Bar.Baz\n" + - "new Baz()\n" + - "new Baz(a)\n"); - - IMethod constructor = foo.getType("Foo").getType("Bar").getType("Baz").getMethods()[0]; - new SearchEngine().search( - SearchPattern.createPattern(constructor, IJavaSearchConstants.REFERENCES), - new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, - SearchEngine.createJavaSearchScope(new IJavaElement[] {foo.getPackageFragmentRoot()}, false), - searchRequestor, new NullProgressMonitor()); - - List matches = searchRequestor.getMatches(); - assertEquals("Incorrect number of matches;", 2, matches.size()); - - int fooCount = 0, otherCount = 0; - for (SearchMatch match : matches) { - if (match.getElement() instanceof IMethod) { - if (((IMethod) match.getElement()).getResource().getName().equals("Foo.groovy")) { - fooCount += 1; - } else if (((IMethod) match.getElement()).getResource().getName().equals("Other.groovy")) { - otherCount += 1; - } - } - } - assertEquals("Should have found 0 matches in Foo.groovy", 0, fooCount); - assertEquals("Should have found 2 matches in Other.groovy", 2, otherCount); - } - @Test public void testStaticMethodReferenceSearch() throws Exception { String contents = diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/integration/internal/GroovyIndexingVisitor.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/integration/internal/GroovyIndexingVisitor.java index 733acee05b..2010b7e7b8 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/integration/internal/GroovyIndexingVisitor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/integration/internal/GroovyIndexingVisitor.java @@ -15,6 +15,10 @@ */ package org.codehaus.jdt.groovy.integration.internal; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.stream.Stream; + import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; @@ -48,6 +52,7 @@ */ public class GroovyIndexingVisitor extends DepthFirstVisitor { + private final Deque enclosingMethods = new ArrayDeque<>(); private final ISourceElementRequestor requestor; public GroovyIndexingVisitor(ISourceElementRequestor requestor) { @@ -102,7 +107,9 @@ public void visitMethod(MethodNode node) { } } } + enclosingMethods.push(node); super.visitMethod(node); + enclosingMethods.pop(); } @Override @@ -164,7 +171,20 @@ public void visitConstantExpression(ConstantExpression expression) { @Override public void visitConstructorCallExpression(ConstructorCallExpression expression) { - char[] typeName = expression.getType().getName().toCharArray(); + ClassNode type = expression.getType(); + if (expression.isSpecialCall()) { + type = enclosingMethods.peek().getDeclaringClass(); + if (expression.isSuperCall()) { + type = type.getUnresolvedSuperClass(); + } + } else if (expression.isUsingAnonymousInnerClass()) { + type = Stream.concat( + Stream.of(type.getUnresolvedSuperClass()), + Stream.of(type.getUnresolvedInterfaces()) + ).filter(t -> t.getEnd() > 0).findFirst().get(); + } + + char[] typeName = type.getName().toCharArray(); // we don't know how many arguments the ctor has, so go up to 9 for (int i = 0; i <= 9; i += 1) { requestor.acceptConstructorReference(typeName, i, expression.getStart()); diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/ConstructorReferenceSearchRequestor.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/ConstructorReferenceSearchRequestor.java index c25991940f..695dbaafbb 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/ConstructorReferenceSearchRequestor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/ConstructorReferenceSearchRequestor.java @@ -15,55 +15,140 @@ */ package org.eclipse.jdt.groovy.search; +import java.util.Optional; + import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.jdt.groovy.model.GroovyClassFileWorkingCopy; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.search.MethodDeclarationMatch; import org.eclipse.jdt.core.search.MethodReferenceMatch; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jdt.groovy.core.util.ReflectionUtils; import org.eclipse.jdt.internal.core.search.matching.ConstructorPattern; import org.eclipse.jdt.internal.core.util.Util; public class ConstructorReferenceSearchRequestor implements ITypeRequestor { - private final SearchRequestor requestor; - private final SearchParticipant participant; - private final String declaringQualifiedName; + protected final SearchRequestor requestor; + protected final SearchParticipant participant; + protected final String declaringQualifiedName; + protected final String[] parameterQualifiedNames; + protected final boolean findDeclarations, findReferences; public ConstructorReferenceSearchRequestor(ConstructorPattern pattern, SearchRequestor requestor, SearchParticipant participant) { this.requestor = requestor; this.participant = participant; - this.declaringQualifiedName = String.valueOf(CharOperation.concat(pattern.declaringQualification, pattern.declaringSimpleName, '.')); + + IType declaringType = ((IMethod) pattern.focus).getDeclaringType(); + char[] declaringQualifier = Optional.ofNullable(pattern.declaringQualification) + .orElse(declaringType.getPackageFragment().getElementName().toCharArray()); + this.declaringQualifiedName = String.valueOf(CharOperation.concat(declaringQualifier, pattern.declaringSimpleName, '.')); + + this.parameterQualifiedNames = new String[pattern.parameterCount]; + for (int i = 0; i < pattern.parameterCount; i += 1) { + if (pattern.parameterQualifications[i] != null || MethodReferenceSearchRequestor.isPrimitiveType(pattern.parameterSimpleNames[i])) { + parameterQualifiedNames[i] = String.valueOf(CharOperation.concat(pattern.parameterQualifications[i], pattern.parameterSimpleNames[i], '.')); + } else { + int arrayCount = 0, j = pattern.parameterSimpleNames[i].length - 1; + while (pattern.parameterSimpleNames[i][j] == ']') { + arrayCount += 1; + j -= 2; + } + try { + String[][] resolved = declaringType.resolveType(String.valueOf(pattern.parameterSimpleNames[i], 0, j + 1)); + parameterQualifiedNames[i] = Signature.toQualifiedName(resolved[0]); + if (parameterQualifiedNames[i].charAt(0) == '.') { // default package + parameterQualifiedNames[i] = parameterQualifiedNames[i].substring(1); + } + while (arrayCount-- > 0) { + parameterQualifiedNames[i] += "[]"; + } + } catch (Exception e) { + Util.log(e); + } + } + } + + this.findDeclarations = (Boolean) ReflectionUtils.getPrivateField(ConstructorPattern.class, "findDeclarations", pattern); + this.findReferences = (Boolean) ReflectionUtils.getPrivateField(ConstructorPattern.class, "findReferences", pattern); } @Override public VisitStatus acceptASTNode(ASTNode node, TypeLookupResult result, IJavaElement enclosingElement) { - if (node instanceof ConstructorCallExpression && node.getEnd() > 0) { - ConstructorCallExpression call = (ConstructorCallExpression) node; + if (node.getEnd() > 0 && result.declaration instanceof ConstructorNode) { - // only match on type name, not method parameters (for now) - if (result.declaringType.getName().replace('$', '.').equals(declaringQualifiedName)) { + if (findDeclarations && node instanceof ConstructorNode) { + ConstructorNode decl = (ConstructorNode) node; + String typeName = result.declaringType.getName().replace('$', '.'); + if (typeName.equals(declaringQualifiedName) && hasMatchingParameters(decl.getParameters())) { - // must translate from synthetic source to binary if necessary - if (enclosingElement.getOpenable() instanceof GroovyClassFileWorkingCopy) { - enclosingElement = ((GroovyClassFileWorkingCopy) enclosingElement.getOpenable()).convertToBinary(enclosingElement); + if (enclosingElement.getOpenable() instanceof GroovyClassFileWorkingCopy) { + enclosingElement = ((GroovyClassFileWorkingCopy) enclosingElement.getOpenable()).convertToBinary(enclosingElement); + } + try { + requestor.acceptSearchMatch(new MethodDeclarationMatch( + enclosingElement, SearchMatch.A_ACCURATE, decl.getNameStart(), decl.getNameEnd() + 1 - decl.getNameStart(), + participant, enclosingElement.getResource())); + } catch (CoreException e) { + Util.log(e, "Error reporting search match inside of " + enclosingElement + " in resource " + enclosingElement.getResource()); + } } - boolean isConstructor = true, isSynthetic = false, isSuperInvocation = call.isSuperCall(), isWithinComment = false; + } - SearchMatch match = new MethodReferenceMatch( - enclosingElement, SearchMatch.A_ACCURATE, call.getNameStart(), call.getNameEnd() + 1 - call.getNameStart(), - isConstructor, isSynthetic, isSuperInvocation, isWithinComment, participant, enclosingElement.getResource()); - try { - requestor.acceptSearchMatch(match); - } catch (CoreException e) { - Util.log(e, "Error reporting search match inside of " + enclosingElement + " in resource " + enclosingElement.getResource()); + if (findReferences && node instanceof ConstructorCallExpression) { + ConstructorCallExpression call = (ConstructorCallExpression) node; + String typeName = result.declaringType.getName().replace('$', '.'); + Parameter[] parameters = ((ConstructorNode) result.declaration).getParameters(); + if (typeName.equals(declaringQualifiedName) && hasMatchingParameters(parameters)) { + boolean isConstructor = true, isSynthetic = false, isSuperInvocation = call.isSuperCall(), isWithinComment = false; + + if (enclosingElement.getOpenable() instanceof GroovyClassFileWorkingCopy) { + enclosingElement = ((GroovyClassFileWorkingCopy) enclosingElement.getOpenable()).convertToBinary(enclosingElement); + } + try { + requestor.acceptSearchMatch(new MethodReferenceMatch( + enclosingElement, SearchMatch.A_ACCURATE, call.getNameStart(), call.getNameEnd() + 1 - call.getNameStart(), + isConstructor, isSynthetic, isSuperInvocation, isWithinComment, participant, enclosingElement.getResource())); + } catch (CoreException e) { + Util.log(e, "Error reporting search match inside of " + enclosingElement + " in resource " + enclosingElement.getResource()); + } } } } return VisitStatus.CONTINUE; } + + protected boolean hasMatchingParameters(Parameter[] declarationParameters) { + if (declarationParameters.length == parameterQualifiedNames.length) { + for (int i = 0; i < parameterQualifiedNames.length; i += 1) { + ClassNode candidate = declarationParameters[i].getType(); + ClassNode pattern = makeType(parameterQualifiedNames[i]); + if (!candidate.equals(pattern)) { + return false; + } + } + return true; + } + return false; + } + + protected static ClassNode makeType(String typeName) { + int i = typeName.indexOf('['); + if (i < 0) { + return ClassHelper.make(typeName); + } + return makeType(typeName.substring(0, i)).makeArray(); + } } diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/MethodReferenceSearchRequestor.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/MethodReferenceSearchRequestor.java index 3f585184e4..675cbbbd5c 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/MethodReferenceSearchRequestor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/MethodReferenceSearchRequestor.java @@ -26,7 +26,6 @@ import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; -import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.PropertyNode; @@ -239,6 +238,7 @@ public VisitStatus acceptASTNode(ASTNode node, TypeLookupResult result, IJavaEle if (!acceptedPositions.contains(position)) { if (nameAndArgsMatch(GroovyUtils.getBaseType(result.declaringType), isDeclaration ? GroovyUtils.getParameterTypes(((MethodNode) node).getParameters()) : result.scope.getMethodCallArgumentTypes())) { + if (enclosingElement.getOpenable() instanceof GroovyClassFileWorkingCopy) { enclosingElement = ((GroovyClassFileWorkingCopy) enclosingElement.getOpenable()).convertToBinary(enclosingElement); } @@ -291,7 +291,7 @@ private boolean nameAndArgsMatch(ClassNode declaringType, List argume if (argumentTypes.size() == parameterTypeNames.length) { for (int i = 0; i < parameterTypeNames.length; i += 1) { if (parameterTypeNames[i] == null) continue; // skip check - ClassNode source = argumentTypes.get(i), target = ClassHelper.makeWithoutCaching(parameterTypeNames[i]); + ClassNode source = argumentTypes.get(i), target = ConstructorReferenceSearchRequestor.makeType(parameterTypeNames[i]); if (Boolean.FALSE.equals(SimpleTypeLookup.isTypeCompatible(source, target))) { return false; } diff --git a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java index 3f5efdd7be..4e8ad21f2a 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java @@ -969,7 +969,7 @@ public void visitConstructorCallExpression(ConstructorCallExpression node) { Stream.of(type.getUnresolvedSuperClass()), Stream.of(type.getUnresolvedInterfaces())); superTypes.filter(t -> t.getEnd() > 0).forEach(this::visitClassReference); - } else { + } else if (!node.isSpecialCall()) { visitClassReference(type); } if (node.getArguments() instanceof TupleExpression) { diff --git a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy index 5ac7312603..55c660a3ac 100644 --- a/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy +++ b/ide-test/org.codehaus.groovy.alltests/src/org/codehaus/groovy/alltests/GroovyJDTTests.groovy @@ -74,6 +74,7 @@ import org.junit.runners.Suite org.eclipse.jdt.core.groovy.tests.search.BinarySearchTests, org.eclipse.jdt.core.groovy.tests.search.CategorySearchTests, org.eclipse.jdt.core.groovy.tests.search.ClosureInferencingTests, + org.eclipse.jdt.core.groovy.tests.search.ConstructorReferenceSearchTests, org.eclipse.jdt.core.groovy.tests.search.DGMInferencingTests, org.eclipse.jdt.core.groovy.tests.search.DeclarationInferencingTests, org.eclipse.jdt.core.groovy.tests.search.FieldReferenceSearchTests,