Skip to content

Use contributed search participants in call hierarchy#2881

Merged
jjohnstn merged 2 commits into
eclipse-jdt:masterfrom
arcivanov:call-hierarchy-search-participants
May 13, 2026
Merged

Use contributed search participants in call hierarchy#2881
jjohnstn merged 2 commits into
eclipse-jdt:masterfrom
arcivanov:call-hierarchy-search-participants

Conversation

@arcivanov
Copy link
Copy Markdown
Contributor

@arcivanov arcivanov commented Mar 21, 2026

Summary

Enable cross-language call hierarchy by using contributed search participants in both incoming and outgoing call analysis.

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. For contributed (non-Java) elements, accept A_INACCURATE matches since JDT's MatchLocator cannot resolve type bindings for types not compiled by ECJ.

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.

Outgoing calls from Java to non-Java targets (CalleeAnalyzerVisitor)

When a Java method invocation has a null ECJ binding (receiver type is from a contributed language, not compiled by ECJ), fall back to resolveViaSearch() which uses SearchEngine.getSearchParticipants() to find METHOD DECLARATIONS matching the method name across all contributed participants.

Declaration resolution with filtering (CallHierarchyCore.findFirstDeclaration)

Shared utility method findFirstDeclaration() searches for the first matching IMember declaration by name, with optional post-filters applied in acceptSearchMatch():

  • Argument count — reject candidates with wrong parameter count, with varargs support: if the last parameter is an array type, accept any expected argument count ≥ paramCount − 1 (O(1))
  • Receiver type — reject candidates whose declaring type is not the receiver type or a supertype
  • Declaring type candidates — reject candidates not in the candidate list
  • Argument types — reject candidates with incompatible parameter types (FQN comparison with type resolution; for non-Java elements without a declaring type, FQN parameters are still matched while unresolvable simple names are skipped)

Non-standard ICompilationUnit handling (CallHierarchyCore)

  • Guard getCompilationUnitNode() with isJavaLikeFileName() check so non-Java source files (e.g. .kt) are not parsed as Java AST
  • Catch ClassCastException for non-standard ICompilationUnit implementations that do not implement the internal compiler interface required by ASTParser.createAST()

Dependency

Bump org.eclipse.jdt.core dependency to [3.46.0,4.0.0) for the SearchEngine.getSearchParticipants() and SearchParticipant.locateCallees() APIs.

Related PRs

Test plan

  • 6 tests in CallHierarchyParticipantTest:
    • searchParticipantsIncludesContributed — verifies contributed participant is returned by getSearchParticipants()
    • outgoingCallsJavaASTPathUnchanged — regression: Java AST callee analysis still works
    • outgoingCallsFallBackToParticipantslocateCallees() called for binary members without source, returns 2 callees
    • outgoingCallsResolvesExistingCallees — existing IMember callees resolved directly
    • outgoingCallsFallBackOnClassCastException — non-standard ICompilationUnit triggers ClassCastException, falls back to participant path
    • incomingCallsUsesAllParticipants — regression: incoming call search with all participants finds Java callers correctly
  • All existing CallHierarchyTest tests pass (regression)

@arcivanov arcivanov force-pushed the call-hierarchy-search-participants branch from 0fc11a2 to 5130843 Compare May 13, 2026 18:08
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.derivedSourceSearchParticipant` extension point. For
contributed (non-Java) elements, accept A_INACCURATE matches since JDT's
MatchLocator cannot resolve type bindings for types not compiled by ECJ.

Outgoing calls (CalleeMethodWrapper):

When the member has no Java AST (i.e. `getCompilationUnitNode()` returns
null), fall back to `DerivedSourceSearchParticipant.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.

Outgoing calls from Java to non-Java targets (CalleeAnalyzerVisitor):

When a Java method invocation has a null ECJ binding (receiver type is from
a contributed language, not compiled by ECJ), fall back to resolveViaSearch()
which uses SearchEngine.getSearchParticipants() to find METHOD DECLARATIONS
matching the method name across all contributed participants.

Non-standard ICompilationUnit handling (CallHierarchyCore):

Guard getCompilationUnitNode() with isJavaLikeFileName() check so non-Java
source files (e.g. .kt) are not parsed as Java AST, which would produce
broken results and false positives in outgoing call analysis.

Dependency:

Bump `org.eclipse.jdt.core` dependency to `[3.46.0,4.0.0)` for the
`SearchEngine.getSearchParticipants()` and
`DerivedSourceSearchParticipant.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.
@arcivanov arcivanov force-pushed the call-hierarchy-search-participants branch from 5130843 to 7bc2c46 Compare May 13, 2026 18:20
@arcivanov arcivanov requested a review from jjohnstn May 13, 2026 18:20
@arcivanov
Copy link
Copy Markdown
Contributor Author

I don't understand how suppression isn't necessary as it's a List being assigned to List<String>. I thought it was a false positive.

Copy link
Copy Markdown
Contributor

@jjohnstn jjohnstn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Getting this in will allow us to properly exercise the code via jdt.ls testing.

@jjohnstn jjohnstn merged commit 4197f17 into eclipse-jdt:master May 13, 2026
13 checks passed
@jjohnstn jjohnstn self-assigned this May 13, 2026
@jjohnstn jjohnstn added the enhancement New feature or request label May 13, 2026
@jjohnstn jjohnstn modified the milestones: 4.40 RC1, 4.40 M3 May 13, 2026
@jjohnstn
Copy link
Copy Markdown
Contributor

I don't understand how suppression isn't necessary as it's a List being assigned to List<String>. I thought it was a false positive.

There is a compiler option to ignore assigning raw types to parameterized ones. This option is set by default for jdt.core because this happens a lot due to legacy code.

@arcivanov
Copy link
Copy Markdown
Contributor Author

Looks good. Getting this in will allow us to properly exercise the code via jdt.ls testing.

Thanks!

@arcivanov arcivanov deleted the call-hierarchy-search-participants branch May 13, 2026 23:47
@arcivanov arcivanov restored the call-hierarchy-search-participants branch May 13, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants