Skip to content

Commit c0ce7ed

Browse files
authored
Expose actionable C# top-level symbols (#5178)
* Add actionable C# top-level symbols (#5164) * Address top-level symbol review findings (#5164) * Fix top-level symbol review regressions (#5164) * Fix C# contract reuse test assumptions (#5164) * Fix reusable snapshot performance fixture (#5164)
1 parent fb89363 commit c0ce7ed

32 files changed

Lines changed: 1637 additions & 183 deletions

DEVELOPER_GUIDE.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,43 @@ guard that would fail before users see multi-hour indexing stalls.
11391139

11401140
Do not add mutable static caches, shared `StringBuilder` instances, reused `MatchCollection` enumerators, or singleton scanner state to extractor code. If a future extractor needs cross-call memoization, use an explicit thread-safe collection and add a targeted parallel regression test that proves deterministic output under concurrent calls.
11411141

1142+
### C# top-level synthetic scope contract
1143+
1144+
C# extractor contract version 13 persists an actionable file-scoped symbol for
1145+
compilation units with executable top-level statements. The symbol uses
1146+
`kind=function`, `sub_kind=top_level_scope`, and `name=<top-level>`; public
1147+
results derive `is_synthetic=true`, qualify the identity as
1148+
`<indexed-path>::<top-level>`, and emit an `id:<symbol-id>@g:<generation>`
1149+
selector. Selectors are valid only for their active index generation and are
1150+
resolved by symbol id, so identical top-level programs in different files do
1151+
not share callee identity.
1152+
1153+
Detection runs after container assignment. It excludes declaration-covered
1154+
ranges, imports with any legal whitespace between their C# keywords, comments,
1155+
directives, and assembly/module metadata, recognizes
1156+
both `using var` and explicitly typed `using Type value = ...` declarations as
1157+
executable rather than import directives, then uses the first and last uncovered
1158+
executable lines as both source and body bounds.
1159+
A top-level local function remains source-declared and containerless; its own
1160+
narrower span owns references inside the function, while a synthetic range may
1161+
cross it when executable statements occur on both sides. When a local function
1162+
and an outside statement share one line, declaration columns retain the local
1163+
function while the outside statement belongs to the synthetic scope. Reference extraction
1164+
uses the synthetic symbol's persisted id for otherwise containerless calls in
1165+
that body. The synthetic scope is not a documented declaration, so an XML-doc
1166+
comment before a top-level statement does not attach to it. `outline`,
1167+
coordinate `inspect`, and identity-scoped `callees` must
1168+
therefore navigate the same row; both CLI and MCP `callees` resolve its selector
1169+
by persisted symbol id. Unused-symbol list and count queries exclude this
1170+
synthetic entry point because it is executable infrastructure, not removable
1171+
dead code. Selector-scoped callee queries fail closed when a readable legacy
1172+
schema lacks the persisted source-identity column. If a stored C# extractor version predates this
1173+
contract or is missing, and no synthetic row is available, outline reports
1174+
`top_level_symbol_support=reindex_required` plus a typed limitation instead of
1175+
claiming support. A normal full index re-extracts unchanged C# files before it
1176+
stamps this contract, so the documented reindex remediation repairs unstamped
1177+
legacy databases without requiring `--rebuild`.
1178+
11421179
### Symbol Kind Taxonomy
11431180

11441181
`symbols.kind`, `symbols.container_kind`, and `symbol_references.container_kind` use the public symbol kind taxonomy below. New extractors must register new kind values in `SymbolKindCatalog` before writing them so schema checks, writer validation, CLI filters, and downstream JSON consumers stay aligned.
@@ -5089,6 +5126,34 @@ regression には、scope rule の focused correctness test と、ユーザー
50895126

50905127
抽出器コードに mutable な static cache、共有 `StringBuilder` インスタンス、使い回しの `MatchCollection` enumerator、シングルトンの scanner state を追加してはならない。将来の抽出器が呼び出しをまたぐ memoization を必要とする場合は、明示的にスレッドセーフなコレクションを使い、並行呼び出し下でも決定的な出力になることを証明する focused な並列回帰テストを追加する。
50915128

5129+
### C# top-level synthetic scope 契約
5130+
5131+
C# extractor contract version 13 は、実行可能な top-level statement を持つ
5132+
compilation unit に、操作可能な file-scoped symbol を永続化します。この symbol は
5133+
`kind=function`、`sub_kind=top_level_scope`、`name=<top-level>` を使い、公開結果は
5134+
`is_synthetic=true`、`<indexed-path>::<top-level>` 形式の qualified identity、
5135+
`id:<symbol-id>@g:<generation>` 形式の selector を派生させます。selector は active な
5136+
index generation だけで有効で、symbol id により解決するため、異なる file にある同一内容の
5137+
top-level program が callee identity を共有することはありません。
5138+
5139+
検出は container assignment の後に実行します。declaration が覆う range、C# keyword 間に
5140+
任意の正当な whitespace を持つ import、comment、directive、assembly/module metadata を除外し、`using var` と明示型の
5141+
`using Type value = ...` declaration の両方を import directive ではなく実行可能コードとして認識し、
5142+
残った最初と最後の実行可能行を source/body 両方の境界にします。top-level local function は container を持たない source-declared symbol
5143+
のままで、その function 内の reference はより狭い自身の span が所有します。両側に実行
5144+
statement がある場合、synthetic range は local function をまたぐことがあります。local function と外側の statement が
5145+
同じ行にある場合、宣言 column は local function が所有し、外側の statement は synthetic scope が所有します。reference
5146+
extraction は、この body 内で従来 container を持たなかった call に synthetic symbol の永続化
5147+
id を使います。synthetic scope は documented declaration ではないため、top-level statement
5148+
直前の XML-doc comment を自身へ結び付けません。そのため `outline`、座標指定 `inspect`、identity-scoped `callees` は同じ row を
5149+
navigate しなければならず、CLI / MCP の両 `callees` が selector を永続化済み symbol id で解決します。
5150+
unused-symbol の list / count query は、この synthetic entry point が削除可能な dead code ではなく実行基盤なので除外します。
5151+
読み取り可能な legacy schema に永続 source-identity column がない場合、selector-scoped callee query は fail-closed します。
5152+
保存済み C# extractor version がこの契約より古いか欠落しており、
5153+
synthetic row も無い場合、outline は対応済みと見せず、`top_level_symbol_support=reindex_required` と型付きの
5154+
limitation を返します。通常の full index はこの contract を stamp する前に未変更 C# file も再抽出するため、
5155+
文書化された reindex remediation は `--rebuild` なしで stamp のない legacy database を修復します。
5156+
50925157
### シンボル種別分類
50935158

50945159
`symbols.kind`、`symbols.container_kind`、`symbol_references.container_kind` は

TESTING_GUIDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ Candidate-ordered parallel-index recovery tests must prove that the fatal result
561561
do not allocate production-sized 50k/20k inputs to test these contracts (#4620).
562562
- Broad extractor `*CompletesWithinPracticalBudget` runaway guards run only on the primary `net8.0` test target. Keep focused functional extractor tests cross-target, but do not duplicate the large-fixture budget guards across every target framework unless the guard is specifically proving a target-framework-specific contract.
563563
- C# reflection-name extraction coverage keeps literal, constant-concatenation, dynamic, comment, and string-decoy cases in one source fixture so those parser boundaries share one symbol/reference pass.
564+
- C# top-level synthetic-scope coverage (#5164) keeps the 10-line entry-point, local-function ownership including an outside call on the same line, typed using declarations, bounded extraction, legal-whitespace import directives, and all import/metadata/comment/directive/type/explicit-`Main` negatives in focused extractor fixtures. CLI reader fixtures own coordinate selection, cross-file selector isolation, caller/callee round-tripping, legacy source-identity fail-closed behavior, script-scope identity parity, compact output, stale-contract guidance, and unused list/count exclusion; the MCP assertion owns selector-to-`callees` round-tripping as well as transport naming parity. Keep full plus incremental no-op/change/add/rename/delete/no-op persistence and unstamped-C# upgrade re-extraction in `IndexCommandRunnerIssue5164Tests.cs` so lifecycle coverage uses real index reconciliation instead of direct database fixtures.
564565
- C# BOM extraction keeps a simple leading-BOM import fixture plus one mixed-newline fixture that simultaneously covers leading and mid-file BOM handling across CRLF, bare CR, and LF boundaries; do not repeat separate extraction passes for newline subsets already present in the mixed fixture.
565566
- C# lambda-capture coverage keeps positive enclosing-local capture, parameter shadowing, and same-named-method isolation in one source fixture; a single capture assertion proves the negative regions did not leak.
566567
- C# static-lambda declaration regression coverage keeps stateful, typed/untyped, explicit-return, both async-modifier orders, Unicode/escaped-identifier, multiline, nested, and argument-position forms in one extractor pass. Preserve real static members/local functions, including generic, constructor, explicit-interface, and verbatim-type-name forms, and an assigned-lambda range/container assertion in that fixture, plus one CLI `symbols` corpus fixture for phantom-name checks (#4830).
@@ -1685,6 +1686,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
16851686
は高負荷な C# reference extraction に対する広めの runaway guard です。C# warmup は `CI=true` かつ `net8.0` test assembly の場合だけ、各 guard が fixture 構築と stopwatch 計測より前に呼ぶ `Lazy` gate により test process ごとに1回実行します。どちらの guard も含まない shard は extraction と強制 GC の固定 warmup cost を負いません。module initializer では hook discovery の delay、persistent worker PID/thread、persistent descendant PID/process の environment 処理をこの順序のまま eager に維持してください。warmup は最後に強制 GC を行うため、この2 guard だけを専用 non-parallel collection に保ち、巨大な `ReferenceExtractorTests` partial class 全体を non-parallel にしないでください。budget は benchmark 閾値ではなく回帰 tripwire として扱い、焦点を絞った最適化根拠がない限り noisy CI に十分な余裕を残してください。
16861687
- extractor の広い `*CompletesWithinPracticalBudget` runaway guard は primary の `net8.0` test target だけで実行します。focused な extractor 機能テストは cross-target のまま維持しますが、その guard が target-framework 固有の契約を証明する場合を除き、大規模 fixture の budget guard をすべての target framework で重複実行しないでください。
16871688
- C# reflection-name 抽出 coverage は、literal、定数連結、dynamic、comment、string decoy を1つの source fixture にまとめ、これらの parser boundary で1回の symbol/reference pass を共有します。
1689+
- C# top-level synthetic scope の coverage (#5164) は、10行の entry point、同じ行の外側 call を含む local-function ownership、typed using declaration、bounded extraction、正当な whitespace を持つ import directive、import / metadata / comment / directive / type / explicit `Main` の全 negative を focused extractor fixture に維持します。CLI reader fixture は座標選択、file 間の selector 分離、caller/callee round-trip、legacy source-identity の fail-closed behavior、script-scope identity parity、compact 出力、stale-contract guidance、unused list/count からの除外を担当し、MCP の assertion は transport の命名 parity に加えて selector から `callees` への round-trip を担当します。full と incremental の no-op / change / add / rename / delete / no-op 永続化、および stamp のない C# upgrade の再抽出は `IndexCommandRunnerIssue5164Tests.cs` に維持し、direct database fixture ではなく実際の index reconciliation で lifecycle を検証してください。
16881690
- C# BOM 抽出は、単純な先頭 BOM import fixture と、CRLF・bare CR・LF 境界で先頭/mid-file BOM を同時に扱う1つの混在改行 fixture を維持します。混在 fixture に含まれる改行 subset ごとに抽出 pass を重複させないでください。
16891691
- C# lambda capture coverage は、外側 local の正例、parameter shadowing、同名 method 間の分離を1つの source fixture にまとめます。capture が1件だけである assertion により、negative region からの漏れも同時に検証します。
16901692
- C# static lambda の宣言回帰 coverage は、stateful、型あり/型なし、明示的戻り値型、両方の async modifier 順、Unicode/escape 識別子、複数行、入れ子、引数位置の各形式を1回の extractor pass にまとめます。同じ fixture で generic、constructor、明示的 interface、verbatim 型名を含む本物の static member / local function と、代入済み lambda の range / container assertion を維持し、phantom 名の確認には CLI `symbols` corpus fixture を1つ追加します(#4830)。

0 commit comments

Comments
 (0)