Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[ty] Add call hierarchy support#25338

Merged
MichaReiser merged 22 commits into
astral-sh:mainfrom
mhuen:mhuen/ty-call-hierarchy-optimizations
May 28, 2026
Merged

[ty] Add call hierarchy support#25338
MichaReiser merged 22 commits into
astral-sh:mainfrom
mhuen:mhuen/ty-call-hierarchy-optimizations

Conversation

@mhuen

@mhuen mhuen commented May 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for LSP call hierarchy calls including:

  • textDocument/prepareCallHierarchy
  • callHierarchy/incoming
  • callHierarchy/outgoing

Closes astral-sh/ty#1976

Test Plan

  • unit tests added in crates/ty_ide/src/call_hierarchy.rs
  • basic e2e tests in crates/ty_server/tests/e2e/call_hierarchy.rs
  • manual cross-checks against pyright (see known differences below)
  • criterion benchmark for e2e performance on anyio

Known differences to pyright (version 1.1.408)

  • Class outgoing uses class-statement semantics
    • ty includes class decorators, base-class expressions, body calls, method decorators, parameter defaults
    • ty excludes method bodies, nested function bodies, lambda bodies
    • Pyright walks __init__'s body instead; reports nothing for classes without __init__
  • Function outgoing stops at nested def/class/lambda bodies
    • ty reports the nested name as a callee, not the calls inside its body
    • Pyright walks into nested defs
    • Symmetry with the class-outgoing behavior above
  • super().__init__() incoming reported once
    • ty deduplicates by (file, caller selection range)
    • Pyright reports the same range twice
  • @property dispatched by access kind
    • Read (c.prop) matches getter only; write (c.prop = v) matches setter only; del (del c.prop) matches deleter only
    • Pyright lumps every read/write/del under getter+setter+deleter
    • Models Python's descriptor protocol more faithfully
  • Caller kind reflects the real symbol kind
    • ty emits Method (6), Class (5), or Function (12) based on the caller's enclosing scope
    • Pyright always emits Function (12) regardless of caller type
  • Aliased-import call sites resolve through the alias
    • from x import f as g; g() reports as a caller of f
    • Pyright misses the alias and returns incomingCalls: null

@MichaReiser

Copy link
Copy Markdown
Member

Wow, nice! I agree, this is a feature that ty desperately needs and it's on our immediate roadmap. However, it may take us 1-2 weeks before I'll have a chance to review this, as there is one more fatal panic that I want to look into first.

From skimming this code (and without having a concrete idea on how this should or is working in the PR), these are a few questions/comments that popped up:

  • The parsed module cache is probably unnecessary. parsed_module is already cached, calling it should be very cheap (roughly as expensive as a hash map lookup)
  • Can we reuse more code with find references?

@mhuen mhuen requested a review from ibraheemdev as a code owner May 27, 2026 06:04
@mhuen

mhuen commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

@MichaReiser thanks for the initial feedback. No worries on the timeline, I am not in a rush.
You're right about the unnecessary parsed module cache. I have removed that and also the local duplicate is_dunder helper along with some other cleanup.
Let me know if this PR is salvageable. I don't usually program in Rust, nor am I familiar with the code base. I'm happy to address issues here, but also fine in case it's easier for you to implement this from scratch.

@astral-sh-bot

astral-sh-bot Bot commented May 27, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 91.94%. The percentage of expected errors that received a diagnostic held steady at 87.09%. The number of fully passing files held steady at 92/134.

@astral-sh-bot

astral-sh-bot Bot commented May 27, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented May 27, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

Full report with detailed diff (timing results)

@astral-sh-bot

astral-sh-bot Bot commented May 27, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

ℹ️ ecosystem check detected linter changes. (+0 -2 violations, +0 -0 fixes in 1 projects; 55 projects unchanged)

zulip/zulip (+0 -2 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

- zproject/prod_settings_template.py:297:1: ERA001 Found commented-out code
- zproject/prod_settings_template.py:298:1: ERA001 Found commented-out code

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
ERA001 2 0 2 0 0

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+0 -2 violations, +0 -0 fixes in 1 projects; 55 projects unchanged)

zulip/zulip (+0 -2 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

- zproject/prod_settings_template.py:297:1: ERA001 Found commented-out code
- zproject/prod_settings_template.py:298:1: ERA001 Found commented-out code

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
ERA001 2 0 2 0 0

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

ruff format --preview --exclude examples/mcp/databricks_mcp_cookbook.ipynb,examples/chatgpt/gpt_actions_library/gpt_action_google_drive.ipynb,examples/chatgpt/gpt_actions_library/gpt_action_redshift.ipynb,examples/chatgpt/gpt_actions_library/gpt_action_salesforce.ipynb,

warning: Detected debug build without --no-cache.
error: Encountered error: No such file or directory (os error 2)

@AlexWaygood AlexWaygood removed their request for review May 27, 2026 17:53

/// Helper for `callHierarchy/incomingCalls`. Iterates over all projects in the
/// session and merges results from whichever project owns the file.
pub(crate) fn incoming_calls_handler(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd move this into the incoming call request handler's file. Given that it's not used elsewhere

/// Helper for `callHierarchy/outgoingCalls`. Same multi-project iteration as
/// the incoming variant, but `from_ranges` come from the *prepared* item's
/// file (the caller).
pub(crate) fn outgoing_calls_handler(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here

Comment thread crates/ty_ide/src/call_hierarchy.rs Outdated
sites
}

struct CallSitesFinder<'a, 'db> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you say more why this is an entirely new visitor. Could we use LocalReferencesFinder instead and either trim the results afterwards or have a mode parameter to drop references we don't care about?

Comment thread crates/ty_ide/src/call_hierarchy.rs Outdated
}

/// AST visitor that, for a single function/class body, records every callee.
struct OutgoingCallsFinder<'a, 'db> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here. Could we reuse some of LocalReferencesFinder

@MichaReiser

Copy link
Copy Markdown
Member

This is awesome. Thank you! I did a few changes, mainly to how the test work and I split the implementation into multiple modules. I'll also remove the benchmark. While cool, I'm worried that it's noisy

@MichaReiser MichaReiser enabled auto-merge (squash) May 28, 2026 18:54
@MichaReiser MichaReiser merged commit f9ba228 into astral-sh:main May 28, 2026
58 of 59 checks passed
@codspeed-hq

codspeed-hq Bot commented May 28, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 4.38%

❌ 1 regressed benchmark
✅ 64 untouched benchmarks
⏩ 60 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime pydantic 34.7 s 36.3 s -4.38%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mhuen:mhuen/ty-call-hierarchy-optimizations (01782de) with main (320a482)

Open in CodSpeed

Footnotes

  1. 60 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

anishgirianish pushed a commit to anishgirianish/ruff that referenced this pull request May 28, 2026
@mhuennefeld

Copy link
Copy Markdown

Awesome thanks! Yes, the benchmark wasn't terribly helpful. Great, thanks for the time spent reviewing and updating the code. Do you have a rough timeline for a next release candidate of ty that will include this?

@MichaReiser

Copy link
Copy Markdown
Member

Do you have a rough timeline for a next release candidate of ty that will include this?

We do very frequent releases. It should go out in the next few days

@thernstig

Copy link
Copy Markdown
Contributor

@mhuen could you update https://docs.astral.sh/ty/features/language-server/#feature-reference? Similar to what was done in https://github.com/astral-sh/ty/pull/3382/changes.

You need to add textDocument/prepareCallHierarchy as well as I did not see it there.

Great job.

@mhuen

mhuen commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

@mhuen could you update https://docs.astral.sh/ty/features/language-server/#feature-reference? Similar to what was done in https://github.com/astral-sh/ty/pull/3382/changes.

You need to add textDocument/prepareCallHierarchy as well as I did not see it there.

Great job.

I had opened astral-sh/ty#3592, but as @MichaReiser points out, it may not be necessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LSP: Support callHierarchy/incomingCalls and callHierarchy/outgoingCalls

4 participants