Skip to content

Commit

Permalink
Make MatchLocator capable of working with DOM
Browse files Browse the repository at this point in the history
Extra strategy in MatchLocator to use DOM, and tweaks in various
Locators to properly handle DOM. Tests in JavaSearchTests help a lot
(currently ~130/270 passing)

It basically clones and adapt some methods and adapt them to DOM,
A new flag is introduced to decide the strategy.

Done-ish (often with remaining issues)
* FieldLocator
* LocalVariableLocator
* MethodLocator
* SuperTypeReferenceLocator
* TypeDeclarationLocator
* TypeParameterLocator
* TypeReferenceLocator
TODO:
* AndLocator
* ModuleLocator
* PackageDeclarationLocator
* PackageReferenceLocator
* OrLocator
* VariableLocator

# Conflicts:
#	org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaSearchTests.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/FieldLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/LocalVariableLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MethodLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PackageReferenceLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PatternLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/SuperTypeReferenceLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeParameterLocator.java
#	org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/TypeReferenceLocator.java
  • Loading branch information
mickaelistria committed Oct 8, 2024
1 parent 55d99cd commit 97d6f5b
Show file tree
Hide file tree
Showing 15 changed files with 2,003 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
Expand Down Expand Up @@ -910,7 +912,12 @@ protected void assertSearchResults(String expected, JavaSearchResultCollector co
assertSearchResults("Unexpected search results", expected, collector);
}
protected void assertSearchResults(String message, String expected, JavaSearchResultCollector collector) {
String actual = collector.toString();
String actual = Arrays.stream(collector.toString().split("\n"))
.sorted()
.collect(Collectors.joining("\n")); // order doesn't matter
expected = Arrays.stream(expected.split("\n"))
.sorted()
.collect(Collectors.joining("\n")); // order doesn't matter
if (!expected.equals(actual)) {
if (this.displayName) {
System.out.print(getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,37 @@
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;

import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CreationReference;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
Expand Down Expand Up @@ -58,6 +82,17 @@ public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) {
return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
@Override
public int match(MethodDeclaration node, MatchingNodeSet nodeSet) {
if (!node.isConstructor()) {
return IMPOSSIBLE_MATCH;
}
if (this.pattern.fineGrain != 0 && !this.pattern.findDeclarations) return IMPOSSIBLE_MATCH;
int referencesLevel = /* this.pattern.findReferences ? matchLevelForReferences(node) : */IMPOSSIBLE_MATCH;
int declarationsLevel = this.pattern.findDeclarations ? matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH;

return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger match
}
@Override
public int match(Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression
if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
if (!(node instanceof AllocationExpression)) return IMPOSSIBLE_MATCH;
Expand All @@ -73,6 +108,52 @@ public int match(Expression node, MatchingNodeSet nodeSet) { // interested in Al
return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
}
@Override
public int match(org.eclipse.jdt.core.dom.Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression
if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
if (node instanceof CreationReference creationRef && (this.pattern.declaringSimpleName == null || matchesTypeReference(this.pattern.declaringSimpleName, creationRef.getType()))) {
return this.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH;
}
if (node instanceof ClassInstanceCreation newInstance) {
return (this.pattern.declaringSimpleName == null || matchesTypeReference(this.pattern.declaringSimpleName, newInstance.getType()))
&& matchParametersCount(node, newInstance.arguments()) ?
POSSIBLE_MATCH : IMPOSSIBLE_MATCH;
}
return IMPOSSIBLE_MATCH;
}
@Override
public int match(org.eclipse.jdt.core.dom.ASTNode node, MatchingNodeSet nodeSet) {
if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
if (node instanceof SuperConstructorInvocation superRef) {
if (!matchParametersCount(node, superRef.arguments())) {
return IMPOSSIBLE_MATCH;
}
if (this.pattern.declaringSimpleName != null) {
Type superType = null;
var current = superRef.getParent();
while (current != null && !(current instanceof AbstractTypeDeclaration) && !(current instanceof CreationReference)) {
current = current.getParent();
}
if (current instanceof org.eclipse.jdt.core.dom.TypeDeclaration typeDecl) {
superType = typeDecl.getSuperclassType();
}
if (current instanceof CreationReference newInstance) {
superType = newInstance.getType();
}
if (!matchesTypeReference(this.pattern.declaringSimpleName, superType)) {
return IMPOSSIBLE_MATCH;
}
}
return this.pattern.mustResolve ? POSSIBLE_MATCH : INACCURATE_MATCH;
}
if (node instanceof EnumConstantDeclaration enumConstantDecl
&& node.getParent() instanceof EnumDeclaration enumDeclaration
&& matchesName(this.pattern.declaringSimpleName, enumDeclaration.getName().getIdentifier().toCharArray())
&& matchParametersCount(enumConstantDecl, enumConstantDecl.arguments())) {
return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
}
return IMPOSSIBLE_MATCH;
}
@Override
public int match(FieldDeclaration field, MatchingNodeSet nodeSet) {
if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
// look only for enum constant
Expand Down Expand Up @@ -151,6 +232,34 @@ protected int matchConstructor(MethodBinding constructor) {
}
return level;
}
protected int matchConstructor(IMethodBinding constructor) {
if (!constructor.isConstructor()) return IMPOSSIBLE_MATCH;

// declaring type, simple name has already been matched by matchIndexEntry()
int level = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification, constructor.getDeclaringClass());
if (level == IMPOSSIBLE_MATCH) return IMPOSSIBLE_MATCH;

// parameter types
int parameterCount = this.pattern.parameterCount;
if (parameterCount > -1) {
if (parameterCount != constructor.getParameterTypes().length) return IMPOSSIBLE_MATCH;
for (int i = 0; i < parameterCount; i++) {
// TODO (frederic) use this call to refine accuracy on parameter types
// int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, constructor.parameters[i]);
int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], constructor.getParameterTypes()[i]);
if (level > newLevel) {
if (newLevel == IMPOSSIBLE_MATCH) {
// if (isErasureMatch) {
// return ERASURE_MATCH;
// }
return IMPOSSIBLE_MATCH;
}
level = newLevel; // can only be downgraded
}
}
}
return level;
}
@Override
protected int matchContainer() {
if (this.pattern.findReferences) return ALL_CONTAINER; // handles both declarations + references & just references
Expand Down Expand Up @@ -194,6 +303,25 @@ protected int matchLevelForDeclarations(ConstructorDeclaration constructor) {

return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
}
protected int matchLevelForDeclarations(MethodDeclaration constructor) {
// constructor name is stored in selector field
if (this.pattern.declaringSimpleName != null && !matchesName(this.pattern.declaringSimpleName, constructor.getName().toString().toCharArray()))
return IMPOSSIBLE_MATCH;

if (this.pattern.parameterSimpleNames != null) {
int length = this.pattern.parameterSimpleNames.length;
var args = constructor.parameters();
int argsLength = args == null ? 0 : args.size();
if (length != argsLength) return IMPOSSIBLE_MATCH;
}

// Verify type arguments (do not reject if pattern has no argument as it can be an erasure match)
if (this.pattern.hasConstructorArguments()) {
if (constructor.typeParameters() == null || constructor.typeParameters().size() != this.pattern.constructorArguments.length) return IMPOSSIBLE_MATCH;
}

return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
}
boolean matchParametersCount(ASTNode node, Expression[] args) {
if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) {
int length = this.pattern.parameterCount;
Expand All @@ -205,6 +333,17 @@ boolean matchParametersCount(ASTNode node, Expression[] args) {
}
return true;
}
boolean matchParametersCount(org.eclipse.jdt.core.dom.ASTNode node, List<org.eclipse.jdt.core.dom.Expression> args) {
if (this.pattern.parameterSimpleNames != null && (!this.pattern.varargs || DOMASTNodeUtils.insideDocComment(node))) {
int length = this.pattern.parameterCount;
if (length < 0) length = this.pattern.parameterSimpleNames.length;
int argsLength = args == null ? 0 : args.size();
if (length != argsLength) {
return false;
}
}
return true;
}
@Override
protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding, int accuracy, MatchLocator locator) throws CoreException {

Expand Down Expand Up @@ -374,6 +513,19 @@ public int resolveLevel(Binding binding) {
}
return level;
}
@Override
public int resolveLevel(IBinding binding) {
if (binding instanceof IMethodBinding constructor) {
int level= matchConstructor(constructor);
if (level== IMPOSSIBLE_MATCH) {
if (constructor != constructor.getMethodDeclaration()) {
level= matchConstructor(constructor.getMethodDeclaration());
}
}
return level;
}
return IMPOSSIBLE_MATCH;
}
protected int resolveLevel(ConstructorDeclaration constructor, boolean checkDeclarations) {
int referencesLevel = IMPOSSIBLE_MATCH;
if (this.pattern.findReferences) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;

import java.util.List;
import java.util.Optional;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.MethodRef;
import org.eclipse.jdt.core.dom.MethodReference;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SuperMethodReference;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclaration;

public class DOMASTNodeUtils {

public static IJavaElement getEnclosingJavaElement(ASTNode node) {
if (node == null) {
return null;
}
if (node instanceof AbstractTypeDeclaration
|| node instanceof MethodDeclaration
|| node instanceof VariableDeclaration
|| node instanceof CompilationUnit
|| node instanceof AnnotationTypeMemberDeclaration) {
return getDeclaringJavaElement(node);
}
return getEnclosingJavaElement(node.getParent());
}

public static IJavaElement getDeclaringJavaElement(ASTNode key) {
if (key instanceof CompilationUnit unit) {
return unit.getJavaElement();
}
return Optional.ofNullable(key).map(DOMASTNodeUtils::getBinding).map(IBinding::getJavaElement).orElse(null);
}

private static IBinding getBinding(ASTNode astNode) {
if (astNode instanceof Name name) {
return name.resolveBinding();
}
if (astNode instanceof VariableDeclaration variable) {
return variable.resolveBinding();
}
if (astNode instanceof EnumConstantDeclaration enumConstantDeclaration) {
return enumConstantDeclaration.resolveVariable();
}
if (astNode instanceof FieldAccess fieldAcces) {
return fieldAcces.resolveFieldBinding();
}
if (astNode instanceof MethodInvocation method) {
return method.resolveMethodBinding();
}
if (astNode instanceof Type type) {
return type.resolveBinding();
}
if (astNode instanceof AbstractTypeDeclaration type) {
return type.resolveBinding();
}
if (astNode instanceof MethodDeclaration method) {
return method.resolveBinding();
}
if (astNode instanceof SuperFieldAccess superField) {
return superField.resolveFieldBinding();
}
if (astNode instanceof SuperMethodInvocation superMethod) {
return superMethod.resolveMethodBinding();
}
if (astNode instanceof SuperMethodReference superRef) {
return superRef.resolveMethodBinding();
}
if (astNode instanceof MethodRef methodRef) {
return methodRef.resolveBinding();
}
if (astNode instanceof MethodReference methodRef) {
return methodRef.resolveMethodBinding();
}
// TODO more...
return null;
}

public static boolean insideDocComment(org.eclipse.jdt.core.dom.ASTNode node) {
return node.getRoot() instanceof org.eclipse.jdt.core.dom.CompilationUnit unit &&
((List<Comment>)unit.getCommentList()).stream().anyMatch(comment -> comment.getStartPosition() <= node.getStartPosition() && comment.getStartPosition() + comment.getLength() >= node.getStartPosition() + node.getLength());
}
}
Loading

0 comments on commit 97d6f5b

Please sign in to comment.