Skip to content

refactor(semantic): flag instantiation subtrees at a single write point - #571

Merged
16bit-ykiko merged 8 commits into
mainfrom
refactor/instantiation-cleanup
Aug 1, 2026
Merged

16bit-ykiko merged 8 commits into
mainfrom
refactor/instantiation-cleanup

Conversation

@16bit-ykiko

@16bit-ykiko 16bit-ykiko commented Aug 1, 2026 •

Copy link
Copy Markdown
Member

What

The per-feature migrations onto the Semantics node table left each feature re-deriving "is this decl a template instantiation" on its own, and one feature (inlay hints) not deriving it at all. This PR makes the builder the single write point for that fact and cleans up the duplication it left behind.

Instantiation flag (single write point)

  • The Semantics builder now sets in_instantiation on every node recorded inside a template-instantiation subtree. The decl created by an explicit instantiation directive is itself written and stays unflagged; an implicit instantiation head is not written, so the flag covers it too. The head predicate lives in the shared decls::is_instantiation.
  • Inlay hints previously walked instantiated subtrees unguarded; the end-of-pipeline dedup masked it for a single instantiation, but two explicit instantiations stacked contradictory type hints (: char and : int) on the same dependent auto. Fixed and pinned by a new fixture. A side effect consciously accepted: a dependent auto local no longer picks up its type from a lone instantiated body (that only ever worked by accident); deducing it properly is tracked by the fixture's partial status (clangd#2275).
  • Document symbols and folding ranges drop their local TSK re-derivations for the shared predicate (byte-identical snapshots).
  • The occurrence layer (semantic tokens, TU index) deliberately does NOT skip instantiated bodies: clice treats a template as a duck-typed interface and each instantiation as an implementation of it, so a dependent name in the pattern classifies as its actual resolutions — and as a conflict token when instantiations disagree. Pinned by a dedicated fixture and written down in the template resolver design doc ("Instantiations as Implementations"; go-to-implementation over these relations is planned, index-side modeling stays open). The in_instantiation flag stays truthful for the members an explicit instantiation delivers as top-level decls.

Hover comment lookup reuse

decl_for_comment hand-rolled a weaker version of decls::instantiated_from (its TSK_Undeclared fallback always chose the primary template). It now chases instantiated_from to a fixed point, so an uninstantiated specialization like Foo<int*> documents itself with the matching partial specialization's comment. Pinned for both class and variable templates.

Comment scanning

Semantic tokens' has_logical_newline re-parsed /*...*/ syntax by hand to decide where directive context ends; it now consults the comment ranges the Lexer scan already collects, so comment syntax is parsed in one place.

Explicit instantiation directives (known limitation, now pinned)

Function and variable explicit instantiation directives (template void f<int>(int);) are invisible today — no semantic token on the name, no outline entry, no occurrence — because clang mislocates them at the pattern. This is fixed upstream by llvm/llvm-project#191658 (ExplicitInstantiationDecl, clang 23); until the toolchain pin catches up, every workaround site is tagged FIXME(explicit-instantiation) and the current behavior is pinned by partial fixtures in the semantic tokens and document symbol corpora. The class form (childless outline node, painted reference) keeps working and is pinned alongside.

Tests

  • New fixtures: inlay_hint/type_conflicting_instantiations, semantic_tokens/explicit_instantiation_directives, document_symbol/kinds_explicit_instantiations, plus hover pins for class and variable template comment fallback.
  • Full local gate: unit (RelWithDebInfo + Debug/ASan), snap (both, standalone + wire), integration, smoke, npm run check, docs check — all green.

The Semantics builder now sets in_instantiation on every recorded node
inside a template-instantiation subtree (the written explicit-directive
decl itself stays unflagged), and the head predicate moves to the shared
decls::is_instantiation. Consumers stop re-deriving TSK logic:

- inlay hints skip instantiation subtrees, fixing contradictory type
  hints (": char" and ": int" stacked on one dependent auto) when a
  template has several explicit instantiations
- document symbols and folding ranges drop their local re-derivations
- semantic tokens and the TU index projection skip flagged entries,
  enforcing the documented "instantiations never produce occurrences"
  contract at the walk level

hover's decl_for_comment now reuses decls::instantiated_from, so an
uninstantiated specialization documents itself with the matching partial
specialization instead of falling back to the primary template.

semantic tokens' has_logical_newline consults the Lexer-collected
comment ranges instead of re-parsing comment syntax by hand.

Function and variable explicit instantiation directives stay invisible
(mislocated by clang) until clang 23's ExplicitInstantiationDecl; the
workaround sites are tagged FIXME(explicit-instantiation) and the
current behavior is pinned by partial fixtures.
@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Template instantiation support

Layer / File(s) Summary
Semantic instantiation tracking
src/semantic/decls.*, src/semantic/semantics.*
Adds shared instantiation detection and records instantiation-subtree state while preserving explicit directive heads and written template arguments.
Feature filtering and hover resolution
src/feature/document_symbols.cpp, src/feature/folding_ranges.cpp, src/feature/hover.cpp, src/feature/inlay_hints.cpp, src/index/tu_index.cpp
Feature collectors skip instantiated subtrees or directives. Hover resolution follows instantiation patterns to the written declaration.
Semantic-token processing
src/feature/semantic_tokens.cpp, tests/snap/semantic_tokens/*, docs/en/features/semantic-tokens.md
Semantic-token collection skips instantiated nodes and improves logical-newline scanning. Tests cover class, function, and variable explicit-instantiation forms.
Snapshot and documentation coverage
tests/snap/document_symbol/*, tests/snap/hover/*, tests/snap/inlay_hint/*, docs/en/features/document-symbols.md, docs/en/features/inlay-hints.md, tools/feature_docs.ts
Adds coverage for symbols, hover comments, dependent type hints, and LLVM issue references.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: centralizing instantiation-subtree tracking at one write point.
Description check ✅ Passed The description directly explains the instantiation tracking refactor, related behavior changes, limitations, and test coverage.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/instantiation-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a082464239

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/semantic/semantics.cpp Outdated
An explicit instantiation directive's template-argument TypeLocs are
written code; flagging the whole subtree made semantic tokens and the
index skip their references. Only the member decls beneath the directive
are instantiated — track the directive node during traversal and flag
member subtrees alone. Pinned by the explicit_instantiation fixture,
which now paints a class-type argument.
An extern declaration and a definition of the same specialization each
happen to own their own redecl on clang 21, so both directives paint
their written name and arguments. The written info still lives on the
specialization rather than the directive — record that fragility in the
FIXME and pin today's behavior so a node-reuse change trips the
snapshot; per-directive info arrives with ExplicitInstantiationDecl.
clang builds no node for a function or variable explicit instantiation
directive (Sema reuses the specialization decl and discards the written
declarator), so the name, template arguments and even the declarator's
type paint nothing, in both the extern and the definition form. Pin all
twelve identifiers as unpainted until ExplicitInstantiationDecl.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/snap/semantic_tokens/explicit_instantiation_directives.cpp`:
- Around line 14-23: Remove all semantic-token markers from the
explicit-instantiation directives in the fixture, including convert, zero, their
template arguments, and declarator types. Keep both extern and non-extern forms
unchanged otherwise so the snapshot verifies that function and variable
explicit-instantiation directives remain fully unflagged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c1952f83-b469-4ce4-93fc-9ebe6d87a128

📥 Commits

Reviewing files that changed from the base of the PR and between e3c3072 and 6b2814d.

📒 Files selected for processing (3)
  • docs/en/features/semantic-tokens.md
  • tests/snap/semantic_tokens/explicit_instantiation_directives.cpp
  • tests/snap/semantic_tokens/explicit_instantiation_directives.snap.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/snap/semantic_tokens/explicit_instantiation_directives.snap.yml
  • docs/en/features/semantic-tokens.md

Comment thread tests/snap/semantic_tokens/explicit_instantiation_directives.cpp Outdated
The class, function and variable directive forms fail (or work) for
different reasons and will grow variants independently — static member
templates, nested specializations. Rename the class fixture to match
and give each form its own file: explicit_instantiation_class (the
supported form), explicit_instantiation_function and
explicit_instantiation_variable (the pinned blackouts).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ab2ff0200

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/semantic/semantics.cpp Outdated
Treat a template as a duck-typed interface and each instantiation as an
implementation: the occurrence layer deliberately classifies inside
instantiated bodies, so a dependent name paints as its actual resolution
and as a conflict when instantiations disagree. Pinned by the
explicit_instantiation_member_bodies fixture and recorded in the
template resolver design doc (go-to-implementation over these relations
is planned; index-side modeling stays open).

Walk-based features keep skipping instantiated subtrees — they emit
location-keyed items where an instantiation only repeats the pattern.
The in_instantiation flag now stays truthful for members an explicit
instantiation delivers as top-level decls (is_member_specialization).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ec4862b25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/feature/semantic_tokens.cpp
Comment thread src/feature/inlay_hints.cpp
Comment thread src/feature/semantic_tokens.cpp
… args

Two follow-ups on the instantiation-classification semantics:

- combine() kept the first candidate's modifiers on agreeing kinds, so a
  dependent name's static modifier depended on the order the explicit
  instantiations were written in. Modifiers now intersect across all
  candidates: agreeing kinds keep what every instantiation shares.

- The inlay walk skipped a class directive's whole subtree, losing hints
  on written template arguments (a call inside decltype). The subtree
  holds only written code, so walk into it; the mislocated function and
  variable directive forms stay skipped whole.

Both pinned; the design doc now also states that only explicitly
instantiated definitions are recorded today, with implicit
instantiations a planned extension of the same direction.
@16bit-ykiko
16bit-ykiko merged commit cf358a7 into main Aug 1, 2026
31 checks passed
@16bit-ykiko
16bit-ykiko deleted the refactor/instantiation-cleanup branch August 1, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant