Skip to content

fix: SCIP symbol-name and enrichment correctness (parameter descriptor pollution, span/name/enrichment) - #40

Open
pradeepmouli wants to merge 3 commits into
intuit:mainfrom
pradeepmouli:fix/scip-parameter-descriptor-pollution
Open

fix: SCIP symbol-name and enrichment correctness (parameter descriptor pollution, span/name/enrichment)#40
pradeepmouli wants to merge 3 commits into
intuit:mainfrom
pradeepmouli:fix/scip-parameter-descriptor-pollution

Conversation

@pradeepmouli

Copy link
Copy Markdown
Contributor

Summary

Three related fixes to SCIP import correctness in crates/infigraph-core/src/scip/mod.rs, found while investigating spurious/ugly entries in get_doc_context output:

  1. scip_sym_to_name returned empty/wrong names for most real SCIP symbols. The old implementation split on suffix characters (., #, /, :) without first stripping the trailing suffix marker itself, so it frequently returned an empty string or included the marker. Rewritten to strip the trailing terminator/disambiguator group first, then extract the name, with explicit handling for backtick-quoted descriptors (`test.ts`/Animal#) and bare identifiers.

  2. SCIP enrichment overwrote existing symbols' start_line/end_line. A SCIP definition occurrence's range is the span of the identifier token itself, not the enclosing declaration — but enrichment was writing that narrow range back over the existing symbol's correct full-body span (already established by tree-sitter's AST extraction), silently collapsing every enriched symbol's displayed source to ~1 line. Enrichment now only ever updates docstring.

  3. Parameter/member descriptors were becoming spurious top-level graph symbols. A simple function parameter's SCIP moniker is <enclosing-method-moniker-ending-in-.>.(paramName) — no local prefix, so it bypassed the existing local-symbol filter and was created as its own (incorrectly named, since it depends on fix Readme Updates and GitHub Pages documentation site #1) graph node. Root-caused against real @sourcegraph/scip-typescript 0.4.0 output; SymbolInformation.kind was verified to always be UnspecifiedKind in real output, so a kind-based filter wasn't viable. Fixed with a structural check (is_member_of_known_symbol) that strips a trailing (...) descriptor group and checks whether what remains is already a known symbol from this same import pass — this exploits SCIP's own Parameter-descriptor grammar directly, so it should generalize across every SCIP indexer, not just TypeScript's.

Each fix is independently tested; #3 was verified to not affect CALLS-edge resolution (Pass 2) or INHERITS-edge resolution via relationships (Pass 3) — traced through both passes to confirm a suppressed parameter symbol cleanly fails to resolve as a reference target rather than dangling or misattributing an edge.

Test plan

  • cargo test -p infigraph-core --lib scip:: — 7/7 pass (all new + pre-existing tests)
  • cargo fmt --all -- --check — clean
  • cargo clippy -p infigraph-core --all-targets -- -D warnings — clean
  • Verified end-to-end against a real indexed TypeScript sample

🤖 Generated with Claude Code

SCIP symbols end with a descriptor suffix (`.`, `#`, `/`, `:`, or a
method disambiguator) as the literal last character, e.g.
`rust-analyzer cargo sittir-core 0.0.0 crate/` or `...main().`. The old
implementation rsplit on those same suffix chars *without stripping the
trailing one first*, so it always read the segment after the last
delimiter -- which is empty when the delimiter is the last byte.

Verified against a real rust-analyzer SCIP index of sittir: 22,075 of
28,861 non-local symbols (76%) resolved to an empty name. Every empty
name causes import_scip_index to miss matching the existing
tree-sitter symbol and instead insert a duplicate placeholder node, and
collisions on the shared (file, "") key make CALLS/enrichment
resolution pick an arbitrary same-file symbol instead of the real
target. Also verified against scip-typescript output (22,028 symbols,
0 empty names after the fix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAXDJyFdnA4BV1U2fxufdC

# Conflicts:
#	crates/infigraph-core/src/scip/mod.rs
…e/end_line

SCIP enrichment's UNWIND SET was writing a Definition occurrence's range
(the identifier token's own span) into an existing symbol's start_line/
end_line, clobbering tree-sitter's correct full-body span. Any symbol SCIP
successfully re-matches during enrichment -- the default, common-case path,
not an edge case -- had its displayed source silently collapsed to ~1 line
afterward, degrading get_doc_context/get_code_snippet output broadly for
any SCIP-enriched project.

Root cause: SCIP definition-occurrence ranges are the identifier token's
position, not the enclosing declaration's extent. enrichments now only
carries (id, docstring); start_line/end_line are left untouched for
existing symbols, matching what tree-sitter already computed correctly.
New symbols added purely from SCIP (no tree-sitter counterpart) are
unaffected by this fix and keep using SCIP's span, since they have no
better source.

Regression test: enrichment_does_not_overwrite_existing_symbol_span
reproduces the exact bug (pre-fix: start_line collapses to the token
line) and asserts the full-body span survives enrichment.

Confirmed live on this repo: crates/infigraph-core/src/embed/mod.rs::
update_embeddings had regressed to a single-line "source" (L444-444)
after ordinary SCIP enrichment; a fresh index + rebuild after this fix
restores its correct ~83-line span.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GhPBkDMHPpgcYJ3TKLAC1r

# Conflicts:
#	crates/infigraph-core/src/scip/mod.rs
…ymbols

get_doc_context on SCIP-enriched (scip-typescript) TypeScript projects
showed raw, unparsed SCIP monikers as caller/callee entries -- one per
function parameter. Root cause: scip-typescript encodes parameters as
a full global-style moniker (<method>().(paramName)) rather than a
SCIP-spec `local <id>` symbol, so the existing local-symbol filter
missed them, and every parameter became its own bogus Symbol node
(each with a tiny declaration-line span that could also steal
container_id from real references via the smallest-span-first
containment lookup).

Fixed by recognizing this structurally rather than by SCIP kind
(verified always UnspecifiedKind in real output, unusable) or by
re-parsing with tree-sitter (would need per-language node-kind tables
across all 10 configured SCIP indexers): a parameter's moniker is
exactly its enclosing method's own already-known moniker with
`(paramName)` appended, so stripping the trailing group and checking
against scip_sym_to_file_name (already built before this decision
runs) exactly identifies membership without any new data or heuristics.
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