Skip to content

Commit 6a0934b

Browse files
committed
Use contributed search participants in call hierarchy
Incoming calls (CallerMethodWrapper): Replace hardcoded `new SearchParticipant[] { getDefaultSearchParticipant() }` with `SearchEngine.getSearchParticipants()` so that incoming call searches include results from contributed search participants registered via the `org.eclipse.jdt.core.searchParticipant` extension point. Outgoing calls (CalleeMethodWrapper): When the member has no Java AST (i.e. `getCompilationUnitNode()` returns null), fall back to `SearchParticipant.locateCallees()` on all contributed participants. Each participant can report call sites within the member's body. Returned callees are resolved to full declarations via `resolveCallee()`, which handles METHOD, FIELD, and TYPE element types. Non-standard ICompilationUnit handling (CallHierarchyCore): Catch `ClassCastException` in `getCompilationUnitNode()` for non-standard `ICompilationUnit` implementations (e.g. from contributed search participants) that do not implement the internal compiler interface (`org.eclipse.jdt.internal.compiler.env.ICompilationUnit`) required by `ASTParser.createAST()`. Returns null to fall through to the participant path. Dependency: Bump `org.eclipse.jdt.core` dependency to `[3.46.0,4.0.0)` for the `SearchEngine.getSearchParticipants()` and `SearchParticipant.locateCallees()` APIs added in eclipse-jdt/eclipse.jdt.core#4938. Depends on eclipse-jdt/eclipse.jdt.core#4938. Companion to eclipse-jdtls/eclipse.jdt.ls#3732.
1 parent aa5da5e commit 6a0934b

8 files changed

Lines changed: 553 additions & 5 deletions

File tree

org.eclipse.jdt.core.manipulation/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Bundle-Localization: plugin
1010
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.31.0,4.0.0)",
1111
org.eclipse.core.resources;bundle-version="[3.20.0,4.0.0)",
1212
org.eclipse.ltk.core.refactoring;bundle-version="[3.14.0,4.0.0)",
13-
org.eclipse.jdt.core;bundle-version="[3.40.0,4.0.0)",
13+
org.eclipse.jdt.core;bundle-version="[3.46.0,4.0.0)",
1414
org.eclipse.core.expressions;bundle-version="[3.9.0,4.0.0)",
1515
org.eclipse.text;bundle-version="[3.14.0,4.0.0)",
1616
org.eclipse.jdt.launching;bundle-version="3.23.0",

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/callhierarchy/CallHierarchyCore.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,18 @@ private static StringMatcher[] parseList(String listString) {
297297
static CompilationUnit getCompilationUnitNode(IMember member, boolean resolveBindings) {
298298
ITypeRoot typeRoot= member.getTypeRoot();
299299
try {
300-
if (typeRoot.exists() && typeRoot.getBuffer() != null) {
300+
if (typeRoot != null && typeRoot.exists() && typeRoot.getBuffer() != null) {
301301
ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
302302
parser.setSource(typeRoot);
303303
parser.setResolveBindings(resolveBindings);
304304
return (CompilationUnit) parser.createAST(null);
305305
}
306306
} catch (JavaModelException e) {
307307
JavaManipulationPlugin.log(e);
308+
} catch (ClassCastException e) {
309+
// Non-standard ITypeRoot (e.g. from a contributed search participant)
310+
// that does not implement the internal compiler interfaces required
311+
// by ASTParser — fall through and return null
308312
}
309313
return null;
310314
}

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/callhierarchy/CalleeMethodWrapper.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@
2020
import java.util.HashMap;
2121
import java.util.Map;
2222

23+
import org.eclipse.core.runtime.CoreException;
2324
import org.eclipse.core.runtime.IProgressMonitor;
2425

26+
import org.eclipse.jdt.core.IJavaElement;
2527
import org.eclipse.jdt.core.IMember;
2628
import org.eclipse.jdt.core.dom.CompilationUnit;
29+
import org.eclipse.jdt.core.search.IJavaSearchConstants;
30+
import org.eclipse.jdt.core.search.SearchEngine;
31+
import org.eclipse.jdt.core.search.SearchMatch;
32+
import org.eclipse.jdt.core.search.SearchParticipant;
33+
import org.eclipse.jdt.core.search.SearchPattern;
34+
35+
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
2736

2837
class CalleeMethodWrapper extends MethodWrapper {
2938
private Comparator<MethodWrapper> fMethodWrapperComparator = new MethodWrapperComparator();
@@ -100,8 +109,90 @@ protected Map<String, MethodCall> findChildren(IProgressMonitor progressMonitor)
100109

101110
cu.accept(visitor);
102111
return visitor.getCallees();
112+
} else {
113+
return findCalleesFromParticipants(member, progressMonitor);
103114
}
104115
}
105116
return new HashMap<>(0);
106117
}
118+
119+
private Map<String, MethodCall> findCalleesFromParticipants(IMember member, IProgressMonitor monitor) {
120+
try {
121+
String path= null;
122+
if (member.getResource() != null) {
123+
path= member.getResource().getFullPath().toString();
124+
} else if (member.getPath() != null) {
125+
path= member.getPath().toString();
126+
}
127+
if (path == null)
128+
return new HashMap<>(0);
129+
130+
CallSearchResultCollector collector= new CallSearchResultCollector();
131+
SearchParticipant[] participants= SearchEngine.getSearchParticipants();
132+
133+
for (SearchParticipant participant : participants) {
134+
SearchMatch[] calleeMatches= participant.locateCallees(
135+
member, participant.getDocument(path), monitor);
136+
137+
for (SearchMatch match : calleeMatches) {
138+
if (match.getElement() instanceof IMember callee) {
139+
IMember resolved= resolveCallee(callee, monitor);
140+
if (resolved != null) {
141+
collector.addMember(member, resolved,
142+
match.getOffset(),
143+
match.getOffset() + match.getLength());
144+
}
145+
}
146+
}
147+
}
148+
return collector.getCallers();
149+
} catch (CoreException e) {
150+
JavaManipulationPlugin.log(e);
151+
return new HashMap<>(0);
152+
}
153+
}
154+
155+
private IMember resolveCallee(IMember callee, IProgressMonitor monitor) {
156+
if (callee.exists()) {
157+
return callee;
158+
}
159+
try {
160+
int searchFor;
161+
switch (callee.getElementType()) {
162+
case IJavaElement.METHOD:
163+
searchFor= IJavaSearchConstants.METHOD;
164+
break;
165+
case IJavaElement.FIELD:
166+
searchFor= IJavaSearchConstants.FIELD;
167+
break;
168+
default:
169+
searchFor= IJavaSearchConstants.TYPE;
170+
break;
171+
}
172+
SearchPattern pattern= SearchPattern.createPattern(
173+
callee.getElementName(), searchFor,
174+
IJavaSearchConstants.DECLARATIONS,
175+
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
176+
if (pattern == null)
177+
return null;
178+
179+
final IMember[] result= { null };
180+
new SearchEngine().search(pattern,
181+
SearchEngine.getSearchParticipants(),
182+
CallHierarchyCore.getDefault().getSearchScope(),
183+
new org.eclipse.jdt.core.search.SearchRequestor() {
184+
@Override
185+
public void acceptSearchMatch(SearchMatch match) {
186+
if (result[0] == null
187+
&& match.getElement() instanceof IMember m) {
188+
result[0]= m;
189+
}
190+
}
191+
}, monitor);
192+
return result[0];
193+
} catch (CoreException e) {
194+
JavaManipulationPlugin.log(e);
195+
return null;
196+
}
197+
}
107198
}

org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/callhierarchy/CallerMethodWrapper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.eclipse.jdt.core.search.IJavaSearchConstants;
3737
import org.eclipse.jdt.core.search.IJavaSearchScope;
3838
import org.eclipse.jdt.core.search.SearchEngine;
39-
import org.eclipse.jdt.core.search.SearchParticipant;
4039
import org.eclipse.jdt.core.search.SearchPattern;
4140

4241
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
@@ -152,7 +151,7 @@ protected Map<String, MethodCall> findChildren(IProgressMonitor progressMonitor)
152151
IJavaSearchScope defaultSearchScope= getSearchScope();
153152
boolean isWorkspaceScope= SearchEngine.createWorkspaceScope().equals(defaultSearchScope);
154153
IJavaSearchScope searchScope= isWorkspaceScope ? getAccurateSearchScope(defaultSearchScope, member) : defaultSearchScope;
155-
searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, searchRequestor,
154+
searchEngine.search(pattern, SearchEngine.getSearchParticipants(), searchScope, searchRequestor,
156155
monitor);
157156
return searchRequestor.getCallers();
158157

org.eclipse.jdt.ui.tests/plugin.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,14 @@
302302
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
303303
</extension>
304304
<!-- End of JavaLeakTest -->
305-
305+
306+
<extension
307+
point="org.eclipse.jdt.core.searchParticipant">
308+
<searchParticipant
309+
class="org.eclipse.jdt.ui.tests.callhierarchy.TestCallHierarchyParticipant"
310+
id="org.eclipse.jdt.ui.tests.testCallHierarchySearchParticipant"
311+
fileExtensions="testlang">
312+
</searchParticipant>
313+
</extension>
314+
306315
</plugin>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Eclipse Foundation and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Arcadiy Ivanov - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.ui.tests.callhierarchy;
15+
16+
import org.eclipse.core.runtime.CoreException;
17+
import org.eclipse.core.runtime.IPath;
18+
import org.eclipse.core.runtime.IProgressMonitor;
19+
20+
import org.eclipse.jdt.core.IMember;
21+
import org.eclipse.jdt.core.search.IJavaSearchScope;
22+
import org.eclipse.jdt.core.search.SearchDocument;
23+
import org.eclipse.jdt.core.search.SearchMatch;
24+
import org.eclipse.jdt.core.search.SearchParticipant;
25+
import org.eclipse.jdt.core.search.SearchPattern;
26+
import org.eclipse.jdt.core.search.SearchRequestor;
27+
28+
/**
29+
* Test search participant for call hierarchy tests. Registered via plugin.xml
30+
* for the {@code .testlang} file extension.
31+
*
32+
* <p>Tests control its behavior via static fields set before invoking the
33+
* call hierarchy API.
34+
*/
35+
public class TestCallHierarchyParticipant extends SearchParticipant {
36+
37+
/** Callees to return from {@link #locateCallees}. Set by tests before use. */
38+
public static SearchMatch[] calleesToReturn = new SearchMatch[0];
39+
40+
/** Number of times {@link #locateCallees} was called. */
41+
public static int locateCalleesCallCount = 0;
42+
43+
public static void reset() {
44+
calleesToReturn = new SearchMatch[0];
45+
locateCalleesCallCount = 0;
46+
}
47+
48+
@Override
49+
public SearchDocument getDocument(String documentPath) {
50+
return new SearchDocument(documentPath, this) {
51+
@Override
52+
public byte[] getByteContents() {
53+
return new byte[0];
54+
}
55+
56+
@Override
57+
public char[] getCharContents() {
58+
return new char[0];
59+
}
60+
61+
@Override
62+
public String getEncoding() {
63+
return "UTF-8"; //$NON-NLS-1$
64+
}
65+
};
66+
}
67+
68+
@Override
69+
public void indexDocument(SearchDocument document, IPath indexLocation) {
70+
// no-op
71+
}
72+
73+
@Override
74+
public void locateMatches(SearchDocument[] documents, SearchPattern pattern,
75+
IJavaSearchScope scope, SearchRequestor requestor,
76+
IProgressMonitor monitor) throws CoreException {
77+
// no-op
78+
}
79+
80+
@Override
81+
public IPath[] selectIndexes(SearchPattern query, IJavaSearchScope scope) {
82+
return new IPath[0];
83+
}
84+
85+
@Override
86+
public SearchMatch[] locateCallees(IMember caller, SearchDocument document,
87+
IProgressMonitor monitor) throws CoreException {
88+
locateCalleesCallCount++;
89+
return calleesToReturn;
90+
}
91+
}

0 commit comments

Comments
 (0)