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: 2 additions & 2 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ Do not add mutable static caches, shared `StringBuilder` instances, reused `Matc
| `environment` | Dockerfile `ENV` variable names | Variable/search symbol; participates in Dockerfile variable references |
| `event` | Event declarations | Search/filter symbol |
| `expose` | Dockerfile `EXPOSE` ports | Container runtime search symbol |
| `field` | Field declarations where distinct from properties; C# const and static readonly fields share the tuple-aware type grammar used by ordinary fields | Search/filter symbol |
| `field` | Field declarations where distinct from properties; C# plain, const, and static readonly fields share the tuple-aware type grammar, and oversized initializer signatures retain declaration metadata while replacing top-level initializer bodies with deterministic markers | Search/filter symbol |
| `file_module` | File-scoped module/package declarations | Namespace-like context symbol |
| `function` | Functions, methods, constructors, delegates, tasks, and callable bindings that do not have a narrower kind | Primary callable definition; participates in callers/callees through reference rows |
| `generator` | JavaScript/TypeScript generator declarations | Callable definition; participates in callers/callees through reference rows |
Expand Down Expand Up @@ -3924,7 +3924,7 @@ filter、downstream JSON consumer が同じ値を理解できるようにして
| `environment` | Dockerfile `ENV` variable name | variable/search symbol。Dockerfile variable reference に参加 |
| `event` | event declaration | Search/filter symbol |
| `expose` | Dockerfile `EXPOSE` port | container runtime search symbol |
| `field` | property と区別される field declaration。C# const / static readonly field は通常 field と tuple-aware な型文法を共有する | Search/filter symbol |
| `field` | property と区別される field declaration。C# の通常 / const / static readonly field tuple-aware な型文法を共有し、巨大 initializer の signature は declaration metadata を維持しつつ top-level initializer 本体を決定的 marker に置換する | Search/filter symbol |
| `file_module` | file-scoped module / package declaration | Namespace-like context symbol |
| `function` | 関数、method、constructor、delegate、task、およびより狭い kind がない callable binding | Primary callable definition。reference row 経由で callers/callees に参加 |
| `generator` | JavaScript / TypeScript generator declaration | Callable definition。reference row 経由で callers/callees に参加 |
Expand Down
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding

- `ChunkSplitterTests.cs`, `SymbolExtractorTests.cs`, `ReferenceExtractorTests.cs`, `SearchSnippetFormatterTests.cs`, `DbPathResolverTests.cs`, `ExcerptRecoveryCommandFormatterTests.cs`, `ConsoleUiTests.cs`
Pure or mostly pure behavior tests with in-memory inputs.
C# field coverage keeps collection expressions, constants, multiple declarators, target-typed `new`, arrays, real properties, oversized multiline initializer summarization, inherited and qualified field-receiver persistence, extractor-contract reindexing, search declaration metadata, and LSP field-kind projection in coordinated fixtures so public kind, signature, and reference-identity contracts cannot drift apart.
`DbPathResolverPureTests` keeps only path, injected-input, and URI validation cases that neither read process-global state nor open SQLite, allowing them to run outside the `SQLite pool sensitive` collection. Keep environment/current-directory resolution, real database and metadata probes, pool resets, and static test seams in `DbPathResolverTests`.
Search snippet origin-priority coverage keeps PascalCase, snake_case, and phrase queries in coordinated mixed comment/string/code fixtures so identifier focus, same-line code-column clamping, over-1-MiB valid chunks, final-window dropped counts, filtered-origin refocusing, and the phrase control share one contract.
Recovery-command coverage asserts structured argv, current `dotnet`/apphost prefix preservation, replay of option-like paths, and display-only escaping for both POSIX sh and PowerShell, including spaces, quotes, dollar signs, and shell metacharacters.
Expand Down Expand Up @@ -984,6 +985,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"

- `ChunkSplitterTests.cs`、`SymbolExtractorTests.cs`、`ReferenceExtractorTests.cs`、`SearchSnippetFormatterTests.cs`、`DbPathResolverTests.cs`、`ExcerptRecoveryCommandFormatterTests.cs`、`ConsoleUiTests.cs`
インメモリ入力中心の、純粋またはほぼ純粋な振る舞いのテスト。
C# field coverage は collection expression、constant、multiple declarator、target-typed `new`、array、実 property、巨大 multiline initializer の要約、継承 / qualified field receiver の永続化、extractor contract による再 index、search declaration metadata、LSP field kind への投影を連携 fixture にまとめ、公開 kind、signature、reference identity の契約が別々にずれないようにする。
`DbPathResolverPureTests` には process-global state を読まず SQLite も開かない path、注入済み入力、URI validation の case だけを置き、`SQLite pool sensitive` collection の外で実行できるようにします。環境変数 / current directory の解決、実 database / metadata probe、pool reset、static test seam は `DbPathResolverTests` に残してください。
search snippet の origin 優先順位 coverage は PascalCase、snake_case、phrase query を連携した comment / string / code 混在 fixture にまとめ、identifier focus、同一行の code 列への clamping、1 MiB を超える有効 chunk、最終 window の dropped count、filter 後 origin への再 focus、phrase の control を一つの contract として検証します。
recovery command の coverage では構造化 argv、現在の `dotnet` / apphost prefix の維持、option と紛らわしい path の再実行、POSIX sh と PowerShell 双方の表示専用 escaping を、空白、quote、dollar sign、shell metacharacter を含めて検証します。
Expand Down
29 changes: 29 additions & 0 deletions changelog.d/unreleased/4865.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
category: fixed
issues:
- 4865
affected:
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.Contracts.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.PatternSignatures.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.ExtractCore.cs
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.CSharpScanner.cs
- src/CodeIndex/Indexer/References/ReferenceExtractor.CoreLookups.cs
- src/CodeIndex/Indexer/References/Languages/CSharpReferenceExtractor.Support.cs
- src/CodeIndex/Database/DbWriter.References.cs
- src/CodeIndex/Database/DbSearchReader.cs
- src/CodeIndex/Lsp/LspServer.Protocol.cs
- src/CodeIndex/Lsp/LspServer.NavigationFeatures.cs
- tests/CodeIndex.Tests/SymbolExtractorTests.cs
- tests/CodeIndex.Tests/ReferenceExtractorCSharpTests.cs
- tests/CodeIndex.Tests/IndexCommandRunnerFullScanTests.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- tests/CodeIndex.Tests/LspServerTests.cs
---

## English

- **C# fields now keep field identity and bounded signatures with modern initializers (#4865)** — plain fields initialized with collection expressions, target-typed `new`, arrays, or other values are emitted as `field` symbols, while oversized initializer bodies are replaced with deterministic markers that preserve declaration metadata.

## 日本語

- **C# field が modern initializer 使用時も field identity と bounded signature を維持するようになりました (#4865)** — collection expression、target-typed `new`、array などで初期化した通常 field を `field` symbol として発行し、巨大 initializer 本体は declaration metadata を維持する決定的 marker に置換します。
1 change: 1 addition & 0 deletions src/CodeIndex/Database/DbSearchReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ PARTITION BY requested.request_index
CASE s.kind
WHEN 'function' THEN 0
WHEN 'test.method' THEN 0
WHEN 'field' THEN 1
WHEN 'property' THEN 1
WHEN 'class' THEN 2
WHEN 'interface' THEN 2
Expand Down
8 changes: 4 additions & 4 deletions src/CodeIndex/Database/DbWriter.References.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ JOIN symbols AS target
WHERE source.id = r.source_symbol_id
AND source_file.lang = 'csharp'
AND target_file.lang = 'csharp'
AND target.kind = 'property'
AND target.kind IN ('field', 'property')
AND target.container_qualified_name IN (
SELECT source.container_qualified_name
UNION
Expand All @@ -581,7 +581,7 @@ JOIN symbols AS target
WHERE source.id = r.source_symbol_id
AND source_file.lang = 'csharp'
AND target_file.lang = 'csharp'
AND target.kind = 'property'
AND target.kind IN ('field', 'property')
AND target.container_qualified_name IN (
SELECT source.container_qualified_name
UNION
Expand Down Expand Up @@ -631,7 +631,7 @@ JOIN symbols AS target
WHERE source.id = r.source_symbol_id
AND source_file.lang = 'csharp'
AND target_file.lang = 'csharp'
AND target.kind = 'property'
AND target.kind IN ('field', 'property')
AND target.container_qualified_name IN (
SELECT source.container_qualified_name
UNION
Expand Down Expand Up @@ -732,7 +732,7 @@ FROM symbol_references AS r
AND r.target_qualifier =
char(31) || 'property_receiver:' || s.container_qualified_name
COLLATE BINARY
AND s.kind = 'property';
AND s.kind IN ('field', 'property');

INSERT INTO symbol_reference_candidates(reference_id, symbol_id, scope_rank)
SELECT r.id, s.id, 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ private static void AddCSharpContainingTypeValueReceiverName(
ref Dictionary<string, CSharpContainingTypeValueReceiverNames>? lookup,
SymbolRecord symbol)
{
if (symbol.Kind != "property" || string.IsNullOrWhiteSpace(symbol.Name))
if (symbol.Kind is not ("field" or "property") || string.IsNullOrWhiteSpace(symbol.Name))
return;

var containingType = GetContainingTypeQualifiedName(symbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private static bool TryAddCoreCallLikeReference(
out var receiverNames)
&& (receiverNames.InstanceNames.Contains(normalizedName)
|| receiverNames.StaticNames.Contains(normalizedName))
&& call.Lookups.HasCSharpProperty(
&& call.Lookups.HasCSharpFieldOrPropertyMember(
containingType,
normalizedName))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ private static void RewriteCSharpPropertyReceiverReferences(
reference.Line);
var containingType = GetContainingTypeQualifiedName(owner);
if (containingType == null
|| !lookups.HasCSharpProperty(
|| !lookups.HasCSharpFieldOrPropertyMember(
containingType,
reference.SymbolName))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ private sealed class CoreExtractionLookups
private HashSet<string>? pythonClassNames;
private bool pythonClassNamesResolved;
private PythonImportBindingResolver.ImportedTypeCallLookup? pythonImportedTypeCallLookup;
private HashSet<(string Container, string Name)>? csharpProperties;
private bool csharpPropertiesResolved;
private HashSet<(string Container, string Name)>? csharpFieldOrPropertyMembers;
private bool csharpFieldOrPropertyMembersResolved;
private Dictionary<string, List<SymbolRecord>>? csharpContainerCandidatesByName;
private List<(int StartLine, int StartColumn, int EndLine, int EndColumn, SymbolRecord Container, SymbolRecord Owner)>? recordPrimaryCtorRanges;
private bool recordPrimaryCtorRangesResolved;
Expand Down Expand Up @@ -90,22 +90,22 @@ internal bool HasSameFilePythonClass(string candidate, string leaf)
internal PythonImportBindingResolver.ImportedTypeCallLookup GetPythonImportedTypeCallLookup()
=> pythonImportedTypeCallLookup ??= PythonImportBindingResolver.BuildImportedTypeCallLookup(symbols);

internal bool HasCSharpProperty(string containingType, string propertyName)
internal bool HasCSharpFieldOrPropertyMember(string containingType, string memberName)
{
if (!csharpPropertiesResolved)
if (!csharpFieldOrPropertyMembersResolved)
{
foreach (var symbol in symbols)
{
if (symbol.Kind == "property"
if (symbol.Kind is "field" or "property"
&& symbol.ContainerQualifiedName != null)
{
(csharpProperties ??= []).Add((symbol.ContainerQualifiedName, symbol.Name));
(csharpFieldOrPropertyMembers ??= []).Add((symbol.ContainerQualifiedName, symbol.Name));
}
}
csharpPropertiesResolved = true;
csharpFieldOrPropertyMembersResolved = true;
}

return csharpProperties?.Contains((containingType, propertyName)) == true;
return csharpFieldOrPropertyMembers?.Contains((containingType, memberName)) == true;
}

internal SymbolRecord? FindCSharpContainerCandidate(string? containerName, int lineNumber)
Expand Down
35 changes: 26 additions & 9 deletions src/CodeIndex/Indexer/Symbols/SymbolExtractor.CSharpScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -820,8 +820,9 @@ private static int FindNextBraceStatementStart(string line, int startIndex)
return -1;
}

// For C# plain fields (kind `property`, BodyStyle.None), find the end of the
// field's declaration statement on the same (merged) match line so the
// For C# plain fields (internally tagged as `property`, BodyStyle.None, and
// normalized to public `field`), find the end of the field's declaration
// statement on the same (merged) match line so the
// signature can be clamped to the full declaration text and the same-line
// pattern scanner can resume after the terminating `;`. Walks with paren /
// bracket / brace depth tracking so `{` / `}` inside an initializer
Expand All @@ -831,8 +832,9 @@ private static int FindNextBraceStatementStart(string line, int startIndex)
// so signature and advance both stop before the wrapper terminator. Input
// is expected to be the structurally-masked match line so string-literal
// `{` / `;` cannot poison the depth tracker.
// C# 通常フィールド(kind `property`、BodyStyle.None)向けに、結合済みマッチ行での
// 宣言文の終端位置を返す。signature を `;` まで含む完全な宣言文字列に揃え、かつ
// C# 通常フィールド(内部タグは `property`、BodyStyle.None、公開時に `field` へ
// 正規化)向けに、結合済みマッチ行での宣言文の終端位置を返す。signature を `;`
// まで含む完全な宣言文字列に揃え、かつ
// 同一行のパターンスキャンを `;` の次から再開できるようにするために使う。paren /
// bracket / brace の深さを追うので、初期化子(コレクション / オブジェクト初期化子や
// ラムダ本体)内の `{` / `}` で判定が途切れない。深さ 0 で出現する `}`(囲む型本体の
Expand Down Expand Up @@ -2852,6 +2854,22 @@ private static string BuildCSharpTypeHeaderSignature(
int startColumn,
int lastLineIndex,
int? lastLineExclusiveEndColumn)
{
var sanitized = BuildSanitizedCSharpMultilineSignature(
lines,
startLineIndex,
startColumn,
lastLineIndex,
lastLineExclusiveEndColumn);
return NormalizeCSharpConstraintGenericWhitespace(sanitized);
}

private static string BuildSanitizedCSharpMultilineSignature(
string[] lines,
int startLineIndex,
int startColumn,
int lastLineIndex,
int? lastLineExclusiveEndColumn)
{
// Assemble the raw slice preserving '\n' between physical lines so multi-line raw
// and verbatim string literals keep their newlines and leading indentation. The
Expand Down Expand Up @@ -2888,8 +2906,7 @@ private static string BuildCSharpTypeHeaderSignature(
rawSlice.Append(line, from, to - from);
}

var sanitized = SanitizeCSharpTypeHeaderSlice(rawSlice.ToString()).Trim();
return NormalizeCSharpConstraintGenericWhitespace(sanitized);
return SanitizeCSharpMultilineSignatureSlice(rawSlice.ToString()).Trim();
}

private static string NormalizeCSharpConstraintGenericWhitespace(string signature)
Expand Down Expand Up @@ -3000,7 +3017,7 @@ private struct CSharpHeaderFrame
public bool EscapeNext; // String / Char: true if a preceding backslash awaits its escaped char.
}

// Sanitize a C# type header slice: strip `//` line comments and `/* ... */` block
// Sanitize a multiline C# declaration slice: strip `//` line comments and `/* ... */` block
// comments, collapse runs of Code-mode whitespace (including '\n' between lines) to a
// single space, preserve all String / Verbatim / Raw / Char literal contents verbatim
// (including literal whitespace runs, line breaks inside raw / verbatim strings, and
Expand All @@ -3009,13 +3026,13 @@ private struct CSharpHeaderFrame
// whitespace inside holes is collapsed while literal content outside holes is not.
// Closes #382.
//
// C# 型ヘッダスライスのサニタイザ: `//` 行コメントと `/* ... */` ブロックコメントを
// C# 複数行宣言スライスのサニタイザ: `//` 行コメントと `/* ... */` ブロックコメントを
// 除去し、Code モードの空白列(行間の `\n` も含む)を 1 つのスペースに畳み、String /
// Verbatim / Raw / Char リテラルの中身(リテラル内の空白、raw / verbatim の行末改行、
// エスケープ列)は verbatim に残し、補間ホール(`$"{expr}"`、`$@"{expr}"`、raw
// `$"""{expr}"""` / `$$"""{{expr}}"""`)内部は Code モードとして分類してホール内の
// 空白だけを畳み、ホール外のリテラル内容は畳まないようにする。Closes #382.
private static string SanitizeCSharpTypeHeaderSlice(string input)
private static string SanitizeCSharpMultilineSignatureSlice(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
Expand Down
2 changes: 1 addition & 1 deletion src/CodeIndex/Indexer/Symbols/SymbolExtractor.Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public static partial class SymbolExtractor
public const int DefaultContractVersion = 1;
public const int ExpandedLanguageContractVersion = 2;
public const int PythonContractVersion = 2;
public const int CSharpContractVersion = 7;
public const int CSharpContractVersion = 8;
public const int DockerfileContractVersion = 2;
public const int MakefileContractVersion = 2;
public const int StyleAndXamlContractVersion = 2;
Expand Down
Loading
Loading