Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private static SearchPattern mapLocationToSearchPatternLocation(int location, St
*/
private static SearchPattern getPatternSingleQuery(int location, String query) throws Exception {
var pattern = SearchPattern.R_PATTERN_MATCH;
if ((!query.contains("?") || !query.contains("*")) && (location != 11)) {
if ((!query.contains("?") && !query.contains("*")) && (location != 11)) {
logInfo("Using full match");
pattern = SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
}
Expand Down Expand Up @@ -376,7 +376,8 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri

// Now run ImportScanner only on units in scope
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
Pattern regex = Pattern.compile(query);
// when creating the regex, replace * with .*
Pattern regex = Pattern.compile(query.replaceAll("(?<!\\.)\\*", ".*"));
for (ICompilationUnit unit : units) {
parser.setSource(unit);
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,142 @@
import static org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin.logInfo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import io.konveyor.tackle.core.internal.query.AnnotationQuery;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.internal.core.ResolvedSourceField;
import org.eclipse.jdt.internal.core.ResolvedSourceMethod;
import org.eclipse.jdt.internal.core.ResolvedSourceType;
import org.eclipse.jdt.internal.core.SourceRefElement;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;

public class AnnotationSymbolProvider implements SymbolProvider, WithAnnotationQuery {
import io.konveyor.tackle.core.internal.query.AnnotationQuery;
import io.konveyor.tackle.core.internal.symbol.CustomASTVisitor.QueryLocation;

public class AnnotationSymbolProvider implements SymbolProvider, WithQuery, WithAnnotationQuery {

private AnnotationQuery annotationQuery;
private String query;

private static final List<Class<? extends SourceRefElement>> ACCEPTED_CLASSES = new ArrayList<>();
static {
ACCEPTED_CLASSES.add(ResolvedSourceMethod.class);
ACCEPTED_CLASSES.add(ResolvedSourceField.class);
ACCEPTED_CLASSES.add(ResolvedSourceType.class);
}

@Override
public List<SymbolInformation> get(SearchMatch match) throws CoreException {
List<SymbolInformation> symbols = new ArrayList<>();
try {
IAnnotatable mod = (IAnnotatable) match.getElement();
IJavaElement element = (IJavaElement) match.getElement();
for (IAnnotation annotation : mod.getAnnotations()) {
IAnnotatable annotatable = (IAnnotatable) match.getElement();
for (IAnnotation annotation : annotatable.getAnnotations()) {
IJavaElement annotationElement = annotation.getPrimaryElement();
SymbolInformation symbol = new SymbolInformation();
symbol.setName(annotation.getElementName());
symbol.setKind(convertSymbolKind(element));
symbol.setKind(convertSymbolKind(annotationElement));
symbol.setContainerName(annotation.getParent().getElementName());
symbol.setLocation(getLocation(element, match));

if (annotationQuery != null) {
List<Class<? extends SourceRefElement>> classes = new ArrayList<>();
classes.add(ResolvedSourceMethod.class);
classes.add(ResolvedSourceField.class);
classes.add(ResolvedSourceType.class);
if (matchesAnnotationQuery(match, classes)) {
symbols.add(symbol);
Location location = getLocation(annotationElement, match);
symbol.setLocation(location);
if (this.query.contains(".")) {
// First try to get compilation unit for source files
ICompilationUnit unit = (ICompilationUnit) annotationElement.getAncestor(IJavaElement.COMPILATION_UNIT);
if (unit == null) {
// If not in source, try to get class file for compiled classes
IClassFile cls = (IClassFile) annotationElement.getAncestor(IJavaElement.CLASS_FILE);
if (cls != null) {
unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null);
}
}
if (unit != null) {
IType t = unit.getType(annotationElement.getElementName());
String fqdn = "";
if (!t.isResolved()) {
var elements = unit.codeSelect(match.getOffset(), match.getLength());
for (IJavaElement e: Arrays.asList(elements)) {
if (e instanceof IType) {
var newT = (IType) e;
if (newT.isResolved()) {
fqdn = newT.getFullyQualifiedName('.');
logInfo("FQDN from code select: " + fqdn);
}
}
}
} else {
fqdn = t.getFullyQualifiedName('.');
logInfo("resolved type: " + fqdn);
}
if (query.matches(fqdn) || fqdn.matches(query)) {
if (unit.isWorkingCopy()) {
unit.discardWorkingCopy();
unit.close();
}

if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) {
symbols.add(symbol);
}
return symbols;
}
}

logInfo("falling back to resolving via AST");

if (this.queryQualificationMatches(this.query.replaceAll("\\(([A-Za-z_][A-Za-z0-9_]*(\\|[A-Za-z_][A-Za-z0-9_]*)*)\\)", ".*"), annotationElement, unit, location)) {
ASTParser astParser = ASTParser.newParser(AST.getJLSLatest());
astParser.setSource(unit);
astParser.setResolveBindings(true);
CompilationUnit cu = (CompilationUnit) astParser.createAST(null);
CustomASTVisitor visitor = new CustomASTVisitor(query, match, QueryLocation.ANNOTATION);
// Under tests, resolveConstructorBinding will return null if there are problems
IProblem[] problems = cu.getProblems();
if (problems != null && problems.length > 0) {
logInfo("KONVEYOR_LOG: " + "Found " + problems.length + " problems while compiling");
int count = 0;
for (IProblem problem : problems) {
logInfo("KONVEYOR_LOG: Problem - ID: " + problem.getID() + " Message: " + problem.getMessage());
count++;
if (count >= SymbolProvider.MAX_PROBLEMS_TO_LOG) {
logInfo("KONVEYOR_LOG: Only showing first " + SymbolProvider.MAX_PROBLEMS_TO_LOG + " problems, " + (problems.length - SymbolProvider.MAX_PROBLEMS_TO_LOG) + " more not displayed");
break;
}
}
}
cu.accept(visitor);
if (visitor.symbolMatches()) {
if (annotationQuery != null) {
if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) {
symbols.add(symbol);
}
} else {
symbols.add(symbol);
}
}
}
if (unit != null && unit.isWorkingCopy()) {
unit.discardWorkingCopy();
unit.close();
}
} else {
symbols.add(symbol);
if (annotationQuery != null) {
if (matchesAnnotationQuery(match, ACCEPTED_CLASSES)) {
symbols.add(symbol);
}
} else {
symbols.add(symbol);
}
}
}
return symbols;
Expand All @@ -60,4 +155,9 @@ public AnnotationQuery getAnnotationQuery() {
public void setAnnotationQuery(AnnotationQuery annotationQuery) {
this.annotationQuery = annotationQuery;
}

@Override
public void setQuery(String query) {
this.query = query;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public List<SymbolInformation> get(SearchMatch match) throws CoreException {
unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null);
}
}
if (this.queryQualificationMatches(this.query, unit, location)) {
if (this.queryQualificationMatches(this.query, mod, unit, location)) {
ASTParser astParser = ASTParser.newParser(AST.getJLSLatest());
astParser.setSource(unit);
astParser.setResolveBindings(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.search.SearchMatch;

/*
Expand All @@ -32,6 +37,7 @@ public class CustomASTVisitor extends ASTVisitor {
public enum QueryLocation {
METHOD_CALL,
CONSTRUCTOR_CALL,
ANNOTATION,
}

public CustomASTVisitor(String query, SearchMatch match, QueryLocation location) {
Expand Down Expand Up @@ -62,6 +68,64 @@ private boolean shouldVisit(ASTNode node) {
(node.getStartPosition() + node.getLength());
}

@Override
public boolean visit(MarkerAnnotation node) {
return visit((Annotation) node);
}

@Override
public boolean visit(NormalAnnotation node) {
return visit((Annotation) node);
}

@Override
public boolean visit(SingleMemberAnnotation node) {
return visit((Annotation) node);
}

private boolean visit(Annotation node) {
// There is a problem with trying to run shouldVisit() here because
// matches on annotations aren't directly on the annotation node,
// but on the annotated one (class, method, field, etc). So we can't
// use shouldVisit() to filter out nodes we don't want to visit.
// TODO: think of a better way to handle this
if (this.location != QueryLocation.ANNOTATION) {
return true;
}
try {
IAnnotationBinding binding = node.resolveAnnotationBinding();
if (binding != null) {
// get fqn
ITypeBinding declaringClass = binding.getAnnotationType();
if (declaringClass != null) {
// Handle Erasure results
if (declaringClass.getErasure() != null) {
declaringClass = declaringClass.getErasure();
}
String fullyQualifiedName = declaringClass.getQualifiedName();
// match fqn with query pattern
if (fullyQualifiedName.matches(this.query)) {
this.symbolMatches = true;
return false;
} else {
logInfo("method fqn " + fullyQualifiedName + " did not match with " + query);
return true;
}
}
}
logInfo("failed to get accurate info for MethodInvocation, falling back");
// sometimes binding or declaring class cannot be found, usually due to errors
// in source code. in that case, we will fallback and accept the match
this.symbolMatches = true;
return false;
} catch (Exception e) {
logInfo("KONVEYOR_LOG: error visiting MethodInvocation node: " + e);
// this is so that we fallback and don't lose a match when we fail
this.symbolMatches = true;
return false;
}
}

/*
* This is to get information from a MethodInvocation, used for METHOD_CALL
* we discard a match only when we can tell for sure. otherwise we accept
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public List<SymbolInformation> get(SearchMatch match) {
unit = cls.getWorkingCopy(new WorkingCopyOwnerImpl(), null);
}
}
if (this.queryQualificationMatches(this.query, unit, location)) {
if (this.queryQualificationMatches(this.query, e, unit, location)) {
ASTParser astParser = ASTParser.newParser(AST.getJLSLatest());
astParser.setSource(unit);
astParser.setResolveBindings(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private static void setPosition(Position position, int[] coords) {
* 3. the compilation unit has a package declaration as `konveyor.io.Util`
* we do this so that we can rule out a lot of matches before going the AST route
*/
default boolean queryQualificationMatches(String query, ICompilationUnit unit, Location location) {
default boolean queryQualificationMatches(String query, IJavaElement matchedElement,ICompilationUnit unit, Location location) {
// Make sure that the ICompilationUnit is conistant
try {
unit.makeConsistent(null);
Expand All @@ -206,6 +206,14 @@ default boolean queryQualificationMatches(String query, ICompilationUnit unit, L
// for a query, java.io.paths.File*, queryQualification is java.io.paths
queryQualification = query.substring(0, dotIndex);
}
// an element need not be imported if its referenced by fqn
if (!queryQualification.isEmpty() && (
matchedElement.getElementName().equals(queryQualification)
|| matchedElement.getElementName().startsWith(queryQualification + ".")
|| queryQualification.matches(matchedElement.getElementName())
)) {
return true;
}
String packageQueryQualification = "";
int packageDotIndex = queryQualification.lastIndexOf('.');
if (packageDotIndex > 0) {
Expand Down
6 changes: 3 additions & 3 deletions java-analyzer-bundle.tp/java-analyzer-bundle.target
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<?pde version="3.8"?>
<target name="Java Analyzer Target Platform">
<locations>
<!-- 1st: pick up latest JDT.LS bits
<!-- 1st: pick up JDT.LS bits (1.35.0)
-->
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner"
includeSource="true" type="InstallableUnit">
<unit id="org.eclipse.jdt.ls.core" version="0.0.0" />
<repository location="https://download.eclipse.org/jdtls/milestones/1.40.0/repository"/>
<repository location="https://download.eclipse.org/jdtls/milestones/1.35.0/repository"/>
</location>


Expand Down Expand Up @@ -64,7 +64,7 @@
<repository location="https://download.eclipse.org/releases/2021-09/202109151000/"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
<repository location="https://download.eclipse.org/lsp4j/updates/releases/0.23.1/"/>
<repository location="https://download.eclipse.org/lsp4j/updates/releases/0.22.0/"/>
<unit id="org.eclipse.lsp4j.sdk.feature.group" version="0.0.0"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
Expand Down
Loading