From cd5f0e165c801b9857f0225f90370be10cbc3c53 Mon Sep 17 00:00:00 2001 From: Rob Stryker Date: Thu, 6 Feb 2025 14:04:27 -0500 Subject: [PATCH] Move the DOM-based search into the javac bundle Signed-off-by: Rob Stryker --- org.eclipse.jdt.core.javac/fragment.xml | 7 + .../internal/core/search/DOMASTNodeUtils.java | 190 ++++++++++ .../core/search/DOMJavaSearchDelegate.java | 354 ++++++++++++++++++ .../core/search/PatternLocatorVisitor.java | 291 ++++++++++++++ .../matching/DOMConstructorLocator.java | 202 ++++++++++ .../core/search/matching/DOMFieldLocator.java | 277 ++++++++++++++ .../matching/DOMLocalVariableLocator.java | 75 ++++ .../search/matching/DOMMethodLocator.java | 314 ++++++++++++++++ .../matching/DOMPackageReferenceLocator.java | 89 +++++ .../search/matching/DOMPatternLocator.java | 260 +++++++++++++ .../DOMSuperTypeReferenceLocator.java | 91 +++++ .../matching/DOMTypeDeclarationLocator.java | 115 ++++++ .../matching/DOMTypeParameterLocator.java | 121 ++++++ .../matching/DOMTypeReferenceLocator.java | 344 +++++++++++++++++ .../core/search/matching/NodeSetWrapper.java | 72 ++++ .../matching/SearchMatchingUtility.java | 23 ++ 16 files changed, 2825 insertions(+) create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMASTNodeUtils.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/DOMJavaSearchDelegate.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/PatternLocatorVisitor.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMConstructorLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMFieldLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMLocalVariableLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMMethodLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPackageReferenceLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMPatternLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMSuperTypeReferenceLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeDeclarationLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeParameterLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/DOMTypeReferenceLocator.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/NodeSetWrapper.java create mode 100644 org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/core/search/matching/SearchMatchingUtility.java 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); + } +}