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
4 changes: 4 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,8 @@ every non-inspect command must reject that cursor family before execution.

`symbol_references.reference_kind` stores raw extractor labels. Default call-graph surfaces (`callers`, `callees`, inspect/analyze caller and callee bundles, and their JSON/MCP fields) expose the canonical public vocabulary `call`, `instantiate`, and `subscribe`. The primary `reference_kind`, `reference_kinds`, and `reference_kind_counts` keys use that same vocabulary. Use `--raw-kinds` on `callers` / `callees`, or `references --kind <raw-kind>`, when debugging raw extractor output.

`ReferenceRecord.SpanLength` and `symbol_references.span_length` persist the physical matched-token width rather than deriving it from the resolved symbol name; this matters for constructor-chain tokens such as `base`, `super`, and `this`. `DbReader.GetCallees` preserves that span while aggregating counts: it selects the smallest `(line, column_number)` among rows with a stored column, exposes that 1-based pair as `first_line` / nullable `first_column`, and carries the same row's nullable width as `first_length`; `reference_count` remains the independent aggregate. When every contributing legacy row has `column_number IS NULL`, the reader retains the minimum line and a null column. A migrated row can also retain a column with a null span length. CLI/MCP location adapters degrade either case without fabricating precision.

Reference extraction deduplicates only within the same indexed file and language context. When adding extractor paths, include the file id and language hint in shared `seen` keys so same line/column/name edges from polyglot workspaces do not collapse across Java, Rust, C#, SQL, or other language-specific normalization contexts.

| Raw kind | Logical graph kind | Notes |
Expand Down Expand Up @@ -4307,6 +4309,8 @@ inspect 以外の各 command は実行前にその cursor family を拒否しな

`symbol_references.reference_kind` には extractor が出力した raw label を保存する。既定の call-graph 表示(`callers`、`callees`、inspect/analyze の caller / callee bundle、および JSON/MCP フィールド)は、公開 canonical 語彙 `call`、`instantiate`、`subscribe` を返す。primary `reference_kind`、`reference_kinds`、`reference_kind_counts` の key はすべて同じ語彙を使う。raw extractor 出力を調べる場合は、`callers` / `callees` の `--raw-kinds`、または `references --kind <raw-kind>` を使う。

`ReferenceRecord.SpanLength` と `symbol_references.span_length` は、解決後の symbol 名から導出せず、物理的に一致した token 幅を永続化する。これは `base`、`super`、`this` のような constructor-chain token で重要になる。`DbReader.GetCallees` は count を集約しながらその span を保持し、列が保存された row のうち最小の `(line, column_number)` を選び、その 1-based 座標を `first_line` / nullable な `first_column`、同じ row の nullable な幅を `first_length` として公開し、`reference_count` は独立した集約値のままにする。寄与する legacy row がすべて `column_number IS NULL` の場合は最小行と null 列を保持する。移行済み row では列があっても span 長が null の場合がある。CLI/MCP の location adapter はどちらの場合も精度を捏造せず劣化させる。

| Raw kind | Logical graph kind | 備考 |
|---|---|---|
| `call` | `call` | 直接実行される呼び出しエッジ。 |
Expand Down
4 changes: 4 additions & 0 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,8 @@ By default, `callers` and `callees` return only executable call, construction, a

`callers` and `callees` rank results by weighted structural importance by default: `instantiate` edges count as `3.0`, direct `call` edges as `1.0`, and event `subscribe` edges as `0.1`. This keeps factory or composition-root constructor use from being buried under noisy event subscriptions. Use `--rank-by count` to restore raw `reference_count` ordering, or `--rank-by kind` to group `instantiate`, `call`, then `subscribe` before count. JSON rows keep the raw `reference_count` and add `reference_kind_counts` plus `reference_weight_score` so consumers can re-rank without losing the source counts; MCP structured rows expose the same data as `referenceKindCounts` and `referenceWeightScore`.

Grouped `callees` rows preserve the earliest precise call site separately from the aggregate `reference_count`. CLI JSON exposes its 1-based `first_line`, nullable `first_column`, and persisted physical-token `first_length`; MCP uses the corresponding `firstLine`, `firstColumn`, and `firstLength` fields. This keeps resolved constructor-chain calls on their actual `base`, `super`, or `this` token. Compact and quickfix output use the same 1-based column, SARIF carries the same token-sized region, and LSP converts it to a zero-based, non-empty token range. If every contributing legacy reference lacks a column, the column remains `null`, quickfix reports column `0`, SARIF omits `endColumn`, and LSP returns a zero-width range at the start of the retained source line. If a migrated row retains its column but has no persisted span length, SARIF still omits `endColumn` and LSP returns a zero-width range at that column instead of inventing a token width.

### Outline a single file

```bash
Expand Down Expand Up @@ -4769,6 +4771,8 @@ cdidx callees AddToGitExclude --exclude-tests

`callers` と `callees` は既定で構造的重要度の weighted 順に並びます。`instantiate` は `3.0`、直接 `call` は `1.0`、event `subscribe` は `0.1` として数えるため、factory や composition root の constructor 利用が大量の event subscription に埋もれにくくなります。従来どおり生の `reference_count` で並べたい場合は `--rank-by count`、reference kind を優先して `instantiate`、`call`、`subscribe` の順でまとめたい場合は `--rank-by kind` を使ってください。JSON の各行は生の `reference_count` を維持し、`reference_kind_counts` と `reference_weight_score` も追加で返します。MCP structured row では同じ情報を `referenceKindCounts` と `referenceWeightScore` として返すため、consumer 側で再ランキングできます。

集約された `callees` 行は、集約値の `reference_count` とは別に、最初の精密な call site を保持します。CLI JSON は 1-based の `first_line`、nullable な `first_column`、永続化された物理 token 幅の `first_length` を返し、MCP は対応する `firstLine`、`firstColumn`、`firstLength` を使います。そのため、解決後の constructor-chain call も実際の `base`、`super`、`this` token を指します。compact / quickfix も同じ 1-based 列を使い、SARIF は同じ token 幅の region、LSP は 0-based の非空 token range に変換します。寄与する legacy reference の全てで列が欠けている場合は列を `null` のまま保ち、quickfix は列 `0`、SARIF は `endColumn` を省略し、LSP は保持した source line の先頭に zero-width range を返します。移行済み row に列はあるものの永続化 span 長がない場合も、SARIF は `endColumn` を省略し、LSP はその列に zero-width range を返すため、token 幅を捏造しません。

### 1ファイルのアウトラインを見る

```bash
Expand Down
28 changes: 28 additions & 0 deletions changelog.d/unreleased/4841.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
category: fixed
issues:
- 4841
affected:
- src/CodeIndex/Models/ReferenceRecord.cs
- src/CodeIndex/Indexer/References/ReferenceExtractor.ReferenceRecords.cs
- src/CodeIndex/Indexer/References/ReferenceExtractor.PrimaryConstructors.cs
- src/CodeIndex/Indexer/References/ReferenceExtractor.CoreCallReferences.cs
- src/CodeIndex/Indexer/References/Support/JvmMethodReferenceExtractor.cs
- src/CodeIndex/Indexer/Hooks/PostExtractionHookMutationMaterializer.cs
- src/CodeIndex/Database/DbContext.SchemaInitialization.cs
- src/CodeIndex/Database/DbWriter.ReferenceSql.cs
- src/CodeIndex/Database/DbWriter.References.cs
- src/CodeIndex/Database/DbReader.GraphQueries.cs
- src/CodeIndex/Models/QueryResults.cs
- src/CodeIndex/Cli/QueryCommandRunner.Graph.cs
- src/CodeIndex/Cli/QueryCommandRunner.Locations.cs
- src/CodeIndex/Mcp/McpToolHandlers.Graph.cs
---

## English

- **`callees` now preserves the first physical call-site span in every location output (#4841)** — following the graph-location fidelity work in #4417, reference rows persist the matched token width and aggregated callee rows expose its deterministic 1-based column and length separately from `reference_count`; quickfix, LSP, SARIF, JSON, compact, and MCP output point at the called token—including resolved `base` / `super` / `this` constructor chains—while legacy rows without complete spans degrade explicitly.

## 日本語

- **`callees` がすべての location 出力で最初の物理 call-site span を保持するようになりました (#4841)** — #4417 の graph location fidelity 対応に続き、reference row は一致した token 幅を永続化し、集約された callee 行は `reference_count` とは別に決定的な 1-based 列と長さを公開します。quickfix、LSP、SARIF、JSON、compact、MCP は、解決済みの `base` / `super` / `this` constructor chain を含めて実際の呼び出し token を指し、完全な span を持たない legacy row は明示的に劣化します。
9 changes: 3 additions & 6 deletions src/CodeIndex/Cli/ProjectionFieldRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,7 @@ private static ProjectionCommandFieldSchema CreateCallGraphSchema(string command
var resultFields = isCallerCommand
? GetJsonFieldNames<CallerResult>()
: GetJsonFieldNames<CalleeResult>();
var compactFields = isCallerCommand
? new[] { "file", "line", "column" }
: ["file", "line"];
string[] compactFields = ["file", "line", "column"];
return Create(
command,
compactFields,
Expand All @@ -253,9 +251,8 @@ private static ProjectionCommandFieldSchema CreateCallGraphSchema(string command
"reference_extraction_limits", "reference_graph_complete",
"reference_extraction_cap_hits")
.Alias("file", "path")
.Alias("line", "first_line");
if (isCallerCommand)
builder.Alias("column", "first_column");
.Alias("line", "first_line")
.Alias("column", "first_column");
});
}

Expand Down
16 changes: 13 additions & 3 deletions src/CodeIndex/Cli/QueryCommandRunner.Graph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ public static int RunCallees(string[] cmdArgs, JsonSerializerOptions jsonOptions
{
if (TryWriteFormattedLocations(
options,
results.Select(r => new FormattedLocation(r.Path, r.FirstLine, null, $"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}")),
results.Select(r => new FormattedLocation(r.Path, r.FirstLine, r.FirstColumn, $"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}")),
jsonOptions))
return CommandExitCodes.Success;
if (options.OutputFormat == OutputFormatLsp)
Expand All @@ -379,12 +379,22 @@ public static int RunCallees(string[] cmdArgs, JsonSerializerOptions jsonOptions
}
if (options.OutputFormat == OutputFormatQf)
{
WriteQuickfix(results.Select(r => (r.Path, r.FirstLine, 1, $"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}")));
WriteQuickfix(results.Select(r => (r.Path, r.FirstLine, r.FirstColumn ?? 0, $"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}")));
return CommandExitCodes.Success;
}
if (options.OutputFormat == OutputFormatSarif)
{
WriteSarif(results.Select(r => (r.Path, r.FirstLine, 1, $"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}", r.ReferenceKind)), jsonOptions);
WriteSarif(
results.Select(r => new SarifLocation(
r.Path,
r.FirstLine,
r.FirstColumn ?? 1,
r.FirstColumn.HasValue && r.FirstLength.HasValue
? r.FirstColumn.Value + Math.Max(1, r.FirstLength.Value)
: null,
$"{r.CallerName ?? "<top-level>"} -> {r.CalleeName}",
r.ReferenceKind)),
jsonOptions);
return CommandExitCodes.Success;
}
foreach (var r in results)
Expand Down
13 changes: 12 additions & 1 deletion src/CodeIndex/Cli/QueryCommandRunner.Locations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,18 @@ private static LspLocation ToLspLocation(CallerResult result)
=> BuildLspLocation(result.Path, result.FirstLine, Math.Max(1, result.FirstColumn), result.FirstLine, Math.Max(1, result.FirstColumn) + Math.Max(1, result.CalleeName.Length));

private static LspLocation ToLspLocation(CalleeResult result)
=> BuildLspLocation(result.Path, result.FirstLine, 1, result.FirstLine, 1);
{
var column = Math.Max(1, result.FirstColumn ?? 1);
var endColumn = result.FirstLength.HasValue
? column + Math.Max(1, result.FirstLength.Value)
: column;
return BuildLspLocation(
result.Path,
result.FirstLine,
column,
result.FirstLine,
endColumn);
}

private static void WriteLspLocations(IEnumerable<LspLocation> locations, JsonSerializerOptions jsonOptions)
{
Expand Down
3 changes: 3 additions & 0 deletions src/CodeIndex/Database/DbContext.ReadMigrations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
reference_kind TEXT,
line INTEGER,
column_number INTEGER,
span_length INTEGER,
context TEXT,
reference_line_id INTEGER REFERENCES reference_lines(id) ON DELETE SET NULL,
container_kind TEXT,
Expand All @@ -166,6 +167,8 @@ is_mutual_recursion INTEGER NOT NULL DEFAULT 0
)"));
yield return ("EnsureColumn symbol_references.reference_line_id",
() => EnsureColumn("symbol_references", "reference_line_id", "INTEGER REFERENCES reference_lines(id) ON DELETE SET NULL"));
yield return ("EnsureColumn symbol_references.span_length",
() => EnsureColumn("symbol_references", "span_length", "INTEGER"));
yield return ("EnsureColumn symbol_references.is_self_reference",
() => EnsureColumn("symbol_references", "is_self_reference", "INTEGER NOT NULL DEFAULT 0"));
yield return ("EnsureColumn symbol_references.is_mutual_recursion",
Expand Down
Loading
Loading