Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,28 @@ Query commands that accept path filters (`search`, `definition`, `references`, `

Editor integrations can request standard location shapes directly. `definition`, `references`, `search`, `find`, and `validate` accept `--format <text|json|lsp|qf|sarif>`; `lsp` emits LSP `Location` arrays, `qf` emits Vim quickfix lines, and `sarif` emits SARIF 2.1.0. `goto <symbol>` returns the single unambiguous definition as one LSP `Location`, while `goto --all <symbol>` returns all matching locations.

### Extractor performance contract

Symbol and reference extractors run during `cdidx index`, so language-specific
helpers must assume they will see generated files, very large methods, and
thousands of declarations or references in one syntactic scope. Avoid helper
shapes that rescan the same body, line range, or accumulated result list once
per candidate. If scope or delimiter information is needed for many candidates,
precompute the ranges once per file, function, or block and reuse that structure
for the per-candidate lookup.

Duplicate detection in hot extraction loops should use a `HashSet` or another
constant-time structure keyed by the full emitted record identity. Do not add
`List.Any(...)`, `List.Contains(...)`, nested regex scans, or repeated string
joins to loops that can run once per local variable, parameter, call site, type
reference, or pattern match in a large generated file.

The C# value-receiver path is the reference example: local receiver scopes are
derived from precomputed block spans for the containing function, and duplicate
receiver records are tracked with a hash set. Regressions in this area should
have a focused correctness test for the scoping rule and a large-fixture runaway
guard that would fail before users see multi-hour indexing stalls.

### Extractor concurrency contract

`SymbolExtractor` and `ReferenceExtractor` must be safe to call concurrently for different files or repeated calls on the same file content. Shared `Regex` instances and static lookup tables are initialized once by the CLR and treated as immutable after type initialization. Per-extraction state belongs in local variables, method parameters, caller-owned collections, or language-specific state objects created for that extraction call.
Expand Down Expand Up @@ -2436,6 +2458,24 @@ path filter を受け付ける query コマンド(`search`, `definition`, `ref

editor integration は標準的な location 形状を直接要求できる。`definition`、`references`、`search`、`find`、`validate` は `--format <text|json|lsp|qf|sarif>` を受け付け、`lsp` は LSP `Location` 配列、`qf` は Vim quickfix 行、`sarif` は SARIF 2.1.0 を出力する。`goto <symbol>` は曖昧でない単一定義を 1 つの LSP `Location` として返し、`goto --all <symbol>` は一致する全 location を返す。

### 抽出器の性能契約

symbol / reference extractor は `cdidx index` 中に実行されるため、言語別 helper は
生成ファイル、非常に大きなメソッド、1 つの構文スコープ内に数千個の宣言や参照がある入力を
前提にする。候補ごとに同じ本文、行範囲、蓄積済み結果リストを再走査する helper 形状は避ける。
多数の候補に対して scope や delimiter 情報が必要な場合は、file / function / block 単位で
範囲情報を一度だけ事前計算し、候補ごとの lookup でその構造を再利用する。

hot な抽出ループでの重複検出には、出力 record の完全な identity を key にした `HashSet` などの
定数時間構造を使う。大きな生成ファイルで local variable、parameter、call site、type reference、
pattern match ごとに実行され得るループへ、`List.Any(...)`、`List.Contains(...)`、nested regex scan、
繰り返しの string join を追加してはならない。

C# の value receiver 経路を参照例とする。local receiver の scope は containing function 用に
事前計算した block span から導出し、重複 receiver record は hash set で追跡する。この領域の
regression には、scope rule の focused correctness test と、ユーザーが multi-hour indexing stall を
見る前に失敗する大規模 fixture の runaway guard を追加する。

### 抽出器の並行実行契約

`SymbolExtractor` と `ReferenceExtractor` は、異なるファイルへの並行呼び出しや、同じファイル内容に対する繰り返し呼び出しでも安全でなければならない。共有される `Regex` インスタンスや static な lookup table は CLR が一度だけ初期化し、型初期化後は immutable として扱う。抽出ごとの状態は、ローカル変数、メソッド引数、呼び出し元が所有するコレクション、またはその抽出呼び出し用に生成した言語固有の state object に持たせる。
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
category: fixed
affected:
- DEVELOPER_GUIDE.md
- src/CodeIndex/Indexer/References/Languages/CSharpReferenceExtractor.Support.cs
- src/CodeIndex/Indexer/References/Languages/CSharpReferenceExtractor.ValueReceivers.cs
- tests/CodeIndex.Tests/ReferenceExtractorCSharpTests.cs
---

## English

- **Fixed runaway C# reference extraction on very large methods.** Large C# repositories could spend an excessive amount of time in the `references` phase when a file contained a very large method, especially generated code or hand-written methods with thousands of local variables. In affected cases, indexing that previously finished in tens of minutes could continue for hours while making little progress through C# reference extraction.
- The slowdown came from value receiver tracking. For every local value receiver, the extractor rescanned the method body to find the innermost block end and then performed a linear duplicate check against the receivers already collected for the function. Methods with many locals therefore paid the same body scan and growing duplicate check repeatedly, creating super-linear behavior.
- C# reference extraction now builds block scope spans once per function body and reuses them when assigning local receiver scopes. It also uses a hash set for duplicate receiver detection. This keeps receiver collection practical for very large methods while preserving block-scoped behavior for locals that shadow enum or type names.
- Added regression coverage for block-scoped local receiver behavior and a large-method runaway guard so future changes catch this class of performance regression earlier.
- Documented the extractor performance contract in the developer guide so future language-specific extractor changes avoid per-candidate body rescans and linear duplicate checks in hot paths.

## 日本語

- **非常に大きなメソッドで C# 参照抽出が暴走する問題を修正しました。** 大型の C# リポジトリで、巨大な生成コードや数千個規模のローカル変数を持つ手書きメソッドが含まれている場合、インデックス作成が `references` フェーズで極端に長く止まることがありました。影響を受けるケースでは、以前は数十分で終わっていたインデックス作成が、C# の参照抽出中に何時間も進みにくくなることがありました。
- 原因は value receiver 追跡でした。各ローカル value receiver ごとに、最内側ブロックの終端を求めるためメソッド本文を再走査し、その後で関数内に集め済みの receiver に対して線形の重複チェックを行っていました。ローカル変数が多いメソッドでは、同じ本文走査と増え続ける重複チェックを何度も支払うため、super-linear な挙動になっていました。
- C# 参照抽出では、関数本文ごとのブロックスコープ範囲を一度だけ構築し、ローカル receiver のスコープ判定で再利用するようにしました。また、receiver の重複検出にはハッシュセットを使うようにしました。これにより、巨大なメソッドでも実用的な時間で receiver を収集しつつ、enum や type 名を隠すブロックスコープ付きローカルの扱いは維持されます。
- ブロックスコープ付きローカル receiver の挙動と、大きなメソッドでの暴走を検出する回帰テストを追加しました。
- 開発者ガイドに extractor の性能契約を記載し、今後の言語別 extractor 変更で hot path に候補ごとの本文再走査や線形重複チェックを入れないようにしました。
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
category: fixed
affected:
- src/CodeIndex/Indexer/References/Languages/SwiftReferenceExtractor.cs
- src/CodeIndex/Indexer/References/Languages/TypeScriptReferenceExtractor.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Cpp.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Dockerfile.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Go.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Java.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.JavaScriptTypeScriptSupport.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Rust.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Shell.cs
- tests/CodeIndex.Tests/ReferenceExtractorRustSwiftTests.cs
- tests/CodeIndex.Tests/ReferenceExtractorTests.cs
- tests/CodeIndex.Tests/SymbolExtractorTests.cs
---

## English

- **Reduced symbol extraction overhead for large non-C# generated files.** Rust `use` expansion, Shell alias expansion, Go grouped declarations, Java/Kotlin primary-constructor or record components, C++ same-line class members, Dockerfile named stage chains, and JavaScript/TypeScript exported surfaces and object/class scan-target collection now avoid candidate-by-candidate scans over growing lists.
- The shared symbol-line identity cache preserves the existing duplicate key of file, line, kind, and name for language paths such as Rust, Shell, Go, and JavaScript/TypeScript synthetic class emission. Java/Kotlin record component materialization now tracks existing component names per parent record, Java compact constructor synthesis reuses the already filtered same-line symbols, C++ same-line class-member backfill tracks member names per container, Dockerfile extraction tracks stage names as the file is scanned, and JavaScript/TypeScript export/object-literal supplement passes keep per-file or per-container name sets plus scan-target identity sets.
- Swift and TypeScript reference extraction also now checks existing type-reference rows through the shared reference dedupe key instead of scanning the accumulated reference list while expanding typealias / type-alias targets.
- These changes avoid theoretical super-linear hot paths in generated files with thousands of declarations, imports, aliases, constructor components, object-literal properties, export variables, object literal targets, or class expression targets while keeping emitted symbols and duplicate semantics unchanged.
- Added large-fixture runaway guards for Rust, Shell, Go, Java, Kotlin, C++, Dockerfile, JavaScript, TypeScript, and Swift so future extractor changes catch this class of non-C# performance regression earlier.

## 日本語

- **大きな C# 以外の生成ファイルに対する symbol 抽出のオーバーヘッドを減らしました。** Rust の `use` 展開、Shell の alias 展開、Go の grouped declaration、Java/Kotlin の primary constructor / record component、C++ の same-line class member、Dockerfile の named stage chain、JavaScript/TypeScript の exported surface と object/class scan-target collection で、候補ごとに増え続ける list を走査しないようにしました。
- 共通の symbol-line identity cache は、Rust、Shell、Go、JavaScript/TypeScript の synthetic class emission などの経路で従来どおり file、line、kind、name を重複キーとして使います。Java/Kotlin の record component materialization は親 record ごとの component name set を使い、Java compact constructor synthesis は同一行に絞り込んだ既存 symbol を再利用し、C++ の same-line class member 補完は container ごとの member name set を使い、Dockerfile 抽出はファイル走査中に stage name を追跡し、JavaScript/TypeScript の export / object literal 補完はファイル単位または container 単位の name set と scan-target identity set を使うようにしました。
- Swift と TypeScript の reference 抽出でも、typealias / type alias target 展開時に accumulated reference list を走査するのではなく、共通の reference dedupe key で既存の type-reference 行を確認するようにしました。
- これにより、数千個の declaration、import、alias、constructor component、object literal property、export variable、object literal target、class expression target を含む生成ファイルで理論上発生しうる super-linear な hot path を避けます。出力される symbol と重複判定の意味は変えていません。
- Rust、Shell、Go、Java、Kotlin、C++、Dockerfile、JavaScript、TypeScript、Swift に対する大規模 fixture の runaway guard を追加し、今後の extractor 変更で同種の C# 以外の性能 regression を早く検出できるようにしました。
Loading
Loading