diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaSearchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaSearchTests.java index 01c6cc44468..8219fbc0ec1 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaSearchTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaSearchTests.java @@ -20,6 +20,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; + import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -910,7 +912,12 @@ protected void assertSearchResults(String expected, JavaSearchResultCollector co assertSearchResults("Unexpected search results", expected, collector); } protected void assertSearchResults(String message, String expected, JavaSearchResultCollector collector) { - String actual = collector.toString(); + String actual = Arrays.stream(collector.toString().split("\n")) + .sorted() + .collect(Collectors.joining("\n")); // order doesn't matter + expected = Arrays.stream(expected.split("\n")) + .sorted() + .collect(Collectors.joining("\n")); // order doesn't matter if (!expected.equals(actual)) { if (this.displayName) { System.out.print(getName()); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java index fc7a923b409..136f6fd77e4 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java @@ -13,13 +13,37 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; +import java.util.List; + import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; +import org.eclipse.jdt.core.dom.CreationReference; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; @@ -58,6 +82,17 @@ public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) { return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match } @Override +public int match(MethodDeclaration node, MatchingNodeSet nodeSet) { + if (!node.isConstructor()) { + return IMPOSSIBLE_MATCH; + } + if (this.pattern.fineGrain != 0 && !this.pattern.findDeclarations) return IMPOSSIBLE_MATCH; + int referencesLevel = /* this.pattern.findReferences ? matchLevelForReferences(node) : */IMPOSSIBLE_MATCH; + int declarationsLevel = this.pattern.findDeclarations ? matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH; + + return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match +} +@Override public int match(Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; if (!(node instanceof AllocationExpression)) return IMPOSSIBLE_MATCH; @@ -73,6 +108,52 @@ public int match(Expression node, MatchingNodeSet nodeSet) { // interested in Al return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } @Override +public int match(org.eclipse.jdt.core.dom.Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression + if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; + if (node instanceof CreationReference creationRef && (this.pattern.declaringSimpleName == null || matchesTypeReference(this.pattern.declaringSimpleName, creationRef.getType()))) { + return this.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH; + } + if (node instanceof ClassInstanceCreation newInstance) { + return (this.pattern.declaringSimpleName == null || matchesTypeReference(this.pattern.declaringSimpleName, newInstance.getType())) + && matchParametersCount(node, newInstance.arguments()) ? + POSSIBLE_MATCH : IMPOSSIBLE_MATCH; + } + return IMPOSSIBLE_MATCH; +} +@Override +public int match(org.eclipse.jdt.core.dom.ASTNode node, MatchingNodeSet nodeSet) { + if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; + if (node instanceof SuperConstructorInvocation superRef) { + if (!matchParametersCount(node, superRef.arguments())) { + return IMPOSSIBLE_MATCH; + } + if (this.pattern.declaringSimpleName != null) { + Type superType = null; + var current = superRef.getParent(); + while (current != null && !(current instanceof AbstractTypeDeclaration) && !(current instanceof CreationReference)) { + current = current.getParent(); + } + if (current instanceof org.eclipse.jdt.core.dom.TypeDeclaration typeDecl) { + superType = typeDecl.getSuperclassType(); + } + if (current instanceof CreationReference newInstance) { + superType = newInstance.getType(); + } + if (!matchesTypeReference(this.pattern.declaringSimpleName, superType)) { + return IMPOSSIBLE_MATCH; + } + } + return this.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH; + } + if (node instanceof EnumConstantDeclaration enumConstantDecl + && node.getParent() instanceof EnumDeclaration enumDeclaration + && matchesName(this.pattern.declaringSimpleName, enumDeclaration.getName().getIdentifier().toCharArray()) + && matchParametersCount(enumConstantDecl, enumConstantDecl.arguments())) { + return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); + } + return IMPOSSIBLE_MATCH; +} +@Override public int match(FieldDeclaration field, MatchingNodeSet nodeSet) { if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; // look only for enum constant @@ -151,6 +232,34 @@ protected int matchConstructor(MethodBinding constructor) { } return level; } +protected int matchConstructor(IMethodBinding constructor) { + if (!constructor.isConstructor()) return IMPOSSIBLE_MATCH; + + // declaring type, simple name has already been matched by matchIndexEntry() + int level = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, constructor.getDeclaringClass()); + if (level == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; + + // parameter types + int parameterCount = this.pattern.parameterCount; + if (parameterCount > -1) { + if (parameterCount != constructor.getParameterTypes().length) return IMPOSSIBLE_MATCH; + for (int i = 0; i < parameterCount; i++) { + // TODO (frederic) use this call to refine accuracy on parameter types +// int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, constructor.parameters[i]); + int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], constructor.getParameterTypes()[i]); + if (level > newLevel) { + if (newLevel == IMPOSSIBLE_MATCH) { +// if (isErasureMatch) { +// return ERASURE_MATCH; +// } + return IMPOSSIBLE_MATCH; + } + level = newLevel; // can only be downgraded + } + } + } + return level; +} @Override protected int matchContainer() { if (this.pattern.findReferences) return ALL_CONTAINER; // handles both declarations + references & just references @@ -194,6 +303,25 @@ protected int matchLevelForDeclarations(ConstructorDeclaration constructor) { return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; } +protected int matchLevelForDeclarations(MethodDeclaration constructor) { + // constructor name is stored in selector field + if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, constructor.getName().toString().toCharArray())) + return IMPOSSIBLE_MATCH; + + if (this.pattern.parameterSimpleNames != null) { + int length = this.pattern.parameterSimpleNames.length; + var args = constructor.parameters(); + int argsLength = args == null ? 0 : args.size(); + if (length != argsLength) return IMPOSSIBLE_MATCH; + } + + // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match) + if (this.pattern.hasConstructorArguments()) { + if (constructor.typeParameters() == null || constructor.typeParameters().size() != this.pattern.constructorArguments.length) return IMPOSSIBLE_MATCH; + } + + return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; +} boolean matchParametersCount(ASTNode node, Expression[] args) { if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) { int length = this.pattern.parameterCount; @@ -205,6 +333,17 @@ boolean matchParametersCount(ASTNode node, Expression[] args) { } return true; } +boolean matchParametersCount(org.eclipse.jdt.core.dom.ASTNode node, List args) { + if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || DOMASTNodeUtils.insideDocComment(node))) { + int length = this.pattern.parameterCount; + if (length < 0) length = this.pattern.parameterSimpleNames.length; + int argsLength = args == null ? 0 : args.size(); + if (length != argsLength) { + return false; + } + } + return true; +} @Override protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { @@ -374,6 +513,19 @@ public int resolveLevel(Binding binding) { } return level; } +@Override +public int resolveLevel(IBinding binding) { + if (binding instanceof IMethodBinding constructor) { + int level= matchConstructor(constructor); + if (level== IMPOSSIBLE_MATCH) { + if (constructor != constructor.getMethodDeclaration()) { + level= matchConstructor(constructor.getMethodDeclaration()); + } + } + return level; + } + return IMPOSSIBLE_MATCH; +} protected int resolveLevel(ConstructorDeclaration constructor, boolean checkDeclarations) { int referencesLevel = IMPOSSIBLE_MATCH; if (this.pattern.findReferences) { diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/DOMASTNodeUtils.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/DOMASTNodeUtils.java new file mode 100644 index 00000000000..7e097a29a98 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/DOMASTNodeUtils.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.internal.core.search.matching; + +import java.util.List; +import java.util.Optional; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; +import org.eclipse.jdt.core.dom.Comment; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.FieldAccess; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.MethodRef; +import org.eclipse.jdt.core.dom.MethodReference; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.SuperFieldAccess; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.SuperMethodReference; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.VariableDeclaration; + +public class DOMASTNodeUtils { + + public static IJavaElement getEnclosingJavaElement(ASTNode node) { + if (node == null) { + return null; + } + if (node instanceof AbstractTypeDeclaration + || node instanceof MethodDeclaration + || node instanceof VariableDeclaration + || node instanceof CompilationUnit + || node instanceof AnnotationTypeMemberDeclaration) { + return getDeclaringJavaElement(node); + } + return getEnclosingJavaElement(node.getParent()); + } + + public static IJavaElement getDeclaringJavaElement(ASTNode key) { + if (key instanceof CompilationUnit unit) { + return unit.getJavaElement(); + } + return Optional.ofNullable(key).map(DOMASTNodeUtils::getBinding).map(IBinding::getJavaElement).orElse(null); + } + + private static IBinding getBinding(ASTNode astNode) { + if (astNode instanceof Name name) { + return name.resolveBinding(); + } + if (astNode instanceof VariableDeclaration variable) { + return variable.resolveBinding(); + } + if (astNode instanceof EnumConstantDeclaration enumConstantDeclaration) { + return enumConstantDeclaration.resolveVariable(); + } + if (astNode instanceof FieldAccess fieldAcces) { + return fieldAcces.resolveFieldBinding(); + } + if (astNode instanceof MethodInvocation method) { + return method.resolveMethodBinding(); + } + if (astNode instanceof Type type) { + return type.resolveBinding(); + } + if (astNode instanceof AbstractTypeDeclaration type) { + return type.resolveBinding(); + } + if (astNode instanceof MethodDeclaration method) { + return method.resolveBinding(); + } + if (astNode instanceof SuperFieldAccess superField) { + return superField.resolveFieldBinding(); + } + if (astNode instanceof SuperMethodInvocation superMethod) { + return superMethod.resolveMethodBinding(); + } + if (astNode instanceof SuperMethodReference superRef) { + return superRef.resolveMethodBinding(); + } + if (astNode instanceof MethodRef methodRef) { + return methodRef.resolveBinding(); + } + if (astNode instanceof MethodReference methodRef) { + return methodRef.resolveMethodBinding(); + } + // TODO more... + return null; + } + + public static boolean insideDocComment(org.eclipse.jdt.core.dom.ASTNode node) { + return node.getRoot() instanceof org.eclipse.jdt.core.dom.CompilationUnit unit && + ((List)unit.getCommentList()).stream().anyMatch(comment -> comment.getStartPosition() <= node.getStartPosition() && comment.getStartPosition() + comment.getLength() >= node.getStartPosition() + node.getLength()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/FieldLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/FieldLocator.java index c8bfd4ceb0a..a9bc3663d16 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/FieldLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/FieldLocator.java @@ -19,11 +19,38 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.search.FieldDeclarationMatch; import org.eclipse.jdt.core.search.SearchMatch; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.CompactConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.NameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.Reference; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.env.IBinaryType; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedFieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.core.JavaElement; @@ -61,6 +88,26 @@ public int match(ASTNode node, MatchingNodeSet nodeSet) { } return nodeSet.addMatch(node, declarationsLevel); } +@Override +public int match(org.eclipse.jdt.core.dom.ASTNode node, MatchingNodeSet nodeSet) { + int declarationsLevel = IMPOSSIBLE_MATCH; + if (node instanceof EnumConstantDeclaration enumConstant) { + return match(enumConstant, nodeSet); + } + if (this.pattern.findReferences) { + if (node instanceof ImportDeclaration importRef) { + // With static import, we can have static field reference in import reference + if (importRef.isStatic() && !importRef.isOnDemand() && matchesName(this.pattern.name, importRef.getName().toString().toCharArray()) + && this.pattern instanceof FieldPattern fieldPattern) { + char[] declaringType = CharOperation.concat(fieldPattern.declaringQualification, fieldPattern.declaringSimpleName, '.'); + if (matchesName(declaringType, importRef.getName().toString().toCharArray())) { + declarationsLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + } + } + } + } + return nodeSet.addMatch(node, declarationsLevel); +} //public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT @Override public int match(FieldDeclaration node, MatchingNodeSet nodeSet) { @@ -84,6 +131,47 @@ public int match(FieldDeclaration node, MatchingNodeSet nodeSet) { } return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match } +@Override +public int match(VariableDeclaration node, MatchingNodeSet nodeSet) { + if (!this.pattern.findDeclarations) { + return IMPOSSIBLE_MATCH; + } + if (node.getLocationInParent() != org.eclipse.jdt.core.dom.FieldDeclaration.FRAGMENTS_PROPERTY) { + return IMPOSSIBLE_MATCH; + } + int referencesLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findReferences) + // must be a write only access with an initializer + if (this.pattern.writeAccess && !this.pattern.readAccess && node.getInitializer() != null) + if (matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray())) + referencesLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + + int declarationsLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findDeclarations && + matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray()) && + this.pattern instanceof FieldPattern fieldPattern && + matchesTypeReference(fieldPattern.typeSimpleName, ((org.eclipse.jdt.core.dom.FieldDeclaration)node.getParent()).getType())) { + declarationsLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + } + return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match +} +private int match(EnumConstantDeclaration node, MatchingNodeSet nodeSet) { + int referencesLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findReferences) + // must be a write only access with an initializer + if (this.pattern.writeAccess && !this.pattern.readAccess) + if (matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray())) + referencesLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + + int declarationsLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findDeclarations && + matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray()) && + this.pattern instanceof FieldPattern fieldPattern && + matchesName(fieldPattern.typeSimpleName, ((EnumDeclaration)node.getParent()).getName().getIdentifier().toCharArray())) { + declarationsLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + } + return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match +} //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT //public int match(MessageSend node, MatchingNodeSet nodeSet) - SKIP IT //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT @@ -137,6 +225,40 @@ protected int matchField(FieldBinding field, boolean matchName) { int typeLevel = resolveLevelForType(fieldBinding.type); return declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match } +protected int matchField(IVariableBinding field, boolean matchName) { + if (field == null) return INACCURATE_MATCH; + if (!field.isField()) return IMPOSSIBLE_MATCH; + + if (matchName && !matchesName(this.pattern.name, field.getName().toCharArray())) return IMPOSSIBLE_MATCH; + + FieldPattern fieldPattern = (FieldPattern)this.pattern; + ITypeBinding receiverBinding = field.getDeclaringClass(); + if (receiverBinding == null) { + if (field == ArrayBinding.ArrayLength) + // optimized case for length field of an array + return fieldPattern.declaringQualification == null && fieldPattern.declaringSimpleName == null + ? ACCURATE_MATCH + : IMPOSSIBLE_MATCH; + return INACCURATE_MATCH; + } + + // Note there is no dynamic lookup for field access + int declaringLevel = resolveLevelForType(fieldPattern.declaringSimpleName, fieldPattern.declaringQualification, receiverBinding); + if (declaringLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; + + // look at field type only if declaring type is not specified + if (fieldPattern.declaringSimpleName == null) return declaringLevel; + + // get real field binding + // TODO what is a ParameterizedFieldBinding? +// FieldBinding fieldBinding = field; +// if (field instanceof ParameterizedFieldBinding) { +// fieldBinding = ((ParameterizedFieldBinding) field).originalField; +// } + + int typeLevel = resolveLevelForType(field.getType()); + return declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match +} /* (non-Javadoc) * @see org.eclipse.jdt.internal.core.search.matching.PatternLocator#matchLevelAndReportImportRef(org.eclipse.jdt.internal.compiler.ast.ImportReference, org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.internal.core.search.matching.MatchLocator) * Accept to report match of static field on static import @@ -161,6 +283,16 @@ protected void matchReportReference(ASTNode reference, IJavaElement element, Bin matchReportReference(reference, element, null, null, elementBinding, accuracy, locator); } @Override +public int match(Name name, MatchingNodeSet nodeSet) { + if (this.pattern.findDeclarations) { + return IMPOSSIBLE_MATCH; // already caught by match(VariableDeclaration) + } + if (matchesName(this.pattern.name, name.toString().toCharArray())) { + return nodeSet.addMatch(name, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); + } + return IMPOSSIBLE_MATCH; +} +@Override protected void matchReportReference(ASTNode reference, IJavaElement element, IJavaElement localElement, IJavaElement[] otherElements,Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { if (this.isDeclarationOfAccessedFieldsPattern) { // need exact match to be able to open on type ref @@ -346,6 +478,22 @@ public int resolveLevel(Binding binding) { return matchField((FieldBinding) binding, true); } +@Override +public int resolveLevel(IBinding binding) { + if (binding == null) return INACCURATE_MATCH; + if(binding instanceof IVariableBinding variableBinding) { + if (variableBinding.isRecordComponent()) { + // for matching the component in constructor of a record + if (!matchesName(this.pattern.name, variableBinding.getName().toCharArray())) return IMPOSSIBLE_MATCH; + FieldPattern fieldPattern = (FieldPattern)this.pattern; + return resolveLevelForType(fieldPattern.declaringSimpleName, fieldPattern.declaringQualification,variableBinding.getDeclaringMethod().getDeclaringClass()); + } + if (variableBinding.isField()) { + return matchField(variableBinding, true); + } + } + return IMPOSSIBLE_MATCH; +} protected int resolveLevel(NameReference nameRef) { if (nameRef instanceof SingleNameReference) return resolveLevel(nameRef.binding); @@ -391,4 +539,17 @@ protected int resolveLevelForType(TypeBinding typeBinding) { 0, fieldTypeBinding); } +protected int resolveLevelForType(ITypeBinding typeBinding) { + FieldPattern fieldPattern = (FieldPattern) this.pattern; + ITypeBinding fieldTypeBinding = typeBinding; + if (fieldTypeBinding != null && fieldTypeBinding.isParameterizedType()) { + fieldTypeBinding = typeBinding.getErasure(); + } + return resolveLevelForType( + fieldPattern.typeSimpleName, + fieldPattern.typeQualification, + // fieldPattern.getTypeArguments(), + // 0, + fieldTypeBinding); +} } diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariableLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariableLocator.java index ede064e910b..3ae05915100 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariableLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariableLocator.java @@ -13,8 +13,18 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; +import java.util.Objects; + import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.CompactConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; @@ -50,6 +60,23 @@ public int match(LocalDeclaration node, MatchingNodeSet nodeSet) { return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match } +@Override +public int match(VariableDeclaration node, MatchingNodeSet nodeSet) { + int referencesLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findReferences) + // must be a write only access with an initializer + if (this.pattern.writeAccess && !this.pattern.readAccess && node.getInitializer() != null) + if (matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray())) + referencesLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + + int declarationsLevel = IMPOSSIBLE_MATCH; + if (this.pattern.findDeclarations) + if (matchesName(this.pattern.name, node.getName().getIdentifier().toCharArray())) + if (node.getStartPosition() == getLocalVariable().declarationSourceStart) + declarationsLevel = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + + return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match +} private LocalVariable getLocalVariable() { return ((LocalVariablePattern) this.pattern).localVariable; } @@ -88,6 +115,17 @@ protected void matchReportReference(ASTNode reference, IJavaElement element, Bin } } @Override +public int match(Name node, MatchingNodeSet nodeSet) { + if (node.getLocationInParent() instanceof ChildPropertyDescriptor descriptor + && (descriptor.getChildType() == Expression.class // local variable refs are either expressions as children + || descriptor == QualifiedName.QUALIFIER_PROPERTY) // or dereferenced names + && node instanceof SimpleName simple // local variables cannot be qualified + && getLocalVariable().getElementName().equals(simple.getIdentifier())) { + return POSSIBLE_MATCH; + } + return IMPOSSIBLE_MATCH; +} +@Override protected int matchContainer() { return METHOD_CONTAINER; } @@ -138,6 +176,16 @@ public int resolveLevel(Binding binding) { return matchLocalVariable((LocalVariableBinding) binding, true); } +@Override +public int resolveLevel(IBinding binding) { + if (!(binding instanceof IVariableBinding)) { + return IMPOSSIBLE_MATCH; + } + if (Objects.equals(binding.getJavaElement(), getLocalVariable())) { + return ACCURATE_MATCH; + } + return INACCURATE_MATCH; +} private int matchField(Binding binding, boolean matchName) { if (binding == null) return INACCURATE_MATCH; if(binding instanceof FieldBinding) { diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java index e86877530f1..f14f870e0b5 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java @@ -17,6 +17,7 @@ package org.eclipse.jdt.internal.core.search.matching; import static org.eclipse.jdt.internal.core.JavaModelManager.trace; +import static org.eclipse.jdt.internal.core.search.matching.DOMASTNodeUtils.insideDocComment; import java.io.File; import java.io.IOException; @@ -29,23 +30,107 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.zip.ZipFile; + import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.jdt.core.*; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotatable; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaModelStatusConstants; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.IOrdinaryClassFile; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.SourceRange; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.compiler.InvalidInputException; -import org.eclipse.jdt.core.search.*; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTRequestor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; +import org.eclipse.jdt.core.dom.CreationReference; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IPackageBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.VariableDeclaration; +import org.eclipse.jdt.core.search.FieldDeclarationMatch; +import org.eclipse.jdt.core.search.FieldReferenceMatch; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.LocalVariableDeclarationMatch; +import org.eclipse.jdt.core.search.LocalVariableReferenceMatch; +import org.eclipse.jdt.core.search.MethodDeclarationMatch; +import org.eclipse.jdt.core.search.MethodReferenceMatch; +import org.eclipse.jdt.core.search.ModuleDeclarationMatch; +import org.eclipse.jdt.core.search.ModuleReferenceMatch; +import org.eclipse.jdt.core.search.PackageDeclarationMatch; +import org.eclipse.jdt.core.search.PackageReferenceMatch; +import org.eclipse.jdt.core.search.ReferenceMatch; +import org.eclipse.jdt.core.search.SearchDocument; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jdt.core.search.TypeDeclarationMatch; +import org.eclipse.jdt.core.search.TypeParameterDeclarationMatch; +import org.eclipse.jdt.core.search.TypeParameterReferenceMatch; +import org.eclipse.jdt.core.search.TypeReferenceMatch; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ModuleReference; +import org.eclipse.jdt.internal.compiler.ast.PackageVisibilityStatement; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ProvidesStatement; +import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.RequiresStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.UsesStatement; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; @@ -58,7 +143,24 @@ import org.eclipse.jdt.internal.compiler.env.ISourceType; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TagBits; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; @@ -72,7 +174,29 @@ import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; -import org.eclipse.jdt.internal.core.*; +import org.eclipse.jdt.internal.core.AbstractModule; +import org.eclipse.jdt.internal.core.BinaryMember; +import org.eclipse.jdt.internal.core.BinaryMethod; +import org.eclipse.jdt.internal.core.BinaryType; +import org.eclipse.jdt.internal.core.ClassFile; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.LambdaFactory; +import org.eclipse.jdt.internal.core.LocalVariable; +import org.eclipse.jdt.internal.core.ModularClassFile; +import org.eclipse.jdt.internal.core.NameLookup; +import org.eclipse.jdt.internal.core.NamedMember; +import org.eclipse.jdt.internal.core.Openable; +import org.eclipse.jdt.internal.core.PackageFragment; +import org.eclipse.jdt.internal.core.PackageFragmentRoot; +import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.core.SourceMapper; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.core.SourceType; +import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver; import org.eclipse.jdt.internal.core.index.Index; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; @@ -89,6 +213,8 @@ public class MatchLocator implements ITypeRequestor { +private static final boolean DOM_BASED_MATCH = Boolean.getBoolean(MatchLocator.class.getSimpleName() + ".DOM_BASED_MATCH"); //$NON-NLS-1$ + public static final int MAX_AT_ONCE; static { long maxMemory = Runtime.getRuntime().maxMemory(); @@ -1007,6 +1133,57 @@ public MethodBinding getMethodBinding(MethodPattern methodPattern) { } return null; } +public IMethodBinding getDOMASTMethodBinding(MethodPattern methodPattern) { + return null; //TODO +// if (methodPattern.declaringType != null && this.parsedUnits != null) { +// Optional type = this.parsedUnits.stream().map(unit -> unit.findDeclaringNode(declaringType.getSignature())) +// .filter(AbstractTypeDeclaration.class) +// .map +// .findFirst(); +// } +// this.unitScopeTypeBinding = null; +// MethodBinding methodBinding = getMethodBinding0(methodPattern); +// if (methodBinding != null) +// return methodBinding; // known to be valid. +// // special handling for methods of anonymous/local types. Since these cannot be looked up in the environment the usual way ... +// if (methodPattern.focus instanceof SourceMethod sourceMethod) { +// MethodBinding binding = getClosestMatchMethodBinding(methodPattern); +// if (binding != null) { +// return binding; +// } +// char[] typeName = PatternLocator.qualifiedPattern(methodPattern.declaringSimpleName, methodPattern.declaringQualification); +// if (typeName != null) { +// IType type = methodPattern.declaringType; +// IType enclosingType = type.getDeclaringType(); +// while (enclosingType != null) { +// type = enclosingType; +// enclosingType = type.getDeclaringType(); +// } +// typeName = type.getFullyQualifiedName().toCharArray(); +// TypeBinding declaringTypeBinding = getType(typeName, typeName); +// if (declaringTypeBinding instanceof SourceTypeBinding) { +// SourceTypeBinding sourceTypeBinding = ((SourceTypeBinding) declaringTypeBinding); +// ClassScope skope = sourceTypeBinding.scope; +// if (skope != null) { +// CompilationUnitDeclaration unit = skope.referenceCompilationUnit(); +// if (unit != null) { +// AbstractMethodDeclaration amd = new ASTNodeFinder(unit).findMethod((IMethod) methodPattern.focus); +// if (amd != null && amd.binding != null && amd.binding.isValidBinding()) { +// this.bindings.put(methodPattern, amd.binding); +// return amd.binding; +// } +// } +// } +// } +// } +// } else if (methodPattern.focus instanceof BinaryMethod && +// methodPattern.declaringType instanceof BinaryType && +// this.unitScopeTypeBinding instanceof ProblemReferenceBinding) {//Get binding from unit scope for non-visible member of binary type +// return getClosestMatchMethodBinding(methodPattern); +// } +// return null; +} + private MethodBinding getClosestMatchMethodBinding(MethodPattern methodPattern) { TypeBinding typeBinding = this.unitScopeTypeBinding; @@ -1263,6 +1440,7 @@ public void initialize(JavaProject project, int possibleMatchSize) throws JavaMo this.lookupEnvironment.addResolutionListener(this.patternLocator); } + private boolean skipMatch(JavaProject javaProject, PossibleMatch possibleMatch) { if (this.options.sourceLevel >= ClassFileConstants.JDK9) { char[] pModuleName = possibleMatch.getModuleName(); @@ -1271,7 +1449,115 @@ private boolean skipMatch(JavaProject javaProject, PossibleMatch possibleMatch) } return false; } +protected void locateMatchesWithASTParser(JavaProject javaProject, PossibleMatch[] possibleMatches, int start, int length) throws CoreException { + Map map = javaProject.getOptions(true); + map.put(CompilerOptions.OPTION_TaskTags, org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING); + this.options = new CompilerOptions(map); + + var units = Arrays.stream(possibleMatches, start, start + length) + .filter(match -> !skipMatch(javaProject, match)) + .map(match -> match.openable) + .filter(org.eclipse.jdt.core.ICompilationUnit.class::isInstance) + .map(org.eclipse.jdt.core.ICompilationUnit.class::cast) + .toArray(org.eclipse.jdt.core.ICompilationUnit[]::new); + if (units.length == 0) { + return; + } + + ASTParser astParser = ASTParser.newParser(AST.getJLSLatest()); + astParser.setCompilerOptions(javaProject.getOptions(true)); + astParser.setProject(javaProject); + astParser.setResolveBindings(true); + Map asts = new HashMap<>(); + astParser.createASTs(units, new String[0], new ASTRequestor() { + @Override + public void acceptAST(org.eclipse.jdt.core.ICompilationUnit source, org.eclipse.jdt.core.dom.CompilationUnit ast) { + Arrays.stream(possibleMatches, start, start + length) + .filter(match -> match.openable.equals(source)) + .findAny() + .ifPresent(match -> asts.put(match, ast)); + } + }, this.progressMonitor); // todo, use a subprogressmonitor or slice it + asts.forEach((possibleMatch, ast) -> ast.accept(new PatternLocatorVisitor(this.patternLocator, possibleMatch.nodeSet))); + asts.keySet().forEach(possibleMatch -> { + this.currentPossibleMatch = possibleMatch; + possibleMatch.nodeSet.trustedASTNodeLevels.forEach((node, level) -> { + SearchMatch match = toMatch(node, level, possibleMatch.resource); + try { + this.report(match); + } catch (CoreException ex) { + ILog.get().error(ex.getMessage(), ex); + } + }); + }); +} +private SearchMatch toMatch(org.eclipse.jdt.core.dom.ASTNode node, int accuracy, IResource resource) { + if (node instanceof MethodDeclaration || node instanceof AbstractTypeDeclaration || node instanceof VariableDeclaration) { + IJavaElement javaElement = DOMASTNodeUtils.getDeclaringJavaElement(node); + if (javaElement != null) { + ISourceRange range = new SourceRange(node.getStartPosition(), node.getLength()); + if (javaElement instanceof NamedMember named) { + try { + range = named.getNameRange(); + } catch (JavaModelException ex) { + ILog.get().error(ex.getMessage(), ex); + } + } + return newDeclarationMatch(javaElement, null, accuracy, range.getOffset(), range.getLength()); + } + } + if (node instanceof MethodInvocation method) { + return new MethodReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node.getParent()), accuracy, method.getName().getStartPosition(), method.getStartPosition() + method.getLength() - method.getName().getStartPosition(), false, method.resolveMethodBinding().isSynthetic(), false, insideDocComment(node), getParticipant(), resource); + } + if (node instanceof SuperMethodInvocation method) { + return new MethodReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node.getParent()), accuracy, method.getName().getStartPosition(), method.getStartPosition() + method.getLength() - method.getName().getStartPosition(), false, method.resolveMethodBinding().isSynthetic(), true, insideDocComment(node), getParticipant(), resource); + } + if (node instanceof ClassInstanceCreation newInstance) { + return new MethodReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node.getParent().getParent()) /* we don't want the variable decl */, accuracy, newInstance.getStartPosition(), newInstance.getLength(), true, newInstance.resolveConstructorBinding().isSynthetic(), false, insideDocComment(node), getParticipant(), resource); + } + if (node instanceof SuperConstructorInvocation newInstance) { + return new MethodReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node), accuracy, newInstance.getStartPosition(), newInstance.getLength(), true, newInstance.resolveConstructorBinding().isSynthetic(), false, insideDocComment(node), getParticipant(), resource); + } + if (node instanceof CreationReference constructorRef) { + return new MethodReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node), accuracy, constructorRef.getStartPosition(), constructorRef.getLength(), true, constructorRef.resolveMethodBinding().isSynthetic(), true, insideDocComment(node), getParticipant(), resource); + } + if (node instanceof EnumConstantDeclaration enumConstantDeclaration) { + return new FieldDeclarationMatch(DOMASTNodeUtils.getDeclaringJavaElement(node), accuracy, enumConstantDeclaration.getStartPosition(), enumConstantDeclaration.getLength(), getParticipant(), resource); + } + if (node instanceof Type) { + IJavaElement element = DOMASTNodeUtils.getEnclosingJavaElement(node); + if (element instanceof LocalVariable) { + element = element.getParent(); + } + return new TypeReferenceMatch(element, accuracy, node.getStartPosition(), node.getLength(), DOMASTNodeUtils.insideDocComment(node), getParticipant(), resource); + } + if (node instanceof Name name) { + if (name.resolveBinding() instanceof ITypeBinding) { + return new TypeReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node), accuracy, node.getStartPosition(), node.getLength(), insideDocComment(node), getParticipant(), resource); + } + if (name.resolveBinding() instanceof IVariableBinding variable) { + if (variable.isField()) { + return new FieldReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node), accuracy, node.getStartPosition(), node.getLength(), true, true, insideDocComment(node), getParticipant(), resource); + } + return new LocalVariableReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(node), accuracy, node.getStartPosition(), node.getLength(), true, true, insideDocComment(node), getParticipant(), resource); + } + if (name.resolveBinding() instanceof IPackageBinding) { + return new PackageReferenceMatch(DOMASTNodeUtils.getEnclosingJavaElement(name), accuracy, name.getStartPosition(), name.getLength(), insideDocComment(name), getParticipant(), resource); + } + // more...? + } + if (node.getLocationInParent() == SimpleType.NAME_PROPERTY + || node.getLocationInParent() == QualifiedName.NAME_PROPERTY) { + // more...? + return toMatch(node.getParent(), accuracy, resource); + } + return null; +} protected void locateMatches(JavaProject javaProject, PossibleMatch[] possibleMatches, int start, int length) throws CoreException { + if (DOM_BASED_MATCH) { + locateMatchesWithASTParser(javaProject, possibleMatches, start, length); + return; + } initialize(javaProject, length); // create and resolve binding (equivalent to beginCompilation() in Compiler) @@ -1322,6 +1608,7 @@ protected void locateMatches(JavaProject javaProject, PossibleMatch[] possibleMa possibleMatch.cleanUp(); } } + if (mustResolve) this.lookupEnvironment.completeTypeBindings(); @@ -1714,6 +2001,14 @@ public SearchMatch newDeclarationMatch( int offset, int length) { SearchParticipant participant = getParticipant(); + if (element instanceof IMethod method) { + try { + offset = method.getNameRange().getOffset(); + length = method.getNameRange().getLength(); + } catch (JavaModelException e) { + ILog.get().error(e.getMessage(), e); + } + } IResource resource = this.currentPossibleMatch.resource; return newDeclarationMatch(element, binding, accuracy, offset, length, participant, resource); } diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java index 6e4edc31f27..1e77f0c8f9c 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java @@ -14,7 +14,12 @@ package org.eclipse.jdt.internal.core.search.matching; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Set; + import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.compiler.ast.ASTNode; @@ -50,6 +55,8 @@ public class MatchingNodeSet { SimpleSet possibleMatchingNodesSet = new SimpleSet(7); private final HashtableOfLong possibleMatchingNodesKeys = new HashtableOfLong(7); +private Set possibleASTNodes = new LinkedHashSet<>(); +public final Map trustedASTNodeLevels = new LinkedHashMap<>(); public MatchingNodeSet(boolean mustResolvePattern) { super(); @@ -86,6 +93,39 @@ public int addMatch(ASTNode node, int matchLevel) { } return matchLevel; } +public int addMatch(org.eclipse.jdt.core.dom.ASTNode node, int matchLevel) { + int maskedLevel = matchLevel & PatternLocator.MATCH_LEVEL_MASK; + switch (maskedLevel) { + case PatternLocator.INACCURATE_MATCH: + if (matchLevel != maskedLevel) { + addTrustedMatch(node, Integer.valueOf(SearchMatch.A_INACCURATE+(matchLevel & PatternLocator.FLAVORS_MASK))); + } else { + addTrustedMatch(node, POTENTIAL_MATCH); + } + break; + case PatternLocator.POSSIBLE_MATCH: + addPossibleMatch(node); + break; + case PatternLocator.ERASURE_MATCH: + if (matchLevel != maskedLevel) { + addTrustedMatch(node, Integer.valueOf(SearchPattern.R_ERASURE_MATCH+(matchLevel & PatternLocator.FLAVORS_MASK))); + } else { + addTrustedMatch(node, ERASURE_MATCH); + } + break; + case PatternLocator.ACCURATE_MATCH: + if (matchLevel != maskedLevel) { + addTrustedMatch(node, Integer.valueOf(SearchMatch.A_ACCURATE+(matchLevel & PatternLocator.FLAVORS_MASK))); + } else { + addTrustedMatch(node, EXACT_MATCH); + } + break; + } + return matchLevel; +} +public void addPossibleMatch(org.eclipse.jdt.core.dom.ASTNode node) { + this.possibleASTNodes.add(node); +} public void addPossibleMatch(ASTNode node) { // remove existing node at same position from set // (case of recovery that created the same node several time @@ -101,7 +141,9 @@ public void addPossibleMatch(ASTNode node) { } public void addTrustedMatch(ASTNode node, boolean isExact) { addTrustedMatch(node, isExact ? EXACT_MATCH : POTENTIAL_MATCH); - +} +public void addTrustedMatch(org.eclipse.jdt.core.dom.ASTNode node, boolean isExact) { + addTrustedMatch(node, isExact ? EXACT_MATCH : POTENTIAL_MATCH); } void addTrustedMatch(ASTNode node, Integer level) { // remove existing node at same position from set @@ -116,6 +158,9 @@ void addTrustedMatch(ASTNode node, Integer level) { this.matchingNodes.put(node, level); this.matchingNodesKeys.put(key, node); } +void addTrustedMatch(org.eclipse.jdt.core.dom.ASTNode node, Integer level) { + this.trustedASTNodeLevels.put(node, level); +} protected boolean hasPossibleNodes(int start, int end) { Object[] nodes = this.possibleMatchingNodesSet.values; for (Object n : nodes) { @@ -129,7 +174,9 @@ protected boolean hasPossibleNodes(int start, int end) { if (node != null && start <= node.sourceStart && node.sourceEnd <= end) return true; } - return false; + return + this.possibleASTNodes.stream().anyMatch(node -> start <= node.getStartPosition() && node.getStartPosition() + node.getLength() <= end) || + this.trustedASTNodeLevels.keySet().stream().anyMatch(node -> start <= node.getStartPosition() && node.getStartPosition() + node.getLength() <= end); } /** * Returns the matching nodes that are in the given range in the source order. diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MethodLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MethodLocator.java index f6bf05e08b1..2e486873c11 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MethodLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MethodLocator.java @@ -20,7 +20,11 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.Flags; @@ -30,13 +34,45 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; 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.SearchPattern; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NameReference; +import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.env.IBinaryType; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.core.BinaryMethod; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; @@ -116,6 +152,29 @@ private MethodBinding getMethodBinding(ReferenceBinding type, char[] methodName, } return null; } +private IMethodBinding getDOMASTMethodBinding(ITypeBinding type, String methodName, ITypeBinding[] argumentTypes) { + return Stream.of(type.getDeclaredMethods()) + .filter(method -> Objects.equals(method.getName(), methodName)) + .filter(method -> compatibleByErasure(method.getParameterTypes(), argumentTypes)) + .findAny() + .orElse(null); +} +// can be replaced with `Arrays.equals(method.getParameterTypes(), argumentTypes, Comparator.comparing(ITypeBinding::getErasure))` +// but JDT bugs +private static boolean compatibleByErasure(ITypeBinding[] one, ITypeBinding[] other) { + if (Objects.equals(one, other)) { + return true; + } + if (one == null || other == null || one.length != other.length) { + return false; + } + for (int i = 0; i < one.length; i++) { + if (!Objects.equals(one[i].getErasure(), other[i].getErasure())) { + return false; + } + } + return true; +} @Override public void initializePolymorphicSearch(MatchLocator locator) { @@ -254,6 +313,47 @@ public int match(MethodDeclaration node, MatchingNodeSet nodeSet) { return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } @Override +public int match(org.eclipse.jdt.core.dom.MethodDeclaration node, MatchingNodeSet nodeSet) { + if (!this.pattern.findDeclarations) return IMPOSSIBLE_MATCH; + + // Verify method name + if (!matchesName(this.pattern.selector, node.getName().getIdentifier().toCharArray())) return IMPOSSIBLE_MATCH; + + // Verify parameters types + boolean resolve = this.pattern.mustResolve; + if (this.pattern.parameterSimpleNames != null) { + int length = this.pattern.parameterSimpleNames.length; + List args = node.parameters(); + int argsLength = args == null ? 0 : args.size(); + if (length != argsLength) return IMPOSSIBLE_MATCH; + for (int i = 0; i < argsLength; i++) { + var arg = args.get(i); + if (!matchesTypeReference(this.pattern.parameterSimpleNames[i], arg.getType(), arg.isVarargs())) { + // Do not return as impossible when source level is at least 1.5 + if (this.mayBeGeneric) { + if (!this.pattern.mustResolve) { + // Set resolution flag on node set in case of types was inferred in parameterized types from generic ones... + // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763) + nodeSet.mustResolve = true; + resolve = true; + } + // this.methodDeclarationsWithInvalidParam.put(node, null); + } else { + return IMPOSSIBLE_MATCH; + } + } + } + } + + // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match) + if (this.pattern.hasMethodArguments()) { + if (node.typeParameters() == null || node.typeParameters().size() != this.pattern.methodArguments.length) return IMPOSSIBLE_MATCH; + } + + // Method declaration may match pattern + return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH : ACCURATE_MATCH); +} +@Override public int match(MemberValuePair node, MatchingNodeSet nodeSet) { if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; @@ -275,6 +375,28 @@ public int match(MessageSend node, MatchingNodeSet nodeSet) { return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); } +private int matchReference(SimpleName name, List args, MatchingNodeSet nodeSet) { + if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH; + + if (!matchesName(this.pattern.selector, name.getIdentifier().toCharArray())) return IMPOSSIBLE_MATCH; + if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || DOMASTNodeUtils.insideDocComment(name))) { + int length = this.pattern.parameterSimpleNames.length; + int argsLength = args == null ? 0 : args.size(); + if (length != argsLength) return IMPOSSIBLE_MATCH; + } + return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; +} +@Override +public int match(MethodInvocation node, MatchingNodeSet nodeSet) { + int level = matchReference(node.getName(), node.arguments(), nodeSet); + return level != IMPOSSIBLE_MATCH ? nodeSet.addMatch(node, level) : level; +} +@Override +public int match(org.eclipse.jdt.core.dom.Expression expression, MatchingNodeSet nodeSet) { + int level = expression instanceof SuperMethodInvocation node ? matchReference(node.getName(), node.arguments(), nodeSet) : + IMPOSSIBLE_MATCH; + return level != IMPOSSIBLE_MATCH ? nodeSet.addMatch(expression, level) : level; +} @Override public int match(ReferenceExpression node, MatchingNodeSet nodeSet) { @@ -412,6 +534,94 @@ protected int matchMethod(MethodBinding method, boolean skipImpossibleArg) { return level; } +protected int matchMethod(IMethodBinding method, boolean skipImpossibleArg) { + if (!matchesName(this.pattern.selector, method.getName().toCharArray())) return IMPOSSIBLE_MATCH; + + int level = ACCURATE_MATCH; + // look at return type only if declaring type is not specified + if (this.pattern.declaringSimpleName == null) { + // TODO (frederic) use this call to refine accuracy on return type + // int newLevel = resolveLevelForType(this.pattern.returnSimpleName, this.pattern.returnQualification, this.pattern.returnTypeArguments, 0, method.returnType); + int newLevel = resolveLevelForType(this.pattern.returnSimpleName, this.pattern.returnQualification, method.getReturnType()); + if (level > newLevel) { + if (newLevel == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH; + level = newLevel; // can only be downgraded + } + } + + // parameter types + int parameterCount = this.pattern.parameterSimpleNames == null ? -1 : this.pattern.parameterSimpleNames.length; + if (parameterCount > -1) { + // global verification + if (method.getParameterTypes() == null) return INACCURATE_MATCH; + if (parameterCount != method.getParameterTypes().length) return IMPOSSIBLE_MATCH; + if (method.isRecovered()) { + // return inaccurate match for ambiguous call (bug 80890) + return INACCURATE_MATCH; + } + boolean foundTypeVariable = false; + IMethodBinding focusMethodBinding = null; + boolean checkedFocus = false; + boolean isBinary = this.pattern!= null && this.pattern.focus instanceof BinaryMethod; + // verify each parameter + for (int i = 0; i < parameterCount; i++) { + ITypeBinding argType = method.getParameterTypes()[i]; + int newLevel = IMPOSSIBLE_MATCH; + boolean foundLevel = false; + if (argType.isMember() || this.pattern.parameterQualifications[i] != null) { + if (!checkedFocus) { + focusMethodBinding = this.matchLocator.getDOMASTMethodBinding(this.pattern); + checkedFocus = true; + } + if (focusMethodBinding != null) {// textual comparison insufficient + ITypeBinding[] parameters = focusMethodBinding.getParameterTypes(); + if (parameters.length >= parameterCount) { + // TODO +// newLevel = (isBinary ? argType.getErasure().isEqualTo((parameters[i].getErasureCompatibleType(null)())) :argType.isEquivalentTo((parameters[i]))) ? +// ACCURATE_MATCH : IMPOSSIBLE_MATCH; + foundLevel = true; + } + } + } else { + // TODO (frederic) use this call to refine accuracy on parameter types +// newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, argType); + newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], argType); + } + if (level > newLevel) { + if (newLevel == IMPOSSIBLE_MATCH) { + if (skipImpossibleArg) { + // Do not consider match as impossible while finding declarations and source level >= 1.5 + // (see bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=79990, 96761, 96763) + if (!foundLevel) { + newLevel = level; + } + } else if (argType.isTypeVariable()) { + newLevel = level; + foundTypeVariable = true; + } else { + return IMPOSSIBLE_MATCH; + } + } + level = newLevel; // can only be downgraded + } + } + if (foundTypeVariable) { + if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isPrivate(method.getModifiers())) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=123836, No point in textually comparing type variables, captures etc with concrete types. + if (!checkedFocus) + focusMethodBinding = this.matchLocator.getDOMASTMethodBinding(this.pattern); + if (focusMethodBinding != null + /* && matchOverriddenMethod(focusMethodBinding.getDeclaringClass(), focusMethodBinding, method)*/ + && (focusMethodBinding.overrides(method) || method.overrides(focusMethodBinding))) { + return ACCURATE_MATCH; + } + } + return IMPOSSIBLE_MATCH; + } + } + return level; +} + // This works for only methods of parameterized types. private boolean matchOverriddenMethod(ReferenceBinding type, MethodBinding method, MethodBinding matchMethod) { if (type == null || this.pattern.selector == null) return false; @@ -461,6 +671,7 @@ private boolean matchOverriddenMethod(ReferenceBinding type, MethodBinding metho } return false; } + @Override protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException { matchReportReference(reference, element, null, null, elementBinding, accuracy, locator); @@ -775,6 +986,34 @@ public int resolveLevel(Binding binding) { : resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, method.declaringClass); return (methodLevel & MATCH_LEVEL_MASK) > (declaringLevel & MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match } +@Override +public int resolveLevel(IBinding binding) { + if (binding instanceof IMethodBinding method) { + boolean skipVerif = this.pattern.findDeclarations && this.mayBeGeneric; + int methodLevel = matchMethod(method, skipVerif); + if (methodLevel == IMPOSSIBLE_MATCH) { + if (method != method.getMethodDeclaration()) methodLevel = matchMethod(method.getMethodDeclaration(), skipVerif); + if (methodLevel == IMPOSSIBLE_MATCH) { + return IMPOSSIBLE_MATCH; + } else { + method = method.getMethodDeclaration(); + } + } + + // declaring type + if (this.pattern.declaringSimpleName == null && this.pattern.declaringQualification == null) return methodLevel; // since any declaring class will do + + boolean subType = ((method.getModifiers() & Modifier.STATIC) == 0) && ((method.getModifiers() & Modifier.PRIVATE) == 0); + if (subType && this.pattern.declaringQualification != null && method.getDeclaringClass() != null && method.getDeclaringClass().getPackage() != null) { + subType = CharOperation.compareWith(this.pattern.declaringQualification, method.getDeclaringClass().getPackage().getName().toCharArray()) == 0; + } + int declaringLevel = subType + ? resolveLevelAsSubtype(this.pattern.declaringSimpleName, this.pattern.declaringQualification, method.getDeclaringClass(), method.getName(), null, method.getDeclaringClass().getPackage().getName(), (method.getModifiers() & Modifier.DEFAULT) != 0) + : resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, method.getDeclaringClass()); + return (methodLevel & MATCH_LEVEL_MASK) > (declaringLevel & MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match + } + return INACCURATE_MATCH; +} protected int resolveLevel(MessageSend messageSend) { MethodBinding method = messageSend.binding; if (method == null) { @@ -923,6 +1162,57 @@ protected int resolveLevelAsSubtype(char[] simplePattern, char[] qualifiedPatter } return IMPOSSIBLE_MATCH; } +protected int resolveLevelAsSubtype(char[] simplePattern, char[] qualifiedPattern, ITypeBinding type, String methodName, ITypeBinding[] argumentTypes, String packageName, boolean isDefault) { + if (type == null) return INACCURATE_MATCH; + + int level = resolveLevelForType(simplePattern, qualifiedPattern, type); + if (level != IMPOSSIBLE_MATCH) { + if (isDefault && !Objects.equals(packageName, type.getPackage().getName())) { + return IMPOSSIBLE_MATCH; + } + IMethodBinding method = argumentTypes == null ? null : getDOMASTMethodBinding(type, methodName, argumentTypes); + if (((method != null && !Modifier.isAbstract(method.getModifiers()) || !Modifier.isAbstract(type.getModifiers()))) && !type.isInterface()) { // if concrete, then method is overridden + level |= OVERRIDDEN_METHOD_FLAVOR; + } + return level; + } + + // matches superclass + if (!type.isInterface() && !type.getQualifiedName().equals(Object.class.getName())) { + level = resolveLevelAsSubtype(simplePattern, qualifiedPattern, type.getSuperclass(), methodName, argumentTypes, packageName, isDefault); + if (level != IMPOSSIBLE_MATCH) { + if (argumentTypes != null) { + // need to verify if method may be overridden + IMethodBinding method = getDOMASTMethodBinding(type, methodName, argumentTypes); + if (method != null) { // one method match in hierarchy + if ((level & OVERRIDDEN_METHOD_FLAVOR) != 0) { + // this method is already overridden on a super class, current match is impossible + return IMPOSSIBLE_MATCH; + } + if (!Modifier.isAbstract(method.getModifiers()) && !type.isInterface()) { + // store the fact that the method is overridden + level |= OVERRIDDEN_METHOD_FLAVOR; + } + } + } + return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level + } + } + + // matches interfaces + ITypeBinding[] interfaces = type.getInterfaces(); + if (interfaces == null) return INACCURATE_MATCH; + for (ITypeBinding ref : interfaces) { + level = resolveLevelAsSubtype(simplePattern, qualifiedPattern, ref, methodName, null, packageName, isDefault); + if (level != IMPOSSIBLE_MATCH) { + if (!Modifier.isAbstract(type.getModifiers()) && !type.isInterface()) { // if concrete class, then method is overridden + level |= OVERRIDDEN_METHOD_FLAVOR; + } + return level | SUB_INVOCATION_FLAVOR; // add flavor to returned level + } + } + return IMPOSSIBLE_MATCH; +} /* * Return whether the given type binding or one of its possible super interfaces diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java index 5a23d77d8e1..2a6b00982ae 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java @@ -13,12 +13,20 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; +import java.util.Arrays; + import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IPackageBinding; +import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.search.PackageReferenceMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.compiler.ast.ASTNode; @@ -30,7 +38,18 @@ import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.Reference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.ImportBinding; +import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.util.Util; public class PackageReferenceLocator extends PatternLocator { @@ -80,11 +99,22 @@ public int match(Annotation node, MatchingNodeSet nodeSet) { return match(node.type, nodeSet); } @Override +public int match(org.eclipse.jdt.core.dom.Annotation node, MatchingNodeSet nodeSet) { + return match(node.getTypeName(), nodeSet); +} +@Override public int match(ASTNode node, MatchingNodeSet nodeSet) { // interested in ImportReference if (!(node instanceof ImportReference)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, matchLevel((ImportReference) node)); } +@Override +public int match(org.eclipse.jdt.core.dom.ASTNode node, MatchingNodeSet nodeSet) { // interested in ImportReference + if (node instanceof ImportDeclaration decl) { + return match(decl.getName(), nodeSet); + } + return IMPOSSIBLE_MATCH; +} //public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) - SKIP IT //public int match(Expression node, MatchingNodeSet nodeSet) - SKIP IT //public int match(FieldDeclaration node, MatchingNodeSet nodeSet) - SKIP IT @@ -96,6 +126,10 @@ public int match(Reference node, MatchingNodeSet nodeSet) { // interested in Qua return nodeSet.addMatch(node, matchLevelForTokens(((QualifiedNameReference) node).tokens)); } +@Override +public int match(Name node, MatchingNodeSet nodeSet) { // interested in QualifiedNameReference + return nodeSet.addMatch(node, matchLevelForTokens(Arrays.stream(node.getFullyQualifiedName().split("\\.")).map(String::toCharArray).toArray(char[][]::new))); //$NON-NLS-1$ +} //public int match(TypeDeclaration node, MatchingNodeSet nodeSet) - SKIP IT @Override public int match(TypeReference node, MatchingNodeSet nodeSet) { // interested in QualifiedTypeReference only @@ -106,6 +140,17 @@ public int match(TypeReference node, MatchingNodeSet nodeSet) { // interested in if (!(node instanceof QualifiedTypeReference)) return IMPOSSIBLE_MATCH; return nodeSet.addMatch(node, matchLevelForTokens(((QualifiedTypeReference) node).tokens)); } +@Override +public int match(Type node, MatchingNodeSet nodeSet) { // interested in QualifiedTypeReference only + return node instanceof SimpleType type ? match(type.getName(), nodeSet) : IMPOSSIBLE_MATCH; +// if (node instanceof JavadocSingleTypeReference) { +// char[][] tokens = new char[][] { ((JavadocSingleTypeReference) node).token }; +// return nodeSet.addMatch(node, matchLevelForTokens(tokens)); +// } +// if (!(node instanceof QualifiedTypeReference)) return IMPOSSIBLE_MATCH; +// return nodeSet.addMatch(node, matchLevelForTokens(((QualifiedTypeReference) node).tokens)); +} + @Override protected int matchLevel(ImportReference importRef) { @@ -341,6 +386,11 @@ public int resolveLevel(Binding binding) { } return IMPOSSIBLE_MATCH; } +@Override +public int resolveLevel(IBinding binding) { + return binding instanceof IPackageBinding ? + ACCURATE_MATCH : IMPOSSIBLE_MATCH; +} protected int resolveLevel(QualifiedNameReference qNameRef) { TypeBinding typeBinding = null; switch (qNameRef.bits & ASTNode.RestrictiveFlagMASK) { diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java index ac41d94a689..9e0ecd13340 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java @@ -15,14 +15,59 @@ import java.util.regex.Pattern; + import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.ArrayType; +import org.eclipse.jdt.core.dom.FieldAccess; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.PrimitiveType; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; -import org.eclipse.jdt.internal.compiler.ast.*; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ModuleReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Reference; +import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.CaptureBinding; +import org.eclipse.jdt.internal.compiler.lookup.IQualifiedTypeResolutionListener; +import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; public abstract class PatternLocator implements IIndexConstants, IQualifiedTypeResolutionListener { @@ -125,6 +170,17 @@ public static char[] qualifiedSourceName(TypeBinding binding) { } return binding != null ? binding.qualifiedSourceName() : null; } +public static String qualifiedSourceName(ITypeBinding binding) { + if (binding == null) { + return null; + } + if (binding.isLocal()) { + return binding.isMember() + ? qualifiedSourceName(binding.getDeclaringClass()) + '.' + binding.getName() + : qualifiedSourceName(binding.getDeclaringClass()) + ".1." + binding.getName(); //$NON-NLS-1$ + } + return binding.getQualifiedName(); +} public PatternLocator(SearchPattern pattern) { int matchRule = pattern.getMatchRule(); @@ -171,6 +227,18 @@ protected char[] getQualifiedSourceName(TypeBinding binding) { } return binding != null ? binding.qualifiedSourceName() : null; } +protected String getQualifiedSourceName(ITypeBinding binding) { + if (binding == null) { + return null; + } + ITypeBinding type = binding.isArray() ? binding.getComponentType() : binding; + if (type.isLocal()) { + return qualifiedSourceName(type.getDeclaringClass()) + ".1." + binding.getName(); //$NON-NLS-1$ + } else if (type.isMember()) { + return qualifiedSourceName(type.getDeclaringClass()) + '.' + binding.getName(); + } + return binding.getName(); +} /* * Get binding of type argument from a class unit scope and its index position. * Cache is lazy initialized and if no binding is found, then store a problem binding @@ -377,6 +445,33 @@ protected boolean matchesTypeReference(char[] pattern, TypeReference type) { return matchesName(pattern, simpleName); } +protected boolean matchesTypeReference(char[] pattern, Type type, boolean isVarargs) { + if (pattern == null) return true; // null is as if it was "*" + if (type == null) return true; // treat as an inexact match + + var name = type instanceof SimpleType simple ? simple.getName() : + type instanceof QualifiedType qualified ? qualified.getName() : + type instanceof ArrayType array ? array.getElementType() : + null; + var simpleName = name instanceof SimpleName simple ? simple.getIdentifier() : + name instanceof QualifiedName qName ? qName.getName().getIdentifier() : + type instanceof PrimitiveType primitive ? primitive.getPrimitiveTypeCode().toString() : + null; + if (simpleName == null) { + return true; + } + int dimensions = type instanceof ArrayType arrayType ? arrayType.dimensions().size() : 0; + if (isVarargs) { + dimensions++; + } + for (int i = 0; i < dimensions; i++) { + simpleName += "[]"; //$NON-NLS-1$ + } + return matchesName(pattern, simpleName.toCharArray()); +} +protected boolean matchesTypeReference(char[] pattern, Type type) { + return matchesTypeReference(pattern, type, false); +} /** * Returns the match level for the given importRef. */ @@ -794,7 +889,53 @@ protected int resolveLevelForType(char[] simpleNamePattern, char[] qualification } return IMPOSSIBLE_MATCH; } - +protected int resolveLevelForType(char[] simpleNamePattern, char[] qualificationPattern, ITypeBinding binding) { +// return resolveLevelForType(qualifiedPattern(simpleNamePattern, qualificationPattern), type); + char[] qualifiedPattern = getQualifiedPattern(simpleNamePattern, qualificationPattern); + int level = resolveLevelForType(qualifiedPattern, binding); + if (level == ACCURATE_MATCH || binding == null) return level; + ITypeBinding type = binding.isArray() ? binding.getComponentType() : binding; + char[] sourceName = null; + if (type.isMember() || type.isLocal()) { + if (qualificationPattern != null) { + sourceName = getQualifiedSourceName(binding).toCharArray(); + } else { + sourceName = binding.getQualifiedName().toCharArray(); + } + } else if (qualificationPattern == null) { + sourceName = getQualifiedSourceName(binding).toCharArray(); + } + if (sourceName == null) return IMPOSSIBLE_MATCH; + switch (this.matchMode) { + case SearchPattern.R_PREFIX_MATCH: + if (CharOperation.prefixEquals(qualifiedPattern, sourceName, this.isCaseSensitive)) { + return ACCURATE_MATCH; + } + break; + case SearchPattern.R_CAMELCASE_MATCH: + if ((qualifiedPattern.length>0 && sourceName.length>0 && qualifiedPattern[0] == sourceName[0])) { + if (CharOperation.camelCaseMatch(qualifiedPattern, sourceName, false)) { + return ACCURATE_MATCH; + } + if (!this.isCaseSensitive && CharOperation.prefixEquals(qualifiedPattern, sourceName, false)) { + return ACCURATE_MATCH; + } + } + break; + case SearchPattern.R_CAMELCASE_SAME_PART_COUNT_MATCH: + if ((qualifiedPattern.length>0 && sourceName.length>0 && qualifiedPattern[0] == sourceName[0])) { + if (CharOperation.camelCaseMatch(qualifiedPattern, sourceName, true)) { + return ACCURATE_MATCH; + } + } + break; + default: + if (CharOperation.match(qualifiedPattern, sourceName, this.isCaseSensitive)) { + return ACCURATE_MATCH; + } + } + return IMPOSSIBLE_MATCH; +} /** * Returns whether the given type binding matches the given qualified pattern. * Returns ACCURATE_MATCH if it does. @@ -830,6 +971,30 @@ protected int resolveLevelForType(char[] qualifiedPattern, TypeBinding type) { ? ACCURATE_MATCH : IMPOSSIBLE_MATCH; } +protected int resolveLevelForType(char[] qualifiedPattern, ITypeBinding type) { + if (qualifiedPattern == null) return ACCURATE_MATCH; + if (type == null) return INACCURATE_MATCH; + + // Type variable cannot be specified through pattern => this kind of binding cannot match it (see bug 79803) + if (type.isTypeVariable()) return IMPOSSIBLE_MATCH; + + if (type instanceof IntersectionTypeBinding18) { + int result = IMPOSSIBLE_MATCH, prev = IMPOSSIBLE_MATCH; + IntersectionTypeBinding18 i18 = (IntersectionTypeBinding18) type; + for (ReferenceBinding ref : i18.intersectingTypes) { + result = resolveLevelForType(qualifiedPattern, ref); + if (result == ACCURATE_MATCH) return result; + if (result == IMPOSSIBLE_MATCH) continue; + if (prev == IMPOSSIBLE_MATCH) prev = result; + } + return prev; + } + // NOTE: if case insensitive search then qualifiedPattern is assumed to be lowercase + + return CharOperation.match(qualifiedPattern, type.getQualifiedName().toCharArray(), this.isCaseSensitive) + ? ACCURATE_MATCH + : IMPOSSIBLE_MATCH; +} /* (non-Javadoc) * Resolve level for type with a given binding with all pattern information. */ @@ -1012,4 +1177,79 @@ public String toString(){ public void recordResolution(QualifiedTypeReference typeReference, TypeBinding resolution) { // noop by default } + +// AST DOM Variants + +public int match(org.eclipse.jdt.core.dom.Annotation node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +/** + * Check if the given ast node syntactically matches this pattern. + * If it does, add it to the match set. + * Returns the match level. + */ +public int match(org.eclipse.jdt.core.dom.ASTNode node, MatchingNodeSet nodeSet) { // needed for some generic nodes + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.Expression node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.FieldDeclaration node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.LambdaExpression node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(VariableDeclaration node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.MethodDeclaration node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.MemberValuePair node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(MethodInvocation node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +protected int match(org.eclipse.jdt.core.dom.ModuleDeclaration node, MatchingNodeSet nodeSet) { + return IMPOSSIBLE_MATCH; +} +public int match(Name node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(FieldAccess node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(AbstractTypeDeclaration node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(org.eclipse.jdt.core.dom.TypeParameter node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int match(Type node, MatchingNodeSet nodeSet) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} +public int resolveLevel(IBinding binding) { + // each subtype should override if needed + return IMPOSSIBLE_MATCH; +} } diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocatorVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocatorVisitor.java new file mode 100644 index 00000000000..b3d4900ebb1 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocatorVisitor.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.internal.core.search.matching; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; +import org.eclipse.jdt.core.dom.CreationReference; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.ExpressionMethodReference; +import org.eclipse.jdt.core.dom.IntersectionType; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.NameQualifiedType; +import org.eclipse.jdt.core.dom.ParameterizedType; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.dom.RecordDeclaration; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.SuperMethodReference; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.UnionType; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; + +/** + * Visits an AST to feel the possible match with nodes + */ +class PatternLocatorVisitor extends ASTVisitor { + + private final PatternLocator patternLocator; + private final MatchingNodeSet nodeSet; + + public PatternLocatorVisitor(PatternLocator patternLocator, MatchingNodeSet nodeSet) { + super(true); + this.patternLocator = patternLocator; + this.nodeSet = nodeSet; + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + + @Override + public boolean visit(MethodDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(MethodInvocation node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveMethodBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(ExpressionMethodReference node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveMethodBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SuperMethodReference node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveMethodBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SuperMethodInvocation node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveMethodBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + + private boolean visitAbstractTypeDeclaration(AbstractTypeDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(EnumDeclaration node) { + return visitAbstractTypeDeclaration(node); + } + @Override + public boolean visit(TypeDeclaration node) { + return visitAbstractTypeDeclaration(node); + } + @Override + public boolean visit(RecordDeclaration node) { + return visitAbstractTypeDeclaration(node); + } + @Override + public boolean visit(AnonymousClassDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + + private boolean visitType(Type node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SimpleType type) { + visitType(type); + return false; // No need to visit single name child + } + @Override + public boolean visit(QualifiedType type) { + return visitType(type); + } + @Override + public boolean visit(NameQualifiedType type) { + return visitType(type); + } + @Override + public boolean visit(ParameterizedType node) { + return visitType(node); + } + @Override + public boolean visit(IntersectionType node) { + return visitType(node); + } + @Override + public boolean visit(UnionType node) { + return visitType(node); + } + @Override + public boolean visit(ClassInstanceCreation node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveConstructorBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(CreationReference node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveMethodBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SuperConstructorInvocation node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveConstructorBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SimpleName node) { + if (node.getLocationInParent() == QualifiedName.NAME_PROPERTY || + node.getLocationInParent() == VariableDeclarationFragment.NAME_PROPERTY || + node.getLocationInParent() == SingleVariableDeclaration.NAME_PROPERTY || + node.getLocationInParent() == TypeDeclaration.NAME_PROPERTY || + node.getLocationInParent() == EnumDeclaration.NAME_PROPERTY || + node.getLocationInParent() == MethodDeclaration.NAME_PROPERTY) { + return false; // skip as parent was most likely already matched + } + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(VariableDeclarationFragment node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(SingleVariableDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(EnumConstantDeclaration node) { + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = Math.max(this.patternLocator.resolveLevel(node.resolveVariable()), this.patternLocator.resolveLevel(node.resolveConstructorBinding())); + } + this.nodeSet.addMatch(node, level); + return true; + } + @Override + public boolean visit(QualifiedName node) { + if (node.getLocationInParent() == SimpleType.NAME_PROPERTY) { + return false; // type was already checked + } + int level = this.patternLocator.match(node, this.nodeSet); + if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.mustResolve || this.patternLocator.mustResolve)) { + level = this.patternLocator.resolveLevel(node.resolveBinding()); + } + this.nodeSet.addMatch(node, level); + return true; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeReferenceLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeReferenceLocator.java index c87ffed1645..50a1d56d5d7 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeReferenceLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeReferenceLocator.java @@ -15,6 +15,13 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; @@ -48,6 +55,13 @@ public int match(LambdaExpression node, MatchingNodeSet nodeSet) { nodeSet.mustResolve = true; return nodeSet.addMatch(node, POSSIBLE_MATCH); } +@Override +public int match(org.eclipse.jdt.core.dom.LambdaExpression node, MatchingNodeSet nodeSet) { + if (this.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_INTERFACES) + return IMPOSSIBLE_MATCH; + nodeSet.mustResolve = true; + return nodeSet.addMatch(node, POSSIBLE_MATCH); +} //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT //public int match(MessageSend node, MatchingNodeSet nodeSet) - SKIP IT //public int match(Reference node, MatchingNodeSet nodeSet) - SKIP IT @@ -70,6 +84,28 @@ public int match(TypeReference node, MatchingNodeSet nodeSet) { return IMPOSSIBLE_MATCH; } +@Override +public int match(Type node, MatchingNodeSet nodeSet) { + if (this.flavors != SUPERTYPE_REF_FLAVOR) return IMPOSSIBLE_MATCH; + if (this.pattern.superSimpleName == null) + return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); + + char[] typeRefSimpleName = null; + if (node instanceof SimpleType simple) { + if (simple.getName() instanceof SimpleName name) { + typeRefSimpleName = name.getIdentifier().toCharArray(); + } + if (simple.getName() instanceof QualifiedName name) { + typeRefSimpleName = name.getName().getIdentifier().toCharArray(); + } + } else if (node instanceof QualifiedType qualified) { + typeRefSimpleName = qualified.getName().getIdentifier().toCharArray(); + } + if (matchesName(this.pattern.superSimpleName, typeRefSimpleName)) + return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); + + return IMPOSSIBLE_MATCH; +} @Override protected int matchContainer() { @@ -140,6 +176,29 @@ public int resolveLevel(Binding binding) { return level; } @Override +public int resolveLevel(IBinding binding) { + if (binding == null) return INACCURATE_MATCH; + if (!(binding instanceof ITypeBinding)) return IMPOSSIBLE_MATCH; + + var type = (ITypeBinding) binding; + int level = IMPOSSIBLE_MATCH; + if (this.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_INTERFACES) { + level = resolveLevelForType(this.pattern.superSimpleName, this.pattern.superQualification, type.getSuperclass()); + if (level == ACCURATE_MATCH) return ACCURATE_MATCH; + } + + if (this.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_CLASSES) { + for (ITypeBinding superInterface : type.getInterfaces()) { + int newLevel = resolveLevelForType(this.pattern.superSimpleName, this.pattern.superQualification, superInterface); + if (newLevel > level) { + if (newLevel == ACCURATE_MATCH) return ACCURATE_MATCH; + level = newLevel; + } + } + } + return level; +} +@Override public String toString() { return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ } diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeDeclarationLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeDeclarationLocator.java index d90e11bd918..7b612610487 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeDeclarationLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeDeclarationLocator.java @@ -22,6 +22,10 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IModuleBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchDocument; @@ -63,6 +67,13 @@ public int match(TypeDeclaration node, MatchingNodeSet nodeSet) { return IMPOSSIBLE_MATCH; } +@Override +public int match(AbstractTypeDeclaration node, MatchingNodeSet nodeSet) { + if (this.pattern.simpleName == null || matchesName(this.pattern.simpleName, node.getName().getIdentifier().toCharArray())) + return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH); + + return IMPOSSIBLE_MATCH; +} //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT @Override @@ -115,6 +126,50 @@ public int resolveLevel(Binding binding) { return resolveLevelForType(this.pattern.simpleName, this.pattern.pkg, enclosingTypeName, type); } } +@Override +public int resolveLevel(IBinding binding) { + if (binding == null) return INACCURATE_MATCH; + if (!(binding instanceof ITypeBinding)) return IMPOSSIBLE_MATCH; + + ITypeBinding type = (ITypeBinding) binding; + + switch (this.pattern.typeSuffix) { + case CLASS_SUFFIX: + if (!type.isClass()) return IMPOSSIBLE_MATCH; + break; + case CLASS_AND_INTERFACE_SUFFIX: + if (!(type.isClass() || (type.isInterface() && !type.isAnnotation()))) return IMPOSSIBLE_MATCH; + break; + case CLASS_AND_ENUM_SUFFIX: + if (!(type.isClass() || type.isEnum())) return IMPOSSIBLE_MATCH; + break; + case INTERFACE_SUFFIX: + if (!type.isInterface() || type.isAnnotation()) return IMPOSSIBLE_MATCH; + break; + case INTERFACE_AND_ANNOTATION_SUFFIX: + if (!(type.isInterface() || type.isAnnotation())) return IMPOSSIBLE_MATCH; + break; + case ENUM_SUFFIX: + if (!type.isEnum()) return IMPOSSIBLE_MATCH; + break; + case ANNOTATION_TYPE_SUFFIX: + if (!type.isAnnotation()) return IMPOSSIBLE_MATCH; + break; + case TYPE_SUFFIX : // nothing + } + + if (matchModule(this.pattern, type) == IMPOSSIBLE_MATCH) { + return IMPOSSIBLE_MATCH; + } + // fully qualified name + if (this.pattern instanceof QualifiedTypeDeclarationPattern) { + QualifiedTypeDeclarationPattern qualifiedPattern = (QualifiedTypeDeclarationPattern) this.pattern; + return resolveLevelForType(qualifiedPattern.simpleName, qualifiedPattern.qualification, type); + } else { + char[] enclosingTypeName = this.pattern.enclosingTypeNames == null ? null : CharOperation.concatWith(this.pattern.enclosingTypeNames, '.'); + return resolveLevelForType(this.pattern.simpleName, this.pattern.pkg, enclosingTypeName, type); + } +} /** * Returns whether the given type binding matches the given simple name pattern * qualification pattern and enclosing type name pattern. @@ -134,6 +189,18 @@ protected int resolveLevelForType(char[] simpleNamePattern, char[] qualification return resolveLevelForType(simpleNamePattern, fullQualificationPattern, type); return IMPOSSIBLE_MATCH; } +protected int resolveLevelForType(char[] simpleNamePattern, char[] qualificationPattern, char[] enclosingNamePattern, ITypeBinding type) { + if (enclosingNamePattern == null) + return resolveLevelForType(simpleNamePattern, qualificationPattern, type); + if (qualificationPattern == null) + return resolveLevelForType(simpleNamePattern, enclosingNamePattern, type); + + // pattern was created from a Java element: qualification is the package name. + char[] fullQualificationPattern = CharOperation.concat(qualificationPattern, enclosingNamePattern, '.'); + if (CharOperation.equals(this.pattern.pkg, type.getPackage().getName().toCharArray())) + return resolveLevelForType(simpleNamePattern, fullQualificationPattern, type); + return IMPOSSIBLE_MATCH; +} private HashSet getModuleGraph(String mName, TypeDeclarationPattern typePattern, HashSet mGraph) { mGraph.add(mName); SearchPattern modulePattern = SearchPattern.createPattern(mName, @@ -231,6 +298,26 @@ private int matchModule(TypeDeclarationPattern typePattern, TypeBinding type) { } return IMPOSSIBLE_MATCH; } +private int matchModule(TypeDeclarationPattern typePattern, ITypeBinding type) { + IModuleBinding module = type.getModule(); + if (module == null || module.getName() == null || typePattern.moduleNames == null) + return POSSIBLE_MATCH; //can't determine, say possible to all. + String bindModName = module.getName(); + + if (typePattern.modulePatterns == null) {// use 'normal' matching + char[][] moduleList = getModuleList(typePattern); + for (char[] m : moduleList) { // match any in the list + int ret = matchNameValue(m, bindModName.toCharArray()); + if (ret != IMPOSSIBLE_MATCH) return ret; + } + } else {// use pattern matching + for (Pattern p : typePattern.modulePatterns) { + Matcher matcher = p.matcher(bindModName); + if (matcher.matches()) return ACCURATE_MATCH; + } + } + return IMPOSSIBLE_MATCH; +} @Override public String toString() { return "Locator for " + this.pattern.toString(); //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterLocator.java index 40efaf8a668..da01df1b901 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterLocator.java @@ -14,6 +14,10 @@ package org.eclipse.jdt.internal.core.search.matching; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeParameter; @@ -51,6 +55,18 @@ public int match(TypeReference node, MatchingNodeSet nodeSet) { } return IMPOSSIBLE_MATCH; } + @Override + public int match(Type node, MatchingNodeSet nodeSet) { + if (this.pattern.findReferences) { + if (node instanceof SimpleType simple) { // Type parameter cannot be qualified + if (matchesName(this.pattern.name, simple.getName().toString().toCharArray())) { + int level = this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH; + return nodeSet.addMatch(node, level); + } + } + } + return IMPOSSIBLE_MATCH; + } /* @@ -110,6 +126,29 @@ protected int matchTypeParameter(TypeVariableBinding variable, boolean matchName } return IMPOSSIBLE_MATCH; } + protected int matchTypeParameter(ITypeBinding variable, boolean matchName) { + if (variable.getDeclaringMethod() != null) { + var methBinding = variable.getDeclaringMethod(); + if (matchesName(methBinding.getDeclaringClass().getName().toCharArray(), this.pattern.methodDeclaringClassName) && + (methBinding.isConstructor() || matchesName(methBinding.getName().toCharArray(), this.pattern.declaringMemberName))) { + int length = this.pattern.methodArgumentTypes==null ? 0 : this.pattern.methodArgumentTypes.length; + if (methBinding.getParameterTypes() == null) { + if (length == 0) return ACCURATE_MATCH; + } else if (methBinding.getParameterTypes().length == length){ + for (int i=0; i