Skip to content

Commit 9f180ff

Browse files
committed
fix a minor edge case and add test case for method call
Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>
1 parent 826751c commit 9f180ff

File tree

5 files changed

+192
-27
lines changed

5 files changed

+192
-27
lines changed

java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/CustomASTVisitor.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,22 @@ public CustomASTVisitor(String query, SearchMatch match, QueryLocation location)
7373
/*
7474
* When visiting AST nodes, it may happen that we visit more nodes than
7575
* needed. We need to ensure that we are only visiting ones that are found
76-
* in the given search match. I wrote this for methods / constructors where
77-
* I observed that node starts at the beginning of line whereas match starts
78-
* at an offset within that line. However, both end on the same position. This
79-
* could differ for other locations. In that case, change logic based on type of
80-
* the node you get.
76+
* in the given search match.
77+
*
78+
* The match offset/length points to the specific code location (e.g., the method
79+
* call), while the AST node may have slightly different boundaries. We check if
80+
* the match falls within the node's range to handle cases like qualified method
81+
* references where the SearchMatch element is the containing method but the
82+
* offset points to the actual call site.
8183
*/
8284
private boolean shouldVisit(ASTNode node) {
83-
return (this.match.getOffset() + this.match.getLength()) ==
84-
(node.getStartPosition() + node.getLength());
85+
int matchStart = this.match.getOffset();
86+
int matchEnd = matchStart + this.match.getLength();
87+
int nodeStart = node.getStartPosition();
88+
int nodeEnd = nodeStart + node.getLength();
89+
boolean result = (matchStart >= nodeStart && matchEnd <= nodeEnd) ||
90+
(matchEnd == nodeEnd);
91+
return result;
8592
}
8693

8794
@Override

java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/symbol/SymbolProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ default boolean queryQualificationMatches(String query, IJavaElement matchedElem
208208
// e.g. java.nio.file.Paths.get(String)/java.nio.file.Paths.get(*) -> java.nio.file.Paths.get
209209
// Remove any parentheses and their contents
210210
query = query.replaceAll("\\([^|]*\\)", "");
211-
query = query.replaceAll("(?<!\\.)\\*", ".*");
211+
212212
String queryQualification = "";
213213
int dotIndex = query.lastIndexOf('.');
214214
if (dotIndex > 0) {
215215
// for a query, java.io.paths.File*, queryQualification is java.io.paths
216216
queryQualification = query.substring(0, dotIndex);
217217
}
218+
219+
query = query.replaceAll("(?<!\\.)\\*", ".*");
218220
// an element need not be imported if its referenced by fqn
219221
if (!queryQualification.isEmpty() && (
220222
matchedElement.getElementName().equals(queryQualification)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.konveyor.tackle.core.internal.symbol;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import java.util.List;
7+
8+
import org.eclipse.lsp4j.SymbolInformation;
9+
import org.junit.Test;
10+
11+
import io.konveyor.tackle.core.internal.testing.AbstractSymbolProviderTest;
12+
13+
14+
public class MethodCallSymbolProviderIntegrationTest extends AbstractSymbolProviderTest {
15+
16+
@Test
17+
public void testFullyQualifiedMethodCalls() {
18+
List<SymbolInformation> results = searchMethodCalls("io.konveyor.demo.PackageUsageExample.merge");
19+
results = inFile(results, "SampleApplication.java");
20+
printResults(results);
21+
assertTrue("[1] Should find usage of merge() call in test-project/SampleApplication.java#callFullyQualifiedMethod()",
22+
results.size() == 1);
23+
24+
List<SymbolInformation> entityManagerMergeResults = searchMethodCalls("javax.persistence.EntityManager.merge");
25+
entityManagerMergeResults = inFile(entityManagerMergeResults, "PackageUsageExample.java");
26+
printResults(entityManagerMergeResults);
27+
assertTrue("[2] Should find usage of entityManager.merge() call in test-project/PackageUsageExample.java#merge()",
28+
entityManagerMergeResults.size() == 1);
29+
30+
// Half qualified method call should not work
31+
List<SymbolInformation> halfQualifiedResults = searchMethodCalls("PackageUsageExample.merge");
32+
printResults(halfQualifiedResults);
33+
assertTrue("[3] Should not find any results matching PackageUsageExample.merge",
34+
halfQualifiedResults.size() == 0);
35+
36+
// make sure patterns for fully qualified method calls work
37+
List<SymbolInformation> mergeStarResults = searchMethodCalls("io.konveyor.demo.PackageUsageExample.merg*");
38+
printResults(mergeStarResults);
39+
assertTrue("[4] Should find 1 result matching io.konveyor.demo.PackageUsageExample.merg*",
40+
mergeStarResults.size() == 1);
41+
}
42+
43+
44+
@Test
45+
public void testNonQualifiedMethodCalls() {
46+
List<SymbolInformation> allResults = searchMethodCalls("merge");
47+
48+
List<SymbolInformation> sampleAppResults = inFile(allResults, "SampleApplication.java");
49+
printResults(sampleAppResults);
50+
assertTrue("[1] Should find usage of merge() call in test-project/SampleApplication.java#callFullyQualifiedMethod()",
51+
sampleAppResults.size() == 1);
52+
53+
List<SymbolInformation> packageExampleResults = inFile(allResults, "PackageUsageExample.java");
54+
printResults(packageExampleResults);
55+
assertTrue("[2] Should find usage of merge() call in test-project/PackageUsageExample.java#merge()",
56+
packageExampleResults.size() == 1);
57+
58+
// make sure pattterns work
59+
List<SymbolInformation> mergeStarResults = searchMethodCalls("merg*");
60+
printResults(mergeStarResults);
61+
assertTrue("[3] Should find usage of merge() call in test-project/SampleApplication.java#callFullyQualifiedMethod() and test-project/PackageUsageExample.java#merge()",
62+
mergeStarResults.size() == 2);
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package io.konveyor.tackle.core.internal.symbol;
2+
3+
import static org.junit.Assert.assertTrue;
4+
5+
import java.util.List;
6+
7+
import org.eclipse.lsp4j.SymbolInformation;
8+
import org.junit.Test;
9+
10+
import io.konveyor.tackle.core.internal.testing.AbstractSymbolProviderTest;
11+
12+
13+
public class MethodDeclarationSymbolProviderIntegrationTest extends AbstractSymbolProviderTest {
14+
15+
@Test
16+
public void testFullyQualifiedMethodDeclarations() {
17+
// Test fully qualified method declaration search for SampleApplication.merge
18+
List<SymbolInformation> sampleAppResults = searchMethodDeclarations("io.konveyor.demo.SampleApplication.merge");
19+
printResults(sampleAppResults);
20+
assertTrue("[1] Should find merge() declaration in SampleApplication.java",
21+
sampleAppResults.size() == 1);
22+
assertTrue("[1b] Result should be in SampleApplication.java",
23+
sampleAppResults.get(0).getLocation().getUri().contains("SampleApplication.java"));
24+
25+
// Test fully qualified method declaration search for PackageUsageExample.merge
26+
List<SymbolInformation> packageExampleResults = searchMethodDeclarations("io.konveyor.demo.PackageUsageExample.merge");
27+
printResults(packageExampleResults);
28+
assertTrue("[2] Should find merge() declaration in PackageUsageExample.java",
29+
packageExampleResults.size() == 1);
30+
assertTrue("[2b] Result should be in PackageUsageExample.java",
31+
packageExampleResults.get(0).getLocation().getUri().contains("PackageUsageExample.java"));
32+
33+
// Test fully qualified method declaration search for ServletExample.merge
34+
List<SymbolInformation> servletResults = searchMethodDeclarations("io.konveyor.demo.ServletExample.merge");
35+
printResults(servletResults);
36+
assertTrue("[3] Should find merge() declaration in ServletExample.java",
37+
servletResults.size() == 1);
38+
assertTrue("[3b] Result should be in ServletExample.java",
39+
servletResults.get(0).getLocation().getUri().contains("ServletExample.java"));
40+
41+
// Half qualified method declaration DOES work (different from method calls)
42+
// This is because method declarations are matched by class name + method name
43+
List<SymbolInformation> halfQualifiedResults = searchMethodDeclarations("SampleApplication.merge");
44+
printResults(halfQualifiedResults);
45+
assertTrue("[4] Should find 1 result matching SampleApplication.merge (half-qualified works for declarations)",
46+
halfQualifiedResults.size() == 1);
47+
assertTrue("[4b] Result should be in SampleApplication.java",
48+
halfQualifiedResults.get(0).getLocation().getUri().contains("SampleApplication.java"));
49+
50+
// Test pattern matching with wildcard on class name
51+
List<SymbolInformation> patternResults = searchMethodDeclarations("io.konveyor.demo.*.merge");
52+
printResults(patternResults);
53+
assertTrue("[5] Should find all 3 merge() declarations matching io.konveyor.demo.*.merge",
54+
patternResults.size() == 3);
55+
}
56+
57+
58+
@Test
59+
public void testNonQualifiedMethodDeclarations() {
60+
// Search for all "merge" method declarations
61+
List<SymbolInformation> allResults = searchMethodDeclarations("merge");
62+
printResults(allResults);
63+
64+
// Should find all 3 merge() declarations in the project
65+
assertTrue("[1] Should find all 3 merge() declarations in the project",
66+
allResults.size() == 3);
67+
68+
// Verify each file has exactly one merge() declaration
69+
List<SymbolInformation> sampleAppResults = inFile(allResults, "SampleApplication.java");
70+
assertTrue("[2] Should find exactly 1 merge() declaration in SampleApplication.java",
71+
sampleAppResults.size() == 1);
72+
73+
List<SymbolInformation> packageExampleResults = inFile(allResults, "PackageUsageExample.java");
74+
assertTrue("[3] Should find exactly 1 merge() declaration in PackageUsageExample.java",
75+
packageExampleResults.size() == 1);
76+
77+
List<SymbolInformation> servletResults = inFile(allResults, "ServletExample.java");
78+
assertTrue("[4] Should find exactly 1 merge() declaration in ServletExample.java",
79+
servletResults.size() == 1);
80+
81+
// Test pattern matching with wildcard
82+
List<SymbolInformation> mergeStarResults = searchMethodDeclarations("merg*");
83+
printResults(mergeStarResults);
84+
assertTrue("[5] Should find all 3 merge() declarations matching merg*",
85+
mergeStarResults.size() == 3);
86+
}
87+
88+
89+
@Test
90+
public void testMethodDeclarationPatterns() {
91+
// Test various pattern combinations
92+
93+
// Pattern: method name ending with 'e'
94+
List<SymbolInformation> mergeResults = searchMethodDeclarations("*merge");
95+
printResults(mergeResults);
96+
assertTrue("[1] Should find all 3 merge() declarations matching *merge",
97+
mergeResults.size() == 3);
98+
99+
// Test fully qualified pattern with class wildcard
100+
List<SymbolInformation> demoPackageResults = searchMethodDeclarations("io.konveyor.demo.Sample*.merge");
101+
printResults(demoPackageResults);
102+
assertTrue("[2] Should find merge() in SampleApplication matching io.konveyor.demo.Sample*.merge",
103+
demoPackageResults.size() == 1);
104+
105+
// Test fully qualified pattern with method wildcard
106+
List<SymbolInformation> sampleAllMethods = searchMethodDeclarations("io.konveyor.demo.SampleApplication.*");
107+
printResults(sampleAllMethods);
108+
assertTrue("[3] Should find multiple method declarations in SampleApplication matching io.konveyor.demo.SampleApplication.*",
109+
sampleAllMethods.size() > 1);
110+
}
111+
}

java-analyzer-bundle.test/src/main/java/io/konveyor/tackle/core/internal/testing/SearchTestHelper.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,6 @@
1717
* Provides convenient wrappers around the RuleEntry command with filtering and
1818
* debugging utilities.
1919
*
20-
* <h3>Location Type Reference:</h3>
21-
* <ul>
22-
* <li>0 - DEFAULT (searches across all types)</li>
23-
* <li>1 - INHERITANCE</li>
24-
* <li>2 - METHOD_CALL</li>
25-
* <li>3 - CONSTRUCTOR_CALL</li>
26-
* <li>4 - ANNOTATION</li>
27-
* <li>5 - IMPLEMENTS_TYPE</li>
28-
* <li>6 - ENUM_CONSTANT</li>
29-
* <li>7 - RETURN_TYPE</li>
30-
* <li>8 - IMPORT</li>
31-
* <li>9 - VARIABLE_DECLARATION</li>
32-
* <li>10 - TYPE</li>
33-
* <li>11 - PACKAGE</li>
34-
* <li>12 - FIELD</li>
35-
* <li>13 - METHOD_DECLARATION</li>
36-
* <li>14 - CLASS_DECLARATION</li>
37-
* </ul>
38-
*
3920
* <h3>Usage Example:</h3>
4021
* <pre>{@code
4122
* // Simple search

0 commit comments

Comments
 (0)