You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
C# 13 extended the partial modifier beyond methods to properties and indexers. SymbolExtractor added partial to the class / struct / interface / method regex modifier lists, but not to the property regexes (tracked separately in #228) and not to the indexer regex at :118. As a result, every partial indexer declaration and every matching implementation is silently dropped from the symbol index, even though partial indexers are a real, shipped C# 13 feature used alongside partial properties by source generators.
publicpartialclassSvc{// Declaring part — DROPPEDpublicpartialintthis[inti]{get;set;}publicpartialstringthis[stringkey]{get;}}publicpartialclassSvc{// Implementing part — DROPPEDpublicpartialintthis[inti]{get=>0;set{}}// Expression-bodied implementation — DROPPEDpublicpartialstringthis[stringkey]=>"x";}
definition this --exact, symbols --kind function filtered to the class, and inspect on the enclosing class all miss every partial indexer row. The non-partial baseline indexer (public int this[long key] { get => 0; set { } }) is captured, so the gap is specific to the partial modifier on the indexer grammar — not to the indexer regex overall.
This is distinct from #228 (partial properties). #228's scope is explicitly SymbolExtractor.cs:100 and :103 — the two property regexes. The indexer lives in its own regex at :118 with its own modifier list, so #228's fix would not cover the indexer row. Filing separately so the fix lands in the right regex.
Repro
CDIDX=/root/.local/bin/cdidx
mkdir -p /tmp/dogfood/cs-13-partial-indexer
cat > /tmp/dogfood/cs-13-partial-indexer/M.cs <<'EOF'namespace Cs13PartialIndexer;public partial class Svc{ // Declarations public partial int this[int i] { get; set; } public partial string this[string key] { get; } // Baseline: non-partial indexer (CAPTURED) public int this[long key] { get => 0; set { } } // Baseline: partial method (CAPTURED) public partial int M();}public partial class Svc{ // Partial indexer impl with block accessors public partial int this[int i] { get => 0; set { } } // Partial indexer impl with expression body public partial string this[string key] => "x"; // Baseline: partial method impl (CAPTURED) public partial int M() => 42;}EOF"$CDIDX" index /tmp/dogfood/cs-13-partial-indexer --rebuild
"$CDIDX" symbols --db /tmp/dogfood/cs-13-partial-indexer/.cdidx/codeindex.db
Observed:
function M M.cs:13 ← partial method decl, CAPTURED
function M M.cs:31 ← partial method impl, CAPTURED
class Svc M.cs:3-14
class Svc M.cs:16-32
function this M.cs:10 ← non-partial indexer, CAPTURED
namespace Cs13PartialIndexer M.cs:1
(6 symbols in 1 files)
Missing: the partial indexer declarations at M.cs:6, M.cs:7, and their implementations at M.cs:19-23, M.cs:26 — 4 out of 5 indexer rows silently lost. definition this --exact returns only the non-partial baseline, making symbols --kind function, callers, and inspect materially wrong on any source-generator-heavy C# 13 codebase that uses partial indexers.
Suspected root cause
src/CodeIndex/Indexer/SymbolExtractor.cs:118 — the C# indexer row:
The modifier alternation is static|virtual|override|abstract|sealed|new. Missing: partial (also arguably missing required and extern, but partial is the C# 13 canonical case).
Walkthrough for public partial int this[int i] { get; set; }:
visibility matches public.
(?:(?:static|virtual|override|abstract|sealed|new)\s+)* tries to match partial. partial is not in the list → zero iterations.
(?<name>this) expects the literal string this — next is int → fails.
Regex backtracks: shorter returnType (partia, parti, …) all followed by non-whitespace chars → none lead to \s+this.
Whole regex fails.
No other C# pattern in the cache matches the indexer shape:
Method regex at :94 requires \s*\( after the name — but here we have \s*\[.
Explicit-interface regex at :116 requires a dot between interface type and name — this has no dot prefix.
Property regexes :100 / :103 require \w+ after return type, but this is matched literally only by the indexer regex.
Net: silent drop. No warning, no phantom.
Methods with partial are fine because the method regex at :94 already includes partial in its modifier list. Properties with partial are covered by #228's scope, which proposes adding partial to :100 / :103. Indexers fall between the two and need the same one-word addition in :118.
Suggested direction
(A) Add partial to the indexer regex modifier alternation at :118:
partial is the primary fix (C# 13). required is not legal on indexers per the spec, but matches the property row's modifier set and is harmless. extern covers the interop case.
(B) Same expansion to the event regex at :107 for consistency:
This adds partial support for partial events (adjacent C# feature, not strictly C# 13 but same grammar shape). #334 tracks the abstract / virtual / override / sealed / new slice of this expansion — partial would be an additional entry in the same list change.
(C) Fold this into #228's approach: if #228's fix lands first, reviewing the entire C# row block and sweeping in partial across :100, :103, :107, :118 in one commit avoids relanding the same file several times.
Preferred: (A) as the focused, narrow fix for this ticket, and (B) as the adjacent hardening on the event row. Either can land independently.
Regression fixtures:
public partial int this[int i] { get; set; } → captured as indexer row.
public partial int this[int i] { get => 0; set { } } → captured.
public partial string this[string key] => "x"; → captured.
public int this[long key] { get => 0; set { } } → still captured (baseline).
public static int this[int i] { get; } → still captured (if/where legal; Roslyn allows compile errors, but the regex already handles static).
Why it matters
Partial indexers are a shipped C# 13 feature, used together with partial properties by source generators (CommunityToolkit.Mvvm, Roslyn analyzers, internal framework code). A file that exercises partial indexers drops every such declaration silently.
Silent drop.definition this, outline, inspect, callers, hotspots --kind function all under-report on partial-indexer files. No warning surfaces to the user.
Graph completeness. Impact analysis and caller/callee traversal miss every indexer access site that targets a partial indexer, because the symbol-side definition is absent.
Source-generator ViewModels. MVVM codebases with [ObservableProperty]-style generators that include indexer overloads lose their indexer members entirely.
Partial indexers are C#-specific grammar. Not applicable to other languages.
Adjacent: partial events (covered by expansion (B) above) — legality varies by C# version and is less clean-cut than partial indexers, but the regex gap is the same shape, so (B) is worth folding in alongside (A). #334 is the existing pivot for expanding the event regex modifier list; this issue nudges that expansion to also cover partial.
tests/CodeIndex.Tests/SymbolExtractorTests.cs — fixtures as listed in the Suggested direction section, plus regression for the existing non-partial indexer.
DEVELOPER_GUIDE.md language-pattern reference table — C# row's indexer entry should list partial as a supported modifier.
Summary
C# 13 extended the
partialmodifier beyond methods to properties and indexers.SymbolExtractoraddedpartialto the class / struct / interface / method regex modifier lists, but not to the property regexes (tracked separately in #228) and not to the indexer regex at:118. As a result, every partial indexer declaration and every matching implementation is silently dropped from the symbol index, even though partial indexers are a real, shipped C# 13 feature used alongside partial properties by source generators.definition this --exact,symbols --kind functionfiltered to the class, andinspecton the enclosing class all miss every partial indexer row. The non-partial baseline indexer (public int this[long key] { get => 0; set { } }) is captured, so the gap is specific to thepartialmodifier on the indexer grammar — not to the indexer regex overall.This is distinct from #228 (partial properties). #228's scope is explicitly
SymbolExtractor.cs:100and:103— the two property regexes. The indexer lives in its own regex at:118with its own modifier list, so #228's fix would not cover the indexer row. Filing separately so the fix lands in the right regex.Repro
Observed:
Missing: the partial indexer declarations at M.cs:6, M.cs:7, and their implementations at M.cs:19-23, M.cs:26 — 4 out of 5 indexer rows silently lost.
definition this --exactreturns only the non-partial baseline, makingsymbols --kind function,callers, andinspectmaterially wrong on any source-generator-heavy C# 13 codebase that uses partial indexers.Suspected root cause
src/CodeIndex/Indexer/SymbolExtractor.cs:118— the C# indexer row:The modifier alternation is
static|virtual|override|abstract|sealed|new. Missing:partial(also arguably missingrequiredandextern, butpartialis the C# 13 canonical case).Walkthrough for
public partial int this[int i] { get; set; }:visibilitymatchespublic.(?:(?:static|virtual|override|abstract|sealed|new)\s+)*tries to matchpartial.partialis not in the list → zero iterations.partial int this[int i] { get; set; }.returnType (?:global::)?[\w?.<>\[\],:]+greedy matchespartial.\s+matches space.(?<name>this)expects the literal stringthis— next isint→ fails.returnType(partia,parti, …) all followed by non-whitespace chars → none lead to\s+this.No other C# pattern in the cache matches the indexer shape:
:94requires\s*\(after the name — but here we have\s*\[.:116requires a dot between interface type and name —thishas no dot prefix.:100/:103require\w+after return type, butthisis matched literally only by the indexer regex.Net: silent drop. No warning, no phantom.
Methods with
partialare fine because the method regex at:94already includespartialin its modifier list. Properties withpartialare covered by #228's scope, which proposes addingpartialto:100/:103. Indexers fall between the two and need the same one-word addition in:118.Suggested direction
(A) Add
partialto the indexer regex modifier alternation at:118:partialis the primary fix (C# 13).requiredis not legal on indexers per the spec, but matches the property row's modifier set and is harmless.externcovers the interop case.(B) Same expansion to the event regex at
:107for consistency:This adds
partialsupport for partial events (adjacent C# feature, not strictly C# 13 but same grammar shape). #334 tracks theabstract/virtual/override/sealed/newslice of this expansion —partialwould be an additional entry in the same list change.(C) Fold this into #228's approach: if #228's fix lands first, reviewing the entire C# row block and sweeping in
partialacross:100,:103,:107,:118in one commit avoids relanding the same file several times.Preferred: (A) as the focused, narrow fix for this ticket, and (B) as the adjacent hardening on the event row. Either can land independently.
Regression fixtures:
public partial int this[int i] { get; set; }→ captured as indexer row.public partial int this[int i] { get => 0; set { } }→ captured.public partial string this[string key] => "x";→ captured.public int this[long key] { get => 0; set { } }→ still captured (baseline).public static int this[int i] { get; }→ still captured (if/where legal; Roslyn allows compile errors, but the regex already handles static).Why it matters
definition this,outline,inspect,callers,hotspots --kind functionall under-report on partial-indexer files. No warning surfaces to the user.[ObservableProperty]-style generators that include indexer overloads lose their indexer members entirely.public partial string Name { get; set; }) silently dropped —partialnot in property-regex modifier list #228 for properties.Cross-language note
Partial indexers are C#-specific grammar. Not applicable to other languages.
Adjacent: partial events (covered by expansion (B) above) — legality varies by C# version and is less clean-cut than partial indexers, but the regex gap is the same shape, so (B) is worth folding in alongside (A). #334 is the existing pivot for expanding the event regex modifier list; this issue nudges that expansion to also cover
partial.Scope
src/CodeIndex/Indexer/SymbolExtractor.cs:118— indexer regex modifier list — addpartial(and optionallyrequired/extern).src/CodeIndex/Indexer/SymbolExtractor.cs:107— event regex modifier list — expand to includepartial(overlaps with C# events withabstract/virtual/override/sealed/newmodifiers are silently dropped from the symbol index #334's scope).tests/CodeIndex.Tests/SymbolExtractorTests.cs— fixtures as listed in the Suggested direction section, plus regression for the existing non-partial indexer.DEVELOPER_GUIDE.mdlanguage-pattern reference table — C# row's indexer entry should listpartialas a supported modifier.Related
public partial string Name { get; set; }) silently dropped —partialnot in property-regex modifier list #228 — C# 13 partial properties silently dropped. Same C# 13 feature family (partial members), different regex. Fix is the same pattern (addpartialto modifier list) applied to a different row.abstract/virtual/override/sealed/newmodifiers are silently dropped from the symbol index #334 — C# events withabstract/virtual/override/sealed/newmodifiers dropped. Same event regex (:107), adjacent modifier gap. Expansion (B) above is a superset of C# events withabstract/virtual/override/sealed/newmodifiers are silently dropped from the symbol index #334's fix.file interface Foo(C# 11 file-scoped interface) is silently dropped whilefile class/file struct/file enum/file recordare captured #335 / C#file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302 — C#file interfacesilently dropped. Adjacent missing-modifier-on-row-type family.operator checked +/operator checked -(C# 11 user-defined checked operators) are dropped from the symbol index #238 —operator checkeddropped. Same "C# 11+ feature, regex missing modifier" family.ref/ref readonlyreturn types on methods and properties are silently dropped —public ref T Find(...)produces zero symbols #224 —ref/ref readonlyreturn. Adjacent modifier-gap family.=>on the next line is silently dropped — related to but distinct from #229 (block-{): regex requires=>on the same physical line as the name #345 — expression-bodied property with=>on next line. Adjacent wrapping family.{on the next line (block style) are silently dropped — only same-line{ get; set; }is captured #229 — block-style property with{on next line. Adjacent wrapping family.partial,required,readonly) + tuple-suffix return type emit phantomfunction partial/function required/function readonlyrows via ctor-regex fallback #349 — ctor-regex fallback phantoms forpartial/required/readonly+ tuple-suffix. Different mechanism, samepartialkeyword in the culprit role.Environment
/root/.local/bin/cdidx)./tmp/dogfood/cs-13-partial-indexer/M.cs(inline in Repro).CLOUD_BOOTSTRAP_PROMPT.md.