diff --git a/org.eclipse.jdt.core.javac/fragment.xml b/org.eclipse.jdt.core.javac/fragment.xml
index fc5459d9769..30fd3fca6df 100644
--- a/org.eclipse.jdt.core.javac/fragment.xml
+++ b/org.eclipse.jdt.core.javac/fragment.xml
@@ -15,5 +15,12 @@
id="org.eclipse.jdt.core.dom.DOMCompletionEngineProvider">
+
+
+
+
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMASTNodeUtils.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMASTNodeUtils.java
new file mode 100644
index 00000000000..63abcee33f4
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMASTNodeUtils.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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;
+
+import java.util.List;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IParent;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.*;
+
+public class DOMASTNodeUtils {
+
+ public static IJavaElement getEnclosingJavaElement(ASTNode node) {
+ if (node == null) {
+ return null;
+ }
+ if (node instanceof AbstractTypeDeclaration
+ || node instanceof MethodDeclaration
+ || node instanceof FieldDeclaration
+ || node instanceof Initializer
+ || node instanceof ImportDeclaration
+ || 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();
+ }
+ IJavaElement je = findElementForNodeViaDirectBinding(key);
+ if( je != null ) {
+ return je;
+ }
+ IJavaElement je2 = findElementForNodeCustom(key);
+ return je2;
+ }
+
+ private static IJavaElement findElementForNodeCustom(ASTNode key) {
+ if( key instanceof FieldDeclaration fd ) {
+ List fragments = fd.fragments();
+ if( fragments.size() > 0 ) {
+ VariableDeclarationFragment vdf = (VariableDeclarationFragment)fragments.get(0);
+ if( vdf != null ) {
+ IJavaElement ret = findElementForNodeViaDirectBinding(vdf);
+ return ret;
+ }
+ }
+ }
+ if( key instanceof Initializer i) {
+ ASTNode parentNode = i.getParent();
+ int domOccurance = -1;
+ if( parentNode instanceof AbstractTypeDeclaration typeDecl) {
+ List parentBody = typeDecl.bodyDeclarations();
+ for( int z = 0; z < parentBody.size() && domOccurance == -1; z++ ) {
+ if( parentBody.get(z) == key) {
+ domOccurance = z + 1;
+ }
+ }
+ }
+ IJavaElement parentEl = findElementForNodeViaDirectBinding(parentNode);
+ if( parentEl instanceof IParent parentElement) {
+ try {
+ IJavaElement[] kiddos = parentElement.getChildren();
+ for( int q = 0; q < kiddos.length; q++ ) {
+ if( kiddos[q] instanceof IMember kiddoMember) {
+ int count = kiddoMember.getOccurrenceCount();
+ if( count == domOccurance ) {
+ return kiddos[q];
+ }
+ }
+ }
+ } catch( JavaModelException jme) {
+ // ignore
+ }
+ }
+ }
+ if( key instanceof ImportDeclaration id) {
+ ASTNode parentNode = id.getParent();
+ if( parentNode instanceof CompilationUnit unit) {
+ IJavaElement parentEl = ((CompilationUnit)id.getParent()).getJavaElement();
+ return ((org.eclipse.jdt.internal.core.CompilationUnit) parentEl).getImport(id.getName().toString());
+ }
+ }
+ return null;
+ }
+
+ private static IJavaElement findElementForNodeViaDirectBinding(ASTNode key) {
+ if( key != null ) {
+ IBinding b = DOMASTNodeUtils.getBinding(key);
+ if( b != null ) {
+ IJavaElement el = b.getJavaElement();
+ return el;
+ }
+ }
+ return null;
+ }
+
+ public 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 SuperConstructorInvocation superRef) {
+ return superRef.resolveConstructorBinding();
+ }
+ if (astNode instanceof MethodRef methodRef) {
+ return methodRef.resolveBinding();
+ }
+ if (astNode instanceof MethodReference methodRef) {
+ return methodRef.resolveMethodBinding();
+ }
+ if (astNode instanceof AnnotationTypeMemberDeclaration methodRef) {
+ return methodRef.resolveBinding();
+ }
+ if (astNode instanceof ClassInstanceCreation ref) {
+ return ref.resolveConstructorBinding();
+ }
+ if (astNode instanceof TypeParameter ref) {
+ return ref.resolveBinding();
+ }
+ // 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());
+ }
+
+ public static boolean isWithinRange(org.eclipse.jdt.core.dom.ASTNode node, IJavaElement el) {
+ if( el instanceof ISourceReference isr) {
+ try {
+ ISourceRange r = isr.getSourceRange();
+ if( r != null ) {
+ int astStart = node.getStartPosition();
+ int astLen = node.getLength();
+ int rangeStart = r.getOffset();
+ int rangeLen = r.getLength();
+ return astStart >= rangeStart && (astStart + astLen) <= (rangeStart + rangeLen);
+ }
+ } catch(JavaModelException jme) {
+ // Ignore
+ }
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMJavaSearchDelegate.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMJavaSearchDelegate.java
new file mode 100644
index 00000000000..590118a9516
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMJavaSearchDelegate.java
@@ -0,0 +1,354 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2021 IBM Corporation 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
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * Bug 377883 - NPE on open Call Hierarchy
+ * Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
+ *******************************************************************************/
+package org.eclipse.jdt.internal.core.search;
+
+import static org.eclipse.jdt.internal.core.search.DOMASTNodeUtils.insideDocComment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IOpenable;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.SourceRange;
+import org.eclipse.jdt.core.WorkingCopyOwner;
+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.IBinding;
+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.ParameterizedType;
+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.IJavaSearchDelegate;
+import org.eclipse.jdt.core.search.LocalVariableReferenceMatch;
+import org.eclipse.jdt.core.search.MethodReferenceMatch;
+import org.eclipse.jdt.core.search.PackageReferenceMatch;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchParticipant;
+import org.eclipse.jdt.core.search.TypeParameterReferenceMatch;
+import org.eclipse.jdt.core.search.TypeReferenceMatch;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.core.LocalVariable;
+import org.eclipse.jdt.internal.core.NamedMember;
+import org.eclipse.jdt.internal.core.search.matching.MatchLocator;
+import org.eclipse.jdt.internal.core.search.matching.MatchingNodeSet;
+import org.eclipse.jdt.internal.core.search.matching.NodeSetWrapper;
+import org.eclipse.jdt.internal.core.search.matching.PossibleMatch;
+import org.eclipse.jdt.internal.core.search.matching.SearchMatchingUtility;
+
+public class DOMJavaSearchDelegate implements IJavaSearchDelegate {
+ private Map matchToWrapper = new HashMap<>();
+ public DOMJavaSearchDelegate() {
+
+ }
+
+ @Override
+ public void locateMatches(MatchLocator locator, IJavaProject javaProject, PossibleMatch[] possibleMatches, int start,
+ int length) throws CoreException {
+
+ for( int i = 0; i < possibleMatches.length; i++ ) {
+ matchToWrapper.put(possibleMatches[i], wrapNodeSet(possibleMatches[i].nodeSet));
+ }
+
+
+ Map map = javaProject.getOptions(true);
+ map.put(CompilerOptions.OPTION_TaskTags, org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING);
+ locator.options = new CompilerOptions(map);
+
+ // Original implementation used Map throughout, however,
+ // PossibleMatch was determined to be a bad / non-unique key where the
+ // hashCode and equals methods would let two different matches overlap.
+ // So we have to use Arrays and Lists, like a bunch of barbarians.
+ org.eclipse.jdt.core.ICompilationUnit[] unitArray = new org.eclipse.jdt.core.ICompilationUnit[possibleMatches.length];
+
+ Map cuToMatch = new HashMap<>();
+ for (int i = 0; i < possibleMatches.length; i++) {
+ if (!skipMatch(locator, javaProject, possibleMatches[i])) {
+ org.eclipse.jdt.core.ICompilationUnit u = findUnitForPossibleMatch(locator, javaProject, possibleMatches[i]);
+ unitArray[i] = u;
+ cuToMatch.put(u, possibleMatches[i]);
+ }
+ }
+ org.eclipse.jdt.core.ICompilationUnit[] nonNullUnits = Arrays.asList(unitArray).stream().filter(x -> x != null)
+ .toArray(org.eclipse.jdt.core.ICompilationUnit[]::new);
+ if (nonNullUnits.length == 0) {
+ return;
+ }
+
+ Set ownerSet = new HashSet<>();
+ for (int i = 0; i < nonNullUnits.length; i++) {
+ if (nonNullUnits[i].getOwner() != null) {
+ ownerSet.add(nonNullUnits[i].getOwner());
+ }
+ }
+ WorkingCopyOwner owner = null;
+ if (ownerSet.size() == 1) {
+ owner = ownerSet.toArray(new WorkingCopyOwner[ownerSet.size()])[0];
+ }
+
+ ASTParser astParser = ASTParser.newParser(AST.getJLSLatest());
+ astParser.setCompilerOptions(javaProject.getOptions(true));
+ astParser.setProject(javaProject);
+ astParser.setResolveBindings(true);
+ astParser.setBindingsRecovery(true);
+ if (owner != null)
+ astParser.setWorkingCopyOwner(owner);
+
+ org.eclipse.jdt.core.dom.CompilationUnit[] domUnits = new org.eclipse.jdt.core.dom.CompilationUnit[possibleMatches.length];
+
+ List nonNullDomIndexes = new ArrayList<>();
+ astParser.createASTs(nonNullUnits, new String[0], new ASTRequestor() {
+ @Override
+ public void acceptAST(org.eclipse.jdt.core.ICompilationUnit source,
+ org.eclipse.jdt.core.dom.CompilationUnit ast) {
+ PossibleMatch pm = cuToMatch.get(source);
+ if (pm != null) {
+ for (int i = 0; i < possibleMatches.length; i++) {
+ if (possibleMatches[i] == pm) {
+ domUnits[i] = ast;
+ nonNullDomIndexes.add(i);
+ locator.currentPossibleMatch = pm;
+ NodeSetWrapper wrapper = matchToWrapper.get(possibleMatches[i]);
+ ast.accept(new PatternLocatorVisitor(locator.patternLocator,
+ wrapper, locator));
+ return;
+ }
+ }
+ }
+ }
+ // todo, use a subprogressmonitor or slice it
+ }, locator.progressMonitor);
+
+ Collections.sort(nonNullDomIndexes);
+ for (int x : nonNullDomIndexes) {
+ PossibleMatch possibleMatch = possibleMatches[x];
+ locator.currentPossibleMatch = possibleMatch;
+ NodeSetWrapper wrapper = this.matchToWrapper.get(possibleMatch);
+ for (org.eclipse.jdt.core.dom.ASTNode node : wrapper.trustedASTNodeLevels.keySet()) {
+ int level = wrapper.trustedASTNodeLevels.get(node);
+ SearchMatch match = toMatch(locator, node, level, possibleMatch);
+ if (match != null && match.getElement() != null) {
+ try {
+ SearchMatchingUtility.reportSearchMatch(locator, match);
+ } catch (CoreException ex) {
+ ILog.get().error(ex.getMessage(), ex);
+ }
+ }
+ }
+ }
+ }
+
+ private NodeSetWrapper wrapNodeSet(MatchingNodeSet nodeSet) {
+ return new NodeSetWrapper(nodeSet);
+ }
+
+ private boolean skipMatch(MatchLocator locator, IJavaProject javaProject, PossibleMatch possibleMatch) {
+ if (locator.options.sourceLevel >= ClassFileConstants.JDK9) {
+ char[] pModuleName = possibleMatch.getModuleName();
+ if (pModuleName != null && locator.lookupEnvironment.getModule(pModuleName) == null)
+ return true;
+ }
+ return false;
+ }
+
+ private org.eclipse.jdt.core.ICompilationUnit findUnitForPossibleMatch(MatchLocator locator, IJavaProject jp, PossibleMatch match) {
+ if (!skipMatch(locator, jp, match)) {
+ if (match.openable instanceof org.eclipse.jdt.core.ICompilationUnit cu) {
+ return cu;
+ } else if (match.openable instanceof ITypeRoot tr) {
+ ITypeRoot toOpen = tr;
+ try {
+ // If this is a nested class like p/X$Y, it won't work. When it gets to
+ // the bindings for p/X, it thinks it is in p/X$Y.class file. :|
+ String n = tr.getElementName();
+ if (n.toLowerCase().endsWith(".class") && n.contains("$")) {
+ String enclosingSourceFile = n.substring(0, n.indexOf("$")) + ".class";
+ IJavaElement parent = tr.getParent();
+ if (parent instanceof IPackageFragment ipf) {
+ IOpenable open2 = ipf.getClassFile(enclosingSourceFile);
+ if (open2 instanceof ITypeRoot tr2) {
+ toOpen = tr2;
+ }
+ }
+ }
+ org.eclipse.jdt.core.ICompilationUnit ret = toOpen.getWorkingCopy(null, new NullProgressMonitor());
+ return ret;
+ } catch (JavaModelException jme) {
+ // Ignore for now
+ }
+ }
+ }
+ return null;
+ }
+
+ private SearchMatch toMatch(MatchLocator locator, org.eclipse.jdt.core.dom.ASTNode node, int accuracy, PossibleMatch possibleMatch) {
+ IResource resource = possibleMatch.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 locator.newDeclarationMatch(javaElement, null, accuracy, range.getOffset(), range.getLength());
+ }
+ }
+ if (node instanceof MethodInvocation method) {
+ IJavaElement enclosing = DOMASTNodeUtils.getEnclosingJavaElement(node.getParent());
+ IMethodBinding mb = method.resolveMethodBinding();
+ boolean isSynthetic = mb != null && mb.isSynthetic();
+ return new MethodReferenceMatch(enclosing, accuracy, method.getName().getStartPosition(),
+ method.getStartPosition() + method.getLength() - method.getName().getStartPosition(), false,
+ isSynthetic, false, insideDocComment(node), getParticipant(locator), 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(locator),
+ 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(locator), 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(locator), 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(locator),
+ resource);
+ }
+ if (node instanceof EnumConstantDeclaration enumConstantDeclaration) {
+ int start = enumConstantDeclaration.getStartPosition();
+ int len = enumConstantDeclaration.getLength();
+ if (enumConstantDeclaration.getAnonymousClassDeclaration() != null) {
+ len = enumConstantDeclaration.getAnonymousClassDeclaration().getStartPosition() - start;
+ }
+ return new FieldDeclarationMatch(DOMASTNodeUtils.getDeclaringJavaElement(node), accuracy, start, len,
+ getParticipant(locator), resource);
+ }
+ if (node instanceof Type nt) {
+ IBinding b = DOMASTNodeUtils.getBinding(nt);
+ IJavaElement element = DOMASTNodeUtils.getEnclosingJavaElement(node);
+ if (element instanceof LocalVariable) {
+ element = element.getParent();
+ }
+ TypeReferenceMatch ret = new TypeReferenceMatch(element, accuracy, node.getStartPosition(),
+ node.getLength(), DOMASTNodeUtils.insideDocComment(node), getParticipant(locator), resource);
+ if (nt.isParameterizedType()) {
+ if (((ParameterizedType) nt).typeArguments().size() == 0) {
+ ret.setRaw(true);
+ }
+ }
+ return ret;
+ }
+ if (node instanceof org.eclipse.jdt.core.dom.TypeParameter nodeTP) {
+ IJavaElement element = DOMASTNodeUtils.getEnclosingJavaElement(node);
+ return new TypeParameterReferenceMatch(element, accuracy, nodeTP.getName().getStartPosition(),
+ nodeTP.getName().getLength(), DOMASTNodeUtils.insideDocComment(node), getParticipant(locator), resource);
+ }
+ if (node instanceof Name name) {
+ IBinding b = name.resolveBinding();
+ IJavaElement enclosing = DOMASTNodeUtils.getEnclosingJavaElement(node);
+// if( b == null ) {
+// // This fixes some issues but causes even more failures
+// return new SearchMatch(enclosing, accuracy, node.getStartPosition(), node.getLength(), getParticipant(locator), resource);
+// }
+ if (b instanceof ITypeBinding btb) {
+ TypeReferenceMatch ref = new TypeReferenceMatch(enclosing, accuracy, node.getStartPosition(),
+ node.getLength(), insideDocComment(node), getParticipant(locator), resource);
+ if (btb.isRawType())
+ ref.setRaw(true);
+ return ref;
+ }
+ if (b instanceof IVariableBinding variable) {
+ if (variable.isField()) {
+ return new FieldReferenceMatch(enclosing, accuracy, node.getStartPosition(), node.getLength(), true,
+ true, insideDocComment(node), getParticipant(locator), resource);
+ }
+ return new LocalVariableReferenceMatch(enclosing, accuracy, node.getStartPosition(), node.getLength(),
+ true, true, insideDocComment(node), getParticipant(locator), resource);
+ }
+ if (b instanceof IPackageBinding) {
+ return new PackageReferenceMatch(enclosing, accuracy, name.getStartPosition(), name.getLength(),
+ insideDocComment(name), getParticipant(locator), resource);
+ }
+ if (b instanceof IMethodBinding) {
+ return new MethodReferenceMatch(enclosing, accuracy, node.getStartPosition(), node.getLength(),
+ insideDocComment(node), getParticipant(locator), resource);
+ }
+ // more...?
+ }
+ if (node.getLocationInParent() == SimpleType.NAME_PROPERTY
+ || node.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
+ // more...?
+ return toMatch(locator, node.getParent(), accuracy, possibleMatch);
+ }
+ return null;
+ }
+ public SearchParticipant getParticipant(MatchLocator locator) {
+ return locator.currentPossibleMatch.document.getParticipant();
+ }
+
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/PatternLocatorVisitor.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/PatternLocatorVisitor.java
new file mode 100644
index 00000000000..ed288ee4466
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/PatternLocatorVisitor.java
@@ -0,0 +1,291 @@
+/*******************************************************************************
+ * 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;
+
+import java.util.function.Function;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+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.IBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+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.Name;
+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.TypeParameter;
+import org.eclipse.jdt.core.dom.UnionType;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.internal.core.search.matching.ConstructorLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMConstructorLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMFieldLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMLocalVariableLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMMethodLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMPackageReferenceLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMPatternLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMSuperTypeReferenceLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMTypeDeclarationLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMTypeParameterLocator;
+import org.eclipse.jdt.internal.core.search.matching.DOMTypeReferenceLocator;
+import org.eclipse.jdt.internal.core.search.matching.FieldLocator;
+import org.eclipse.jdt.internal.core.search.matching.LocalVariableLocator;
+import org.eclipse.jdt.internal.core.search.matching.MatchLocator;
+import org.eclipse.jdt.internal.core.search.matching.MethodLocator;
+import org.eclipse.jdt.internal.core.search.matching.NodeSetWrapper;
+import org.eclipse.jdt.internal.core.search.matching.PackageReferenceLocator;
+import org.eclipse.jdt.internal.core.search.matching.PatternLocator;
+import org.eclipse.jdt.internal.core.search.matching.SuperTypeReferenceLocator;
+import org.eclipse.jdt.internal.core.search.matching.TypeDeclarationLocator;
+import org.eclipse.jdt.internal.core.search.matching.TypeParameterLocator;
+import org.eclipse.jdt.internal.core.search.matching.TypeReferenceLocator;
+
+/**
+ * Visits an AST to feel the possible match with nodes
+ */
+class PatternLocatorVisitor extends ASTVisitor {
+
+ private final PatternLocator patternLocator;
+ private final NodeSetWrapper nodeSet;
+ private MatchLocator locator;
+
+ public PatternLocatorVisitor(PatternLocator patternLocator, NodeSetWrapper nodeSet, MatchLocator locator) {
+ super(true);
+ this.patternLocator = patternLocator;
+ this.nodeSet = nodeSet;
+ this.locator = locator;
+ }
+
+ private DOMPatternLocator getWrapper(PatternLocator locator) {
+ // TODO implement all this.
+ if( locator instanceof FieldLocator fl) {
+ return new DOMFieldLocator(fl);
+ }
+ if( locator instanceof ConstructorLocator cl) {
+ return new DOMConstructorLocator(cl);
+ }
+ if( locator instanceof LocalVariableLocator lcl) {
+ return new DOMLocalVariableLocator(lcl);
+ }
+ if( locator instanceof MethodLocator ml) {
+ return new DOMMethodLocator(ml);
+ }
+ if( locator instanceof PackageReferenceLocator prl) {
+ return new DOMPackageReferenceLocator(prl);
+ }
+ if( locator instanceof SuperTypeReferenceLocator strl) {
+ return new DOMSuperTypeReferenceLocator(strl);
+ }
+ if( locator instanceof TypeDeclarationLocator tdl) {
+ return new DOMTypeDeclarationLocator(tdl);
+ }
+ if( locator instanceof TypeParameterLocator tpl) {
+ return new DOMTypeParameterLocator(tpl);
+ }
+ if( locator instanceof TypeReferenceLocator trl) {
+ return new DOMTypeReferenceLocator(trl);
+ }
+ return new DOMPatternLocator(null); // stub
+ }
+
+ private boolean defaultVisitImplementation(T node, Function levelFunc) {
+ return defaultVisitImplementationWithFunc(node, levelFunc, DOMASTNodeUtils::getBinding);
+ }
+
+ private boolean defaultVisitImplementationWithFunc(
+ T node,
+ Function levelFunc,
+ Function bindingFunc) {
+ int level = levelFunc.apply(node);
+ if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.getWrapped().mustResolve || this.patternLocator.isMustResolve())) {
+ level = getWrapper(this.patternLocator).resolveLevel(node, bindingFunc.apply(node), this.locator);
+ }
+ this.nodeSet.addMatch(node, level);
+ return true;
+
+ }
+
+
+ @Override
+ public boolean visit(AnnotationTypeDeclaration node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(TypeParameter node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(MethodInvocation node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(ExpressionMethodReference node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SuperMethodReference node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+
+ private boolean visitAbstractTypeDeclaration(AbstractTypeDeclaration node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @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) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+
+ private boolean visitType(Type node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SimpleType type) {
+ visitType(type);
+ Name n = type.getName();
+ if( n instanceof QualifiedName qn ) {
+ Name qualifier = qn.getQualifier();
+ if( qualifier instanceof SimpleName sn1 ) {
+ visit(sn1);
+ } else if( qualifier instanceof QualifiedName qn1) {
+ visit(qn1);
+ }
+ }
+ 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) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(CreationReference node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SuperConstructorInvocation node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ if (
+ 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 = getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator);
+ if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.getWrapped().mustResolve || this.patternLocator.isMustResolve())) {
+ IBinding b = node.resolveBinding();
+ level = getWrapper(this.patternLocator).resolveLevel(node, b, this.locator);
+ }
+ this.nodeSet.addMatch(node, level);
+ return level == 0;
+ }
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ return defaultVisitImplementation(node, x -> getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator));
+ }
+ @Override
+ public boolean visit(EnumConstantDeclaration node) {
+ int level = getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator);
+ if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.getWrapped().mustResolve || this.patternLocator.isMustResolve())) {
+ int l1 = getWrapper(this.patternLocator).resolveLevel(node, node.resolveVariable(), this.locator);
+ int l2 = getWrapper(this.patternLocator).resolveLevel(node, node.resolveConstructorBinding(), this.locator);
+ level = Math.max(l1, l2);
+ }
+ 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 = getWrapper(this.patternLocator).match(node, this.nodeSet, this.locator);
+ if ((level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.POSSIBLE_MATCH && (this.nodeSet.getWrapped().mustResolve || this.patternLocator.isMustResolve())) {
+ level = getWrapper(this.patternLocator).resolveLevel(node, node.resolveBinding(), this.locator);
+ }
+ this.nodeSet.addMatch(node, level);
+ if( (level & PatternLocator.MATCH_LEVEL_MASK) == PatternLocator.IMPOSSIBLE_MATCH ) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(ImportDeclaration node) {
+ return true;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMConstructorLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMConstructorLocator.java
new file mode 100644
index 00000000000..af85d870038
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMConstructorLocator.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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 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.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.core.search.DOMASTNodeUtils;
+
+public class DOMConstructorLocator extends DOMPatternLocator {
+
+ private ConstructorLocator locator;
+
+ public DOMConstructorLocator(ConstructorLocator cl) {
+ super(cl.pattern);
+ this.locator = cl;
+ }
+
+ @Override
+ public int match(MethodDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (!node.isConstructor()) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if (this.locator.pattern.fineGrain != 0 && !this.locator.pattern.findDeclarations)
+ return IMPOSSIBLE_MATCH;
+ int referencesLevel = /* this.locator.pattern.findReferences ? matchLevelForReferences(node) : */IMPOSSIBLE_MATCH;
+ int declarationsLevel = this.locator.pattern.findDeclarations ? this.matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH;
+
+ // use the stronger match
+ return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel);
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.Expression node, NodeSetWrapper nodeSet, MatchLocator locator) { // interested
+ // in
+ // AllocationExpression
+ if (!this.locator.pattern.findReferences)
+ return IMPOSSIBLE_MATCH;
+ if (node instanceof CreationReference creationRef && (this.locator.pattern.declaringSimpleName == null
+ || this.matchesTypeReference(this.locator.pattern.declaringSimpleName, creationRef.getType()))) {
+ return this.locator.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH;
+ }
+ if (node instanceof ClassInstanceCreation newInstance) {
+ return (this.locator.pattern.declaringSimpleName == null
+ || this.matchesTypeReference(this.locator.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, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (!this.locator.pattern.findReferences)
+ return IMPOSSIBLE_MATCH;
+ if (node instanceof SuperConstructorInvocation superRef) {
+ if (!matchParametersCount(node, superRef.arguments())) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if (this.locator.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 (!this.matchesTypeReference(this.locator.pattern.declaringSimpleName, superType)) {
+ return IMPOSSIBLE_MATCH;
+ }
+ }
+ return this.locator.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH;
+ }
+ if (node instanceof EnumConstantDeclaration enumConstantDecl
+ && node.getParent() instanceof EnumDeclaration enumDeclaration
+ && this.locator.matchesName(this.locator.pattern.declaringSimpleName,
+ enumDeclaration.getName().getIdentifier().toCharArray())
+ && matchParametersCount(enumConstantDecl, enumConstantDecl.arguments())) {
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ 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;
+ }
+
+ boolean matchParametersCount(org.eclipse.jdt.core.dom.ASTNode node,
+ List args) {
+ if (this.locator.pattern.parameterSimpleNames != null
+ && (!this.locator.pattern.varargs || DOMASTNodeUtils.insideDocComment(node))) {
+ int length = this.locator.pattern.parameterCount;
+ if (length < 0)
+ length = this.locator.pattern.parameterSimpleNames.length;
+ int argsLength = args == null ? 0 : args.size();
+ if (length != argsLength) {
+ return false;
+ }
+ }
+ return true;
+ }
+ protected int matchLevelForDeclarations(ConstructorDeclaration constructor) {
+ // constructor name is stored in selector field
+ if (this.locator.pattern.declaringSimpleName != null && !this.locator.matchesName(this.locator.pattern.declaringSimpleName, constructor.selector))
+ return IMPOSSIBLE_MATCH;
+
+ if (this.locator.pattern.parameterSimpleNames != null) {
+ int length = this.locator.pattern.parameterSimpleNames.length;
+ Argument[] args = constructor.arguments;
+ int argsLength = args == null ? 0 : args.length;
+ 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.locator.pattern.hasConstructorArguments()) {
+ if (constructor.typeParameters == null || constructor.typeParameters.length != this.locator.pattern.constructorArguments.length) return IMPOSSIBLE_MATCH;
+ }
+
+ return this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ }
+ protected int matchConstructor(IMethodBinding constructor) {
+ if (!constructor.isConstructor()) return IMPOSSIBLE_MATCH;
+
+ // declaring type, simple name has already been matched by matchIndexEntry()
+ int level = this.resolveLevelForType(this.locator.pattern.declaringSimpleName, this.locator.pattern.declaringQualification, constructor.getDeclaringClass());
+ if (level == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH;
+
+ // parameter types
+ int parameterCount = this.locator.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 = this.resolveLevelForType(this.locator.pattern.parameterSimpleNames[i], this.locator.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;
+ }
+
+ protected int matchLevelForDeclarations(MethodDeclaration constructor) {
+ // constructor name is stored in selector field
+ if (this.locator.pattern.declaringSimpleName != null && !this.locator.matchesName(this.locator.pattern.declaringSimpleName, constructor.getName().toString().toCharArray()))
+ return IMPOSSIBLE_MATCH;
+
+ if (this.locator.pattern.parameterSimpleNames != null) {
+ int length = this.locator.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.locator.pattern.hasConstructorArguments()) {
+ if (constructor.typeParameters() == null || constructor.typeParameters().size() != this.locator.pattern.constructorArguments.length) return IMPOSSIBLE_MATCH;
+ }
+
+ return this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMFieldLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMFieldLocator.java
new file mode 100644
index 00000000000..0624f66e145
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMFieldLocator.java
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.JavaModelException;
+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.SearchPattern;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.core.search.DOMASTNodeUtils;
+
+public class DOMFieldLocator extends DOMPatternLocator {
+
+ private FieldLocator fieldLocator;
+
+ public DOMFieldLocator(FieldLocator locator) {
+ super(locator.pattern);
+ this.fieldLocator = locator;
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.ASTNode node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ int declarationsLevel = PatternLocator.IMPOSSIBLE_MATCH;
+ if (node instanceof EnumConstantDeclaration enumConstant) {
+ return match(enumConstant, nodeSet);
+ }
+ if (this.fieldLocator.pattern.findReferences) {
+ if (node instanceof ImportDeclaration importRef) {
+ // With static import, we can have static field reference in import reference
+ if (importRef.isStatic() && !importRef.isOnDemand()
+ && this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ importRef.getName().toString().toCharArray())
+ && this.fieldLocator.pattern instanceof FieldPattern fieldPattern) {
+ char[] declaringType = CharOperation.concat(fieldPattern.declaringQualification,
+ fieldPattern.declaringSimpleName, '.');
+ if (this.fieldLocator.matchesName(declaringType, importRef.getName().toString().toCharArray())) {
+ declarationsLevel = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+ }
+ }
+ }
+ }
+ return nodeSet.addMatch(node, declarationsLevel);
+ }
+
+ @Override
+ public int match(Name name, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.fieldLocator.pattern.findDeclarations) {
+ return PatternLocator.IMPOSSIBLE_MATCH; // already caught by match(VariableDeclaration)
+ }
+
+ if (this.fieldLocator.matchesName(this.fieldLocator.pattern.name, name.toString().toCharArray())) {
+ if (this.fieldLocator.isDeclarationOfAccessedFieldsPattern
+ && this.fieldLocator.pattern instanceof DeclarationOfAccessedFieldsPattern doafp) {
+ if (doafp.enclosingElement != null) {
+ // we have an enclosing element to check
+ if (!DOMASTNodeUtils.isWithinRange(name, doafp.enclosingElement)) {
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ // We need to report the declaration, not the usage
+ // TODO testDeclarationOfAccessedFields2
+ IBinding b = name.resolveBinding();
+ IJavaElement je = b == null ? null : b.getJavaElement();
+ if (je != null && doafp.knownFields.includes(je)) {
+ doafp.knownFields.remove(je);
+ ISourceReference sr = je instanceof ISourceReference ? (ISourceReference) je : null;
+ IResource r = null;
+ ISourceRange srg = null;
+ String elName = je.getElementName();
+ try {
+ srg = sr.getSourceRange();
+ IJavaElement ancestor = je.getAncestor(IJavaElement.COMPILATION_UNIT);
+ r = ancestor == null ? null : ancestor.getCorrespondingResource();
+ } catch (JavaModelException jme) {
+ // ignore
+ }
+ if (srg != null) {
+ int accuracy = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+ FieldDeclarationMatch fdMatch = new FieldDeclarationMatch(je, accuracy,
+ srg.getOffset() + srg.getLength() - elName.length() - 1, elName.length(),
+ locator.getParticipant(), r);
+ try {
+ locator.report(fdMatch);
+ } catch (CoreException ce) {
+ // ignore
+ }
+ }
+ }
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ }
+
+ return nodeSet.addMatch(name, this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH);
+ }
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding == null)
+ return PatternLocator.ACCURATE_MATCH;
+ if (binding instanceof IVariableBinding variableBinding) {
+ if (variableBinding.isRecordComponent()) {
+ // for matching the component in constructor of a record
+ if (!this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ variableBinding.getName().toCharArray()))
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ FieldPattern fieldPattern = (FieldPattern) this.fieldLocator.pattern;
+ return this.resolveLevelForType(fieldPattern.declaringSimpleName,
+ fieldPattern.declaringQualification, variableBinding.getDeclaringMethod().getDeclaringClass());
+ }
+ if (variableBinding.isField()) {
+ return this.matchField(variableBinding, true);
+ }
+ }
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int match(VariableDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (!this.fieldLocator.pattern.findDeclarations && !this.fieldLocator.isDeclarationOfAccessedFieldsPattern) {
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ if (node.getLocationInParent() != org.eclipse.jdt.core.dom.FieldDeclaration.FRAGMENTS_PROPERTY) {
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ int referencesLevel = PatternLocator.IMPOSSIBLE_MATCH;
+ if (this.fieldLocator.pattern.findReferences)
+ // must be a write only access with an initializer
+ if (this.fieldLocator.pattern.writeAccess && !this.fieldLocator.pattern.readAccess
+ && node.getInitializer() != null)
+ if (this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ node.getName().getIdentifier().toCharArray()))
+ referencesLevel = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+
+ int declarationsLevel = PatternLocator.IMPOSSIBLE_MATCH;
+ if ((this.fieldLocator.pattern.findDeclarations || this.fieldLocator.isDeclarationOfAccessedFieldsPattern)
+ && this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ node.getName().getIdentifier().toCharArray())
+ && this.fieldLocator.pattern instanceof FieldPattern fieldPattern
+ && this.matchesTypeReference(fieldPattern.typeSimpleName,
+ ((org.eclipse.jdt.core.dom.FieldDeclaration) node.getParent()).getType())) {
+ declarationsLevel = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+ }
+ return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use
+ // the
+ // stronger
+ // match
+ }
+
+ private int match(EnumConstantDeclaration node, NodeSetWrapper nodeSet) {
+ int referencesLevel = PatternLocator.IMPOSSIBLE_MATCH;
+ if (this.fieldLocator.pattern.findReferences)
+ // must be a write only access with an initializer
+ if (this.fieldLocator.pattern.writeAccess && !this.fieldLocator.pattern.readAccess)
+ if (this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ node.getName().getIdentifier().toCharArray()))
+ referencesLevel = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+
+ int declarationsLevel = PatternLocator.IMPOSSIBLE_MATCH;
+ if (this.fieldLocator.pattern.findDeclarations
+ && this.fieldLocator.matchesName(this.fieldLocator.pattern.name,
+ node.getName().getIdentifier().toCharArray())
+ && this.fieldLocator.pattern instanceof FieldPattern fieldPattern
+ && this.fieldLocator.matchesName(fieldPattern.typeSimpleName,
+ ((EnumDeclaration) node.getParent()).getName().getIdentifier().toCharArray())) {
+ declarationsLevel = this.fieldLocator.pattern.mustResolve ? PatternLocator.POSSIBLE_MATCH
+ : PatternLocator.ACCURATE_MATCH;
+ }
+ return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use
+ // the
+ // stronger
+ // match
+ }
+
+ protected int matchField(IVariableBinding field, boolean matchName) {
+ if (field == null)
+ return INACCURATE_MATCH;
+ if (!field.isField())
+ return IMPOSSIBLE_MATCH;
+
+ if (matchName && !this.fieldLocator.matchesName(this.fieldLocator.pattern.name, field.getName().toCharArray()))
+ return IMPOSSIBLE_MATCH;
+
+ FieldPattern fieldPattern = (FieldPattern) this.fieldLocator.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;
+ int mode = fieldPattern.getMatchMode();
+ if (mode == SearchPattern.R_EXACT_MATCH) {
+ return IMPOSSIBLE_MATCH;
+ }
+ return INACCURATE_MATCH;
+ }
+
+ // Note there is no dynamic lookup for field access
+ int declaringLevel = this.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) {
+ if (this.fieldLocator.isDeclarationOfAccessedFieldsPattern
+ && this.fieldLocator.pattern instanceof DeclarationOfAccessedFieldsPattern doafp) {
+ IJavaElement je = field.getJavaElement();
+ if (je != null) {
+ doafp.knownFields.add(je);
+ }
+ } else {
+ return declaringLevel;
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ // 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());
+ int ret = declaringLevel > typeLevel ? typeLevel : declaringLevel; // return the weaker match
+ if (this.fieldLocator.isDeclarationOfAccessedFieldsPattern
+ && this.fieldLocator.pattern instanceof DeclarationOfAccessedFieldsPattern doafp) {
+ IJavaElement je = field.getJavaElement();
+ if (je != null) {
+ doafp.knownFields.add(je);
+ }
+ } else {
+ return ret;
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ protected int resolveLevelForType(ITypeBinding typeBinding) {
+ FieldPattern fieldPattern = (FieldPattern) this.fieldLocator.pattern;
+ ITypeBinding fieldTypeBinding = typeBinding;
+ if (fieldTypeBinding != null && fieldTypeBinding.isParameterizedType()) {
+ fieldTypeBinding = typeBinding.getErasure();
+ }
+ int fieldNameMatch = this.resolveLevelForType(fieldPattern.typeSimpleName,
+ fieldPattern.typeQualification, fieldTypeBinding);
+ return fieldNameMatch;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMLocalVariableLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMLocalVariableLocator.java
new file mode 100644
index 00000000000..fb9d9fcfd69
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMLocalVariableLocator.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.Objects;
+
+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;
+
+public class DOMLocalVariableLocator extends DOMPatternLocator {
+
+ private LocalVariableLocator locator;
+
+ public DOMLocalVariableLocator(LocalVariableLocator lcl) {
+ super(lcl.pattern);
+ this.locator = lcl;
+ }
+
+ @Override
+ public int match(Name node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ 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
+ && this.locator.getLocalVariable().getElementName().equals(simple.getIdentifier())) {
+ return POSSIBLE_MATCH;
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (!(binding instanceof IVariableBinding)) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if (Objects.equals(binding.getJavaElement(), this.locator.getLocalVariable())) {
+ return ACCURATE_MATCH;
+ }
+ return INACCURATE_MATCH;
+ }
+
+ @Override
+ public int match(VariableDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ int referencesLevel = IMPOSSIBLE_MATCH;
+ if (this.locator.pattern.findReferences)
+ // must be a write only access with an initializer
+ if (this.locator.pattern.writeAccess && !this.locator.pattern.readAccess && node.getInitializer() != null)
+ if (this.locator.matchesName(this.locator.pattern.name, node.getName().getIdentifier().toCharArray()))
+ referencesLevel = this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+
+ int declarationsLevel = IMPOSSIBLE_MATCH;
+ if (this.locator.pattern.findDeclarations)
+ if (this.locator.matchesName(this.locator.pattern.name, node.getName().getIdentifier().toCharArray()))
+ if (node.getStartPosition() == this.locator.getLocalVariable().declarationSourceStart)
+ declarationsLevel = this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+
+ // Use the stronger match
+ return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel);
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMMethodLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMMethodLocator.java
new file mode 100644
index 00000000000..5e5d54970ac
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMMethodLocator.java
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.Objects;
+import java.util.stream.Stream;
+
+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.Name;
+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.internal.core.BinaryMethod;
+import org.eclipse.jdt.internal.core.search.DOMASTNodeUtils;
+
+public class DOMMethodLocator extends DOMPatternLocator {
+
+ private MethodLocator locator;
+ public DOMMethodLocator(MethodLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+
+ 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 int match(org.eclipse.jdt.core.dom.MethodDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (!this.locator.pattern.findDeclarations) return IMPOSSIBLE_MATCH;
+
+ // Verify method name
+ if (!this.locator.matchesName(this.locator.pattern.selector, node.getName().getIdentifier().toCharArray())) return IMPOSSIBLE_MATCH;
+
+ // Verify parameters types
+ boolean resolve = this.locator.pattern.mustResolve;
+ if (this.locator.pattern.parameterSimpleNames != null) {
+ int length = this.locator.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 (!this.matchesTypeReference(this.locator.pattern.parameterSimpleNames[i], arg.getType(), arg.isVarargs())) {
+ // Do not return as impossible when source level is at least 1.5
+ if (this.locator.mayBeGeneric) {
+ if (!this.locator.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.getWrapped().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.locator.pattern.hasMethodArguments()) {
+ if (node.typeParameters() == null || node.typeParameters().size() != this.locator.pattern.methodArguments.length)
+ return IMPOSSIBLE_MATCH;
+ }
+
+ // Method declaration may match pattern
+ return nodeSet.addMatch(node, resolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ private int matchReference(SimpleName name, List> args, NodeSetWrapper nodeSet) {
+ if (!this.locator.pattern.findReferences) return IMPOSSIBLE_MATCH;
+
+ if (!this.locator.matchesName(this.locator.pattern.selector, name.getIdentifier().toCharArray())) return IMPOSSIBLE_MATCH;
+ if (this.locator.pattern.parameterSimpleNames != null && (!this.locator.pattern.varargs || DOMASTNodeUtils.insideDocComment(name))) {
+ int length = this.locator.pattern.parameterSimpleNames.length;
+ int argsLength = args == null ? 0 : args.size();
+ if (length != argsLength) return IMPOSSIBLE_MATCH;
+ }
+ return this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ }
+ @Override
+ public int match(MethodInvocation node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ int level = this.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, NodeSetWrapper nodeSet, MatchLocator locator) {
+ int level = expression instanceof SuperMethodInvocation node ? this.matchReference(node.getName(), node.arguments(), nodeSet) :
+ IMPOSSIBLE_MATCH;
+ return level != IMPOSSIBLE_MATCH ? nodeSet.addMatch(expression, level) : level;
+ }
+
+
+ @Override
+ public int match(Name node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ String name = node.toString();
+ String[] segments = name.split("\\."); //$NON-NLS-1$
+ String lastSegment = segments == null || segments.length == 0 ? null : segments[segments.length-1];
+ boolean matchesLastSegment = this.locator.pattern.selector == null ? true :
+ this.locator.matchesName(this.locator.pattern.selector, (lastSegment == null ? "" : lastSegment).toCharArray()); //$NON-NLS-1$
+ boolean matchesPrefix = this.locator.pattern.declaringPackageName == null ? true :
+ name.startsWith(new String(this.locator.pattern.declaringPackageName));
+ return matchesLastSegment && matchesPrefix ? POSSIBLE_MATCH : IMPOSSIBLE_MATCH;
+ }
+
+
+ protected int matchMethod(IMethodBinding method, boolean skipImpossibleArg) {
+ if (!this.locator.matchesName(this.locator.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.locator.pattern.declaringSimpleName == null) {
+ // TODO (frederic) use this call to refine accuracy on return type
+ // int newLevel = resolveLevelForType(this.locator.pattern.returnSimpleName, this.locator.pattern.returnQualification, this.locator.pattern.returnTypeArguments, 0, method.returnType);
+ int newLevel = resolveLevelForType(this.locator.pattern.returnSimpleName, this.locator.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.locator.pattern.parameterSimpleNames == null ? -1 : this.locator.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.locator.pattern!= null && this.locator.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.locator.pattern.parameterQualifications[i] != null) {
+ if (!checkedFocus) {
+ focusMethodBinding = getDOMASTMethodBinding(this.locator.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.locator.pattern.parameterSimpleNames[i], this.locator.pattern.parameterQualifications[i], this.locator.pattern.parametersTypeArguments[i], 0, argType);
+ newLevel = this.resolveLevelForType(this.locator.pattern.parameterSimpleNames[i], this.locator.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 = getDOMASTMethodBinding(this.locator.pattern);
+ if (focusMethodBinding != null
+ /* && matchOverriddenMethod(focusMethodBinding.getDeclaringClass(), focusMethodBinding, method)*/
+ && (focusMethodBinding.overrides(method) || method.overrides(focusMethodBinding))) {
+ return ACCURATE_MATCH;
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ }
+ return level;
+ }
+
+ public IMethodBinding getDOMASTMethodBinding(MethodPattern methodPattern) {
+ // TODO
+ return null;
+ }
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding instanceof IMethodBinding method) {
+ boolean skipVerif = this.locator.pattern.findDeclarations && this.locator.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.locator.pattern.declaringSimpleName == null && this.locator.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.locator.pattern.declaringQualification != null && method.getDeclaringClass() != null && method.getDeclaringClass().getPackage() != null) {
+ subType = CharOperation.compareWith(this.locator.pattern.declaringQualification, method.getDeclaringClass().getPackage().getName().toCharArray()) == 0;
+ }
+ int declaringLevel = subType
+ ? resolveLevelAsSubtype(this.locator.pattern.declaringSimpleName, this.locator.pattern.declaringQualification, method.getDeclaringClass(), method.getName(), null, method.getDeclaringClass().getPackage().getName(), (method.getModifiers() & Modifier.DEFAULT) != 0)
+ : this.resolveLevelForType(this.locator.pattern.declaringSimpleName, this.locator.pattern.declaringQualification, method.getDeclaringClass());
+ return (methodLevel & PatternLocator.MATCH_LEVEL_MASK) > (declaringLevel & PatternLocator.MATCH_LEVEL_MASK) ? declaringLevel : methodLevel; // return the weaker match
+ }
+ return INACCURATE_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 = this.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 |= PatternLocator.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 & PatternLocator.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 |= PatternLocator.OVERRIDDEN_METHOD_FLAVOR;
+ }
+ }
+ }
+ return level | PatternLocator.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 |= PatternLocator.OVERRIDDEN_METHOD_FLAVOR;
+ }
+ return level | PatternLocator.SUB_INVOCATION_FLAVOR; // add flavor to returned level
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPackageReferenceLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPackageReferenceLocator.java
new file mode 100644
index 00000000000..372102c80d6
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPackageReferenceLocator.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.Arrays;
+
+import org.eclipse.jdt.core.dom.ArrayType;
+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.NameQualifiedType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Type;
+
+public class DOMPackageReferenceLocator extends DOMPatternLocator {
+
+ private PackageReferenceLocator locator;
+
+ public DOMPackageReferenceLocator(PackageReferenceLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.Annotation node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ return match(node.getTypeName(), nodeSet, locator);
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.ASTNode node, NodeSetWrapper nodeSet, MatchLocator locator) { // interested
+ // in
+ // ImportReference
+ if (node instanceof ImportDeclaration decl) {
+ return match(decl.getName(), nodeSet, locator);
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int match(Name node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // interested in QualifiedNameReference
+ char[][] arr = Arrays.stream(node.getFullyQualifiedName().split("\\.")).map(String::toCharArray) //$NON-NLS-1$
+ .toArray(char[][]::new);
+ return nodeSet.addMatch(node, this.locator.matchLevelForTokens(arr));
+ }
+
+ @Override
+ public int match(Type node, NodeSetWrapper nodeSet, MatchLocator locator) { // interested in QualifiedTypeReference
+ // only
+ if (node instanceof ArrayType att) {
+ return match(att.getElementType(), nodeSet, locator);
+ }
+ Name typePkg = null;
+ if (node instanceof SimpleType stt) {
+ Name n = stt.getName();
+ typePkg = n instanceof QualifiedName qn ? qn.getQualifier() : n;
+ } else if (node instanceof QualifiedType qt3) {
+ Type t1 = qt3.getQualifier();
+ typePkg = t1 instanceof SimpleType sttt ? sttt.getName() : null;
+ } else if (node instanceof NameQualifiedType qt) {
+ typePkg = qt.getQualifier();
+ }
+ return typePkg != null ? match(typePkg, nodeSet, locator) : IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding instanceof IPackageBinding ipb) {
+ String n = ipb.getName();
+ String patternName = new String(this.locator.pattern.pkgName);
+ if (patternName.equals(n)) {
+ return ACCURATE_MATCH;
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPatternLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPatternLocator.java
new file mode 100644
index 00000000000..993214863c4
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPatternLocator.java
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.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.SearchPattern;
+import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+
+public class DOMPatternLocator extends PatternLocator {
+ public DOMPatternLocator(SearchPattern pattern) {
+ super(pattern);
+ }
+ public static final int IMPOSSIBLE_MATCH = PatternLocator.IMPOSSIBLE_MATCH;
+ public static final int INACCURATE_MATCH = PatternLocator.INACCURATE_MATCH;
+ public static final int POSSIBLE_MATCH = PatternLocator.POSSIBLE_MATCH;
+ public static final int ACCURATE_MATCH = PatternLocator.ACCURATE_MATCH;
+ public static final int ERASURE_MATCH = PatternLocator.ERASURE_MATCH;
+
+
+ // AST DOM Variants
+ public int match(org.eclipse.jdt.core.dom.Annotation node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.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, NodeSetWrapper nodeSet, MatchLocator locator) { // needed for some generic nodes
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.Expression node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.FieldDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.LambdaExpression node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(VariableDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.MethodDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.MemberValuePair node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(MethodInvocation node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ protected int match(org.eclipse.jdt.core.dom.ModuleDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(Name node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(FieldAccess node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(AbstractTypeDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(org.eclipse.jdt.core.dom.TypeParameter node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int match(Type node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ // each subtype should override if needed
+ return PatternLocator.IMPOSSIBLE_MATCH;
+ }
+ 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();
+ }
+ 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;
+ return resolveLevelForTypeSourceName(qualifiedPattern, sourceName, type);
+ }
+ 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();
+ }
+ private Name getBaseTypeName(Type type) {
+ if( type instanceof SimpleType simp) {
+ return simp.getName();
+ }
+ if( type instanceof QualifiedType qn) {
+ return qn.getName();
+ }
+ if( type instanceof ArrayType arr) {
+ return getBaseTypeName(arr.getElementType());
+ }
+ return null;
+ }
+
+ 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 = getBaseTypeName(type);
+ 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);
+ }
+ protected int resolveLevelForTypeSourceName(char[] qualifiedPattern, char[] sourceName, ITypeBinding type) {
+ 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( type != null && type.isLocal() ) {
+ if (CharOperation.prefixEquals(qualifiedPattern, sourceName, this.isCaseSensitive)) {
+ return ACCURATE_MATCH;
+ }
+ }
+ if (CharOperation.match(qualifiedPattern, sourceName, this.isCaseSensitive)) {
+ return ACCURATE_MATCH;
+ }
+ }
+ return 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
+ char[] qualifiedNameFromBinding = type.getQualifiedName().toCharArray();
+ if( qualifiedNameFromBinding == null || qualifiedNameFromBinding.length == 0 ) {
+ qualifiedNameFromBinding = type.getName().toCharArray();
+ }
+ boolean match1 = CharOperation.match(qualifiedPattern, qualifiedNameFromBinding, this.isCaseSensitive);
+ return match1 ? ACCURATE_MATCH : IMPOSSIBLE_MATCH;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMSuperTypeReferenceLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMSuperTypeReferenceLocator.java
new file mode 100644
index 00000000000..f61c74eca06
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMSuperTypeReferenceLocator.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.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;
+
+public class DOMSuperTypeReferenceLocator extends DOMPatternLocator {
+
+ private SuperTypeReferenceLocator locator;
+
+ public DOMSuperTypeReferenceLocator(SuperTypeReferenceLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.LambdaExpression node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_INTERFACES)
+ return IMPOSSIBLE_MATCH;
+ nodeSet.getWrapped().mustResolve = true;
+ return nodeSet.addMatch(node, POSSIBLE_MATCH);
+ }
+
+ @Override
+ public int match(Type node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.flavors != PatternLocator.SUPERTYPE_REF_FLAVOR)
+ return IMPOSSIBLE_MATCH;
+ if (this.locator.pattern.superSimpleName == null)
+ return nodeSet.addMatch(node, this.locator.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 (this.locator.matchesName(this.locator.pattern.superSimpleName, typeRefSimpleName))
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+
+ return IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding == null)
+ return INACCURATE_MATCH;
+ if (!(binding instanceof ITypeBinding))
+ return IMPOSSIBLE_MATCH;
+
+ var type = (ITypeBinding) binding;
+ int level = IMPOSSIBLE_MATCH;
+ if (this.locator.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_INTERFACES) {
+ level = this.resolveLevelForType(this.locator.pattern.superSimpleName, this.locator.pattern.superQualification,
+ type.getSuperclass());
+ if (level == ACCURATE_MATCH)
+ return ACCURATE_MATCH;
+ }
+
+ if (this.locator.pattern.superRefKind != SuperTypeReferencePattern.ONLY_SUPER_CLASSES) {
+ for (ITypeBinding superInterface : type.getInterfaces()) {
+ int newLevel = this.resolveLevelForType(this.locator.pattern.superSimpleName, this.locator.pattern.superQualification,
+ superInterface);
+ if (newLevel > level) {
+ if (newLevel == ACCURATE_MATCH)
+ return ACCURATE_MATCH;
+ level = newLevel;
+ }
+ }
+ }
+ return level;
+ }
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeDeclarationLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeDeclarationLocator.java
new file mode 100644
index 00000000000..da6cfcc47ad
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeDeclarationLocator.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.regex.Matcher;
+import java.util.regex.Pattern;
+
+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.internal.core.search.indexing.IIndexConstants;
+
+public class DOMTypeDeclarationLocator extends DOMPatternLocator {
+
+ private TypeDeclarationLocator locator;
+
+ public DOMTypeDeclarationLocator(TypeDeclarationLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+ @Override
+ public int match(AbstractTypeDeclaration node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.pattern.simpleName == null || this.locator.matchesName(this.locator.pattern.simpleName, node.getName().getIdentifier().toCharArray()))
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+
+ return IMPOSSIBLE_MATCH;
+ }
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding == null) return INACCURATE_MATCH;
+ if (!(binding instanceof ITypeBinding)) return IMPOSSIBLE_MATCH;
+
+ ITypeBinding type = (ITypeBinding) binding;
+
+ switch (this.locator.pattern.typeSuffix) {
+ case IIndexConstants.CLASS_SUFFIX:
+ if (!type.isClass()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.CLASS_AND_INTERFACE_SUFFIX:
+ if (!(type.isClass() || (type.isInterface() && !type.isAnnotation()))) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.CLASS_AND_ENUM_SUFFIX:
+ if (!(type.isClass() || type.isEnum())) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.INTERFACE_SUFFIX:
+ if (!type.isInterface() || type.isAnnotation()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.INTERFACE_AND_ANNOTATION_SUFFIX:
+ if (!(type.isInterface() || type.isAnnotation())) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.ENUM_SUFFIX:
+ if (!type.isEnum()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.ANNOTATION_TYPE_SUFFIX:
+ if (!type.isAnnotation()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.TYPE_SUFFIX : // nothing
+ }
+
+ if (this.matchModule(this.locator.pattern, type) == IMPOSSIBLE_MATCH) {
+ return IMPOSSIBLE_MATCH;
+ }
+ // fully qualified name
+ if (this.locator.pattern instanceof QualifiedTypeDeclarationPattern) {
+ QualifiedTypeDeclarationPattern qualifiedPattern = (QualifiedTypeDeclarationPattern) this.locator.pattern;
+ return this.resolveLevelForType(qualifiedPattern.simpleName, qualifiedPattern.qualification, type);
+ } else {
+ char[] enclosingTypeName = this.locator.pattern.enclosingTypeNames == null ? null : CharOperation.concatWith(this.locator.pattern.enclosingTypeNames, '.');
+ return resolveLevelForType(this.locator.pattern.simpleName, this.locator.pattern.pkg, enclosingTypeName, type);
+ }
+ }
+ protected int resolveLevelForType(char[] simpleNamePattern, char[] qualificationPattern, char[] enclosingNamePattern, ITypeBinding type) {
+ if (enclosingNamePattern == null)
+ return this.resolveLevelForType(simpleNamePattern, qualificationPattern, type);
+ if (qualificationPattern == null)
+ return this.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.locator.pattern.pkg, type.getPackage().getName().toCharArray()))
+ return this.resolveLevelForType(simpleNamePattern, fullQualificationPattern, 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 = this.locator.getModuleList(typePattern);
+ for (char[] m : moduleList) { // match any in the list
+ int ret = this.locator.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;
+ }
+
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeParameterLocator.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeParameterLocator.java
new file mode 100644
index 00000000000..9b978f61f6a
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeParameterLocator.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2024 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.IJavaElement;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.ISourceReference;
+import org.eclipse.jdt.core.JavaModelException;
+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;
+
+public class DOMTypeParameterLocator extends DOMPatternLocator {
+
+ private TypeParameterLocator locator;
+
+ public DOMTypeParameterLocator(TypeParameterLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+
+ private static boolean nodeSourceRangeMatchesElement(org.eclipse.jdt.core.dom.ASTNode node, IJavaElement focus) {
+ if( focus == null )
+ return false;
+
+ ISourceRange sr = null;
+ try {
+ if( focus instanceof ISourceReference isr2) {
+ sr = isr2.getSourceRange();
+ }
+ } catch(JavaModelException jme3) {
+ // ignore
+ }
+
+ if( sr == null )
+ return false;
+
+ if( sr.getOffset() == node.getStartPosition() && sr.getLength() == node.getLength()) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int match(Type node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.pattern.findReferences) {
+ if (node instanceof SimpleType simple) { // Type parameter cannot be qualified
+ if (this.locator.matchesName(this.locator.pattern.name, simple.getName().toString().toCharArray())) {
+ int level = this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ return nodeSet.addMatch(node, level);
+ }
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.TypeParameter node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.pattern.findReferences) {
+ if (this.locator.matchesName(this.locator.pattern.name, node.getName().toString().toCharArray())) {
+ int level = this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ return nodeSet.addMatch(node, level);
+ }
+ }
+ if (this.locator.pattern.findDeclarations) {
+ if (this.locator.matchesName(this.locator.pattern.name, node.getName().toString().toCharArray())) {
+ int level = this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
+ return nodeSet.addMatch(node, level);
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding == null) return INACCURATE_MATCH;
+ if (!(binding instanceof ITypeBinding)) return IMPOSSIBLE_MATCH;
+ ITypeBinding tb = (ITypeBinding)binding;
+ int ret = matchTypeParameter(tb, true);
+ if( ret == ACCURATE_MATCH) {
+ if( !this.locator.pattern.findDeclarations && nodeSourceRangeMatchesElement(node, this.locator.pattern.focus)) {
+ return IMPOSSIBLE_MATCH;
+ }
+ }
+ return ret;
+ }
+ protected int matchTypeParameter(ITypeBinding variable, boolean matchName) {
+ if (variable.getDeclaringMethod() != null) {
+ var methBinding = variable.getDeclaringMethod();
+ if (this.locator.matchesName(methBinding.getDeclaringClass().getName().toCharArray(), this.locator.pattern.methodDeclaringClassName) &&
+ (methBinding.isConstructor() || this.locator.matchesName(methBinding.getName().toCharArray(), this.locator.pattern.declaringMemberName))) {
+ int length = this.locator.pattern.methodArgumentTypes==null ? 0 : this.locator.pattern.methodArgumentTypes.length;
+ if (methBinding.getParameterTypes() == null) {
+ if (length == 0) return ACCURATE_MATCH;
+ } else if (methBinding.getParameterTypes().length == length){
+ ITypeBinding[] p = methBinding.getParameterTypes();
+ for (int i=0; i foundElements = new ArrayList<>();
+
+ public DOMTypeReferenceLocator(TypeReferenceLocator locator) {
+ super(locator.pattern);
+ this.locator = locator;
+ }
+ private boolean hasPackageDeclarationAncestor(org.eclipse.jdt.core.dom.ASTNode node) {
+ if( node instanceof PackageDeclaration) {
+ return true;
+ }
+ return node == null ? false : hasPackageDeclarationAncestor(node.getParent());
+ }
+
+
+
+
+ @Override
+ public int match(org.eclipse.jdt.core.dom.Annotation node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ return match(node.getTypeName(), nodeSet, locator);
+ }
+ @Override
+ public int match(Name name, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (name.getParent() instanceof AbstractTypeDeclaration) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if( name.getParent() instanceof LabeledStatement ls && ls.getLabel() == name) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if( name.getParent() instanceof BreakStatement bs && bs.getLabel() == name) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if (this.locator.pattern.simpleName == null) {
+ return nodeSet.addMatch(name, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ if( name instanceof SimpleName sn2 ) {
+ if( this.locator.pattern.qualification == null)
+ return match(sn2, nodeSet);
+ // searching for a qualified name but we are only simple
+ org.eclipse.jdt.core.dom.ASTNode parent3 = name.getParent();
+ if( !(parent3 instanceof QualifiedName)) {
+ return match(sn2, nodeSet);
+ }
+ // Parent is a qualified name and we didn't match it...
+ // so we know the whole name was a failed match, but...
+ if( parent3 instanceof QualifiedName qn3 && qn3.getQualifier() == name) {
+ // Maybe the qualifier is the type we're looking for
+ if( match(sn2, nodeSet) == POSSIBLE_MATCH) {
+ return POSSIBLE_MATCH;
+ }
+ }
+
+ if( this.locator.pattern.getMatchMode() == SearchPattern.R_EXACT_MATCH) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if( match(sn2, nodeSet) == POSSIBLE_MATCH) {
+ return POSSIBLE_MATCH;
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ if( name instanceof QualifiedName qn2 ) {
+ return match(qn2, nodeSet);
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ @Override
+ public int match(org.eclipse.jdt.core.dom.ASTNode node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (node instanceof EnumConstantDeclaration enumConstantDecl
+ && node.getParent() instanceof EnumDeclaration enumDeclaration
+ && enumConstantDecl.getAnonymousClassDeclaration() != null) {
+ if (this.locator.pattern.simpleName == null) {
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ if (this.locator.matchesName(this.locator.pattern.simpleName, enumDeclaration.getName().getIdentifier().toCharArray())) {
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ @Override
+ public int match(Type node, NodeSetWrapper nodeSet, MatchLocator locator) {
+ if (this.locator.pattern.simpleName == null)
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ String qualifiedName = null;
+ String simpleName = null;
+ if (node instanceof SimpleType simple) {
+ if (simple.getName() instanceof SimpleName name) {
+ simpleName = name.getIdentifier();
+ }
+ if (simple.getName() instanceof QualifiedName name) {
+ simpleName = name.getName().getIdentifier();
+ qualifiedName = name.getFullyQualifiedName();
+ }
+ } else if (node instanceof QualifiedType qualified) {
+ simpleName = qualified.getName().getIdentifier();
+ qualifiedName = qualified.getName().getFullyQualifiedName();
+ }
+ if( qualifiedName != null && this.locator.pattern.qualification != null) {
+ // we have a qualified name in the node, and our pattern is searching for a qualified name
+ char[] patternQualified = (new String(this.locator.pattern.qualification) + "." + new String(this.locator.pattern.simpleName)).toCharArray();
+ char[] found = qualifiedName.toCharArray();
+ if( this.locator.matchesName(patternQualified, found)) {
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ } else if (simpleName != null && this.locator.matchesName(this.locator.pattern.simpleName, simpleName.toCharArray())) {
+ return nodeSet.addMatch(node, this.locator.pattern.mustResolve || this.locator.pattern.qualification == null ? POSSIBLE_MATCH : ACCURATE_MATCH);
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ @Override
+ public int resolveLevel(org.eclipse.jdt.core.dom.ASTNode node, IBinding binding, MatchLocator locator) {
+ if (binding == null) {
+ if( node instanceof SimpleName sn) {
+ int accuracy = resolveLevelForSimpleName(node, sn.getIdentifier());
+ if( accuracy != -1 ) {
+ // Add directly
+ IResource r = null;
+ IJavaElement enclosing = DOMASTNodeUtils.getEnclosingJavaElement(node);
+ IJavaElement ancestor = enclosing == null ? null : enclosing.getAncestor(IJavaElement.COMPILATION_UNIT);
+ try {
+ r = ancestor == null ? null : ancestor.getCorrespondingResource();
+ } catch(JavaModelException jme) {
+ // ignore
+ }
+
+ TypeReferenceMatch typeMatch = new TypeReferenceMatch(enclosing, accuracy, node.getStartPosition(), node.getLength(), insideDocComment(node), locator.getParticipant(), r);
+ try {
+ locator.report(typeMatch);
+ } catch(CoreException ce) {
+ // ignore
+ }
+ // Then return not possible so it doesn't get added again
+ return IMPOSSIBLE_MATCH;
+ }
+ }
+ return INACCURATE_MATCH;
+ }
+ if (binding instanceof ITypeBinding typeBinding) {
+ return resolveLevelForTypeBinding(node, typeBinding, locator);
+ }
+ if( binding instanceof IPackageBinding && node instanceof SimpleName sn) {
+ // var x = (B36479.C)val;
+ // might interpret the B36479 to be a package and C a type,
+ // rather than B36479 to be a type and C to be an inner-type
+ if( this.locator.isDeclarationOfReferencedTypesPattern) {
+ return IMPOSSIBLE_MATCH;
+ }
+ if( hasPackageDeclarationAncestor(node)) {
+ return IMPOSSIBLE_MATCH;
+ }
+ String identifier = sn.getIdentifier();
+ if( this.locator.matchesName(this.locator.pattern.simpleName, identifier.toCharArray())) {
+ return INACCURATE_MATCH;
+ }
+
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+
+ /*
+ * Returns a match flag OR -1 if it cannot determine at all.
+ */
+ private int resolveLevelForSimpleName(org.eclipse.jdt.core.dom.ASTNode node, String simpleNameNeedle) {
+ if( !simpleNameNeedle.contains(".") && this.locator.pattern.qualification != null && this.locator.pattern.qualification.length > 0 ) { //$NON-NLS-1$
+ // we need to find out if we import this thing at all
+ org.eclipse.jdt.core.dom.CompilationUnit cu = findCU(node);
+ List imports = cu.imports();
+ for( Object id : imports) {
+ ImportDeclaration idd = (ImportDeclaration)id;
+ if( idd.getName() instanceof QualifiedName qn) {
+ if( qn.getName().toString().equals(simpleNameNeedle)) {
+ char[] qualifiedPattern = this.locator.getQualifiedPattern(this.locator.pattern.simpleName, this.locator.pattern.qualification);
+ // we were imported as qualified name...
+ int level3 = this.resolveLevelForTypeSourceName(qualifiedPattern, qn.toString().toCharArray(), null);
+ if( level3 == ACCURATE_MATCH ) {
+ return INACCURATE_MATCH;
+ }
+ return INACCURATE_MATCH;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ private int resolveLevelForTypeBinding(org.eclipse.jdt.core.dom.ASTNode node, ITypeBinding typeBinding,
+ MatchLocator locator) {
+ int newLevel = this.resolveLevelForType(this.locator.pattern.simpleName, this.locator.pattern.qualification, typeBinding);
+ if( newLevel == IMPOSSIBLE_MATCH ) {
+ String qualNameFromBinding = typeBinding.getQualifiedName();
+ int simpleNameMatch = resolveLevelForSimpleName(node, qualNameFromBinding);
+ if( simpleNameMatch != -1 ) {
+ return simpleNameMatch;
+ }
+ }
+ if( this.locator.isDeclarationOfReferencedTypesPattern) {
+ IJavaElement enclosing = ((DeclarationOfReferencedTypesPattern)this.locator.pattern).enclosingElement;
+ // We don't add this node. We manually add the declaration
+ ITypeBinding t2 = typeBinding.getTypeDeclaration();
+ IJavaElement je = t2 == null ? null : t2.getJavaElement();
+ if( je != null && !this.foundElements.contains(je) && DOMASTNodeUtils.isWithinRange(node, enclosing)) {
+ ISourceReference sr = je instanceof ISourceReference ? (ISourceReference)je : null;
+ IResource r = null;
+ ISourceRange srg = null;
+ ISourceRange nameRange = null;
+ try {
+ srg = sr.getSourceRange();
+ nameRange = sr.getNameRange();
+ IJavaElement ancestor = je.getAncestor(IJavaElement.COMPILATION_UNIT);
+ r = ancestor == null ? null : ancestor.getCorrespondingResource();
+ } catch(JavaModelException jme) {
+ // ignore
+ }
+ ISourceRange rangeToUse = (nameRange == null) ? srg : nameRange;
+ if( rangeToUse != null ) {
+ TypeDeclarationMatch tdm = new TypeDeclarationMatch(je, newLevel,
+ rangeToUse.getOffset(), rangeToUse.getLength(),
+ locator.getParticipant(), r);
+ try {
+ this.foundElements.add(je);
+ locator.report(tdm);
+ } catch(CoreException ce) {
+ // ignore
+ }
+ }
+ }
+ return IMPOSSIBLE_MATCH;
+ }
+ return newLevel;
+ }
+
+ private org.eclipse.jdt.core.dom.CompilationUnit findCU(org.eclipse.jdt.core.dom.ASTNode node) {
+ if( node == null )
+ return null;
+ if( node instanceof org.eclipse.jdt.core.dom.CompilationUnit cu) {
+ return cu;
+ }
+ return findCU(node.getParent());
+ }
+
+ public int match(SimpleName name, NodeSetWrapper nodeSet) {
+ String simpleName = name.getIdentifier();
+ return simpleName != null && this.locator.matchesName(this.locator.pattern.simpleName, simpleName.toCharArray()) ?
+ POSSIBLE_MATCH : IMPOSSIBLE_MATCH;
+ }
+ public int match(QualifiedName name, NodeSetWrapper nodeSet) {
+ String simpleName = name.getName().getIdentifier();
+ String qualifier = name.getQualifier().toString();
+ if( this.locator.pattern.qualification == null ) {
+ // Return an impossible match here, because we are not seeking a qualifier.
+ // The SimpleName node should be the one to respond.
+ return IMPOSSIBLE_MATCH;
+ }
+ if( qualifier != null) {
+ String desiredQualifier = new String(this.locator.pattern.qualification);
+ if( !qualifier.equals(desiredQualifier)) {
+ return IMPOSSIBLE_MATCH;
+ }
+ }
+ return simpleName != null && this.locator.matchesName(this.locator.pattern.simpleName, simpleName.toCharArray()) ?
+ POSSIBLE_MATCH : IMPOSSIBLE_MATCH;
+ }
+ protected int resolveLevelForType(ITypeBinding typeBinding) {
+ if (typeBinding == null) {
+ if (this.locator.pattern.typeSuffix != IIndexConstants.TYPE_SUFFIX) return INACCURATE_MATCH;
+ } else {
+ switch (this.locator.pattern.typeSuffix) {
+ case IIndexConstants.CLASS_SUFFIX:
+ if (!typeBinding.isClass()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.CLASS_AND_INTERFACE_SUFFIX:
+ if (!(typeBinding.isClass() || (typeBinding.isInterface() && !typeBinding.isAnnotation()))) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.CLASS_AND_ENUM_SUFFIX:
+ if (!(typeBinding.isClass() || typeBinding.isEnum())) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.INTERFACE_SUFFIX:
+ if (!typeBinding.isInterface() || typeBinding.isAnnotation()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.INTERFACE_AND_ANNOTATION_SUFFIX:
+ if (!(typeBinding.isInterface() || typeBinding.isAnnotation())) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.ENUM_SUFFIX:
+ if (!typeBinding.isEnum()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.ANNOTATION_TYPE_SUFFIX:
+ if (!typeBinding.isAnnotation()) return IMPOSSIBLE_MATCH;
+ break;
+ case IIndexConstants.TYPE_SUFFIX : // nothing
+ }
+ }
+ return this.resolveLevelForType(this.locator.pattern.simpleName,
+ this.locator.pattern.qualification,
+ typeBinding);
+ }
+
+}
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/NodeSetWrapper.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/NodeSetWrapper.java
new file mode 100644
index 00000000000..2e6b5ae0c54
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/NodeSetWrapper.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2024 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.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.core.search.SearchPattern;
+
+public class NodeSetWrapper {
+ private MatchingNodeSet wrapped;
+ private Set possibleASTNodes = new LinkedHashSet<>();
+ public final Map trustedASTNodeLevels = new LinkedHashMap<>();
+ public NodeSetWrapper(MatchingNodeSet set) {
+ this.wrapped = set;
+ }
+
+ public MatchingNodeSet getWrapped() {
+ return wrapped;
+ }
+
+ 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, MatchingNodeSet.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, MatchingNodeSet.ERASURE_MATCH);
+ }
+ break;
+ case PatternLocator.ACCURATE_MATCH:
+ if (matchLevel != maskedLevel) {
+ addTrustedMatch(node, Integer.valueOf(SearchMatch.A_ACCURATE+(matchLevel & PatternLocator.FLAVORS_MASK)));
+ } else {
+ addTrustedMatch(node, MatchingNodeSet.EXACT_MATCH);
+ }
+ break;
+ }
+ return matchLevel;
+ }
+ public void addPossibleMatch(org.eclipse.jdt.core.dom.ASTNode node) {
+ this.possibleASTNodes.add(node);
+ }
+ public void addTrustedMatch(org.eclipse.jdt.core.dom.ASTNode node, boolean isExact) {
+ addTrustedMatch(node, isExact ? MatchingNodeSet.EXACT_MATCH : MatchingNodeSet.POTENTIAL_MATCH);
+ }
+ void addTrustedMatch(org.eclipse.jdt.core.dom.ASTNode node, Integer level) {
+ this.trustedASTNodeLevels.put(node, level);
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/SearchMatchingUtility.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/SearchMatchingUtility.java
new file mode 100644
index 00000000000..6aa870395fc
--- /dev/null
+++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/SearchMatchingUtility.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2024 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.core.runtime.CoreException;
+import org.eclipse.jdt.core.search.SearchMatch;
+
+/*
+ * Hold Utility methods for package-private functionality
+ */
+public class SearchMatchingUtility {
+ public static void reportSearchMatch(MatchLocator locator, SearchMatch match) throws CoreException {
+ locator.report(match);
+ }
+}