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# nested new delegate and new enum (hiding base-class nested delegate / enum) are silently dropped — new missing from delegate-regex (:105) and enum-regex (:75) modifier lists #353
C# allows a derived class to hide an inherited nested type using the new modifier: public new enum E { A }, public new delegate int D();, public new class C { }, etc. SymbolExtractor added new to the class regex at :81 and the struct regex at :78, so new class and new struct are captured. But new is missing from the delegate row at :105 (modifier list static|unsafe) and the enum row at :75 (modifier list file only). As a result, every new delegate and new enum declaration in a derived class is silently dropped from the symbol index, even though this shape is standard C# inheritance hiding and appears in any API that evolves nested-type contracts across a class hierarchy.
publicclassBase{publicinterfaceIFoo{}publicdelegateintHandler();publicenumKind{A}}publicclassDerived:Base{publicnewclassNestedCls{}// CAPTURED — class row has `new`publicnewstructNestedStr{}// CAPTURED — struct row has `new`publicnewinterfaceIFoo{}// DROPPED — #302/#335 already scoped to cover thispublicnewdelegateintHandler();// DROPPED — THIS issuepublicnewenumKind{A}// DROPPED — THIS issue}
definition Handler misses the Derived.Handler declaration entirely, outline Derived.cs under-reports nested members, and symbols --kind delegate / --kind enum with a path filter on the derived class shows zero rows for the new-hidden types.
CDIDX=/root/.local/bin/cdidx
mkdir -p /tmp/dogfood/cs-new-nested
cat > /tmp/dogfood/cs-new-nested/N.cs <<'EOF'namespace CsNewNested;public class Base{ public interface IFoo { } public delegate int Handler(); public class NestedCls { } public struct NestedStr { } public enum NestedEnm { A }}public class Derived : Base{ // `new` hides base-class nested types public new interface IFoo { } // DROPPED (#302/#335 scope) public new delegate int Handler(); // DROPPED (this issue) public new class NestedCls { } // CAPTURED (class row has `new`) public new struct NestedStr { } // CAPTURED (struct row has `new`) public new enum NestedEnm { A } // DROPPED (this issue)}EOF"$CDIDX" index /tmp/dogfood/cs-new-nested --rebuild
"$CDIDX" symbols --db /tmp/dogfood/cs-new-nested/.cdidx/codeindex.db
Observed:
class Base N.cs:3-10
class Derived N.cs:12-21
delegate Handler N.cs:6 ← base delegate
interface IFoo N.cs:5 ← base interface
class NestedCls N.cs:7 ← base
class NestedCls N.cs:17 ← `new` derived ✓
enum NestedEnm N.cs:9 ← base
struct NestedStr N.cs:8 ← base
struct NestedStr N.cs:18 ← `new` derived ✓
namespace CsNewNested N.cs:1
(10 symbols in 1 files)
Missing: Handler at N.cs:16 (public new delegate int Handler()), NestedEnm at N.cs:20 (public new enum NestedEnm { A }), plus IFoo at N.cs:15 (covered by #302/#335 — out of scope here).
For inheritance-hiding delegates: definition Handler returns only the Base.Handler declaration, so callers of the Derived.Handler (distinct delegate type in C#'s name-resolution model) cannot be located by callers Handler and inspect Handler fails to surface both rows.
For inheritance-hiding enums: same story for definition NestedEnm; outline Derived.cs misrepresents the derived class's nested-type surface.
Suspected root cause (from reading the source)
Row A — delegate at src/CodeIndex/Indexer/SymbolExtractor.cs:105:
Modifier alternation is static|unsafe, using ? (zero or one). Missing:new, file (#303 tracks this), partial (arguably — not legal on delegates per spec, but worth noting), readonly (not legal).
Walkthrough for public new delegate int Handler();:
^\s* eats leading whitespace.
visibility matches public + trailing space.
(?:(?:static|unsafe)\s+)? attempts to match new. new is not in the list → zero iterations.
Cursor at new delegate int Handler();.
delegate\s+ literal expects delegate — next non-space token is new → fails.
No backtrack options; the regex never matches the line.
Compare with the class row :81 modifier list static|partial|abstract|sealed|readonly|file|new|unsafe — includes new, which is how public new class NestedCls { } is captured. The delegate row was not updated alongside.
Row B — enum at src/CodeIndex/Indexer/SymbolExtractor.cs:75:
The enum row's modifier slot is already *, so combinations are supported.
(C) Align interface regex :73 with the same edit — new interface is already proposed in #302 / #335's suggested fixes. Landing (A), (B), and the new edit from #302 / #335 together is cleaner than three separate commits.
(D) Tests. Add fixtures to SymbolExtractorTests.cs covering:
public new delegate int Handler(); → captured as delegate row.
public new enum Kind { A } → captured as enum row.
public new enum Kind : byte { A } (with explicit underlying type) → captured.
Regression: public delegate int Handler(); / file delegate int D(); / public enum Kind { A } / file enum E { A } still captured.
Why it matters
Inheritance-hiding nested types are the idiomatic way to evolve a nested-type contract between a base class and its derived classes, particularly in framework code that ships versioned nested enums / delegates. Examples from the BCL: Nullable<T>-style wrappers, IEnumerator variants across generic/non-generic namespaces, WPF/WinForms inheritance hierarchies.
Silent drop.definition Handler / definition NestedEnm returns only the base-class declaration, not the derived override — an AI agent asked "where is Derived.Handler defined?" is told the answer is Base, which is structurally wrong.
Graph completeness.callers Handler / impact Handler cannot reach the derived row because the derived symbol is absent from the index.
Very small fix: two modifier words in two regex rows, each protected by a regression fixture.
Cross-language note
C#-specific. The new member-hiding modifier is C#-specific grammar. Java uses @Override semantics; Kotlin, Swift, Rust, TypeScript handle nested type hiding through different (or no) mechanisms.
The fix is C#-scoped; no peer language rows are affected.
tests/CodeIndex.Tests/SymbolExtractorTests.cs — fixtures listed in the Suggested direction (D) section, plus regressions for the existing non-new baseline.
DEVELOPER_GUIDE.md language-pattern reference table — C# delegate and enum row entries should list new as a supported modifier.
Summary
C# allows a derived class to hide an inherited nested type using the
newmodifier:public new enum E { A },public new delegate int D();,public new class C { }, etc.SymbolExtractoraddednewto the class regex at:81and the struct regex at:78, sonew classandnew structare captured. Butnewis missing from the delegate row at:105(modifier liststatic|unsafe) and the enum row at:75(modifier listfileonly). As a result, everynew delegateandnew enumdeclaration in a derived class is silently dropped from the symbol index, even though this shape is standard C# inheritance hiding and appears in any API that evolves nested-type contracts across a class hierarchy.definition Handlermisses theDerived.Handlerdeclaration entirely,outline Derived.csunder-reports nested members, andsymbols --kind delegate/--kind enumwith a path filter on the derived class shows zero rows for thenew-hidden types.This issue is distinct from:
file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302 / C#file interface Foo(C# 11 file-scoped interface) is silently dropped whilefile class/file struct/file enum/file recordare captured #335 —file interfacedrops. Both issues propose addingnewto the interface regex at:73as a secondary defensive fix, sonew interfaceis already covered under those tickets. This issue is specifically about the delegate and enum rows, which have no open issue coveringnew.file delegateis silently dropped by symbol extractor while plain /public/unsafedelegates are captured #303 —file delegatedrops. Same regex:105, different missing keyword (filevsnew). The two fixes land on the same modifier list and can be consolidated, but they are semantically independent.abstract/virtual/override/sealed/newmodifiers are silently dropped from the symbol index #334 — event modifier list narrower than grammar. Same "missing modifier on a type-family regex" pattern, different row.public partial int this[int i] { get; set; }) are silently dropped —partialnot in indexer-regex modifier list #350 — partial indexers. Same pattern, different row and modifier.Repro
Observed:
Missing:
Handlerat N.cs:16 (public new delegate int Handler()),NestedEnmat N.cs:20 (public new enum NestedEnm { A }), plusIFooat N.cs:15 (covered by #302/#335 — out of scope here).For inheritance-hiding delegates:
definition Handlerreturns only theBase.Handlerdeclaration, so callers of theDerived.Handler(distinct delegate type in C#'s name-resolution model) cannot be located bycallers Handlerandinspect Handlerfails to surface both rows.For inheritance-hiding enums: same story for
definition NestedEnm;outline Derived.csmisrepresents the derived class's nested-type surface.Suspected root cause (from reading the source)
Row A — delegate at
src/CodeIndex/Indexer/SymbolExtractor.cs:105:Modifier alternation is
static|unsafe, using?(zero or one). Missing:new,file(#303 tracks this),partial(arguably — not legal on delegates per spec, but worth noting),readonly(not legal).Walkthrough for
public new delegate int Handler();:^\s*eats leading whitespace.visibilitymatchespublic+ trailing space.(?:(?:static|unsafe)\s+)?attempts to matchnew.newis not in the list → zero iterations.new delegate int Handler();.delegate\s+literal expectsdelegate— next non-space token isnew→ fails.Compare with the class row
:81modifier liststatic|partial|abstract|sealed|readonly|file|new|unsafe— includesnew, which is howpublic new class NestedCls { }is captured. The delegate row was not updated alongside.Row B — enum at
src/CodeIndex/Indexer/SymbolExtractor.cs:75:Modifier alternation is
fileonly. Missing:new(this issue), alsounsafe(not legal per spec, but other type rows include it as defensive).Walkthrough for
public new enum NestedEnm { A }:visibilitymatchespublic.(?:(?:file)\s+)*triesnew— not in list → zero iterations.new enum NestedEnm { A }.enum\s+literal expectsenum— next isnew→ fails.No other C# pattern rescues the line:
:81requires(?:record\s+class\s+|record\s+|class\s+)—enumdoesn't match any of those.:78requiresstruct— fails.:73requiresinterface— fails.Net: silent drop. No warning, no phantom.
Suggested direction
(A) Add
newto the delegate regex modifier alternation at:105:Key details:
*instead of?so combinations likestatic newornew unsafeboth parse. (Order is free in C#;new staticandstatic neware both legal.)fileis added to cover C#file delegateis silently dropped by symbol extractor while plain /public/unsafedelegates are captured #303 in the same commit. If a reviewer prefers to keep this ticket scope-focused,filecan be deferred.(B) Add
newto the enum regex modifier alternation at:75:The enum row's modifier slot is already
*, so combinations are supported.(C) Align interface regex
:73with the same edit —new interfaceis already proposed in #302 / #335's suggested fixes. Landing (A), (B), and thenewedit from #302 / #335 together is cleaner than three separate commits.(D) Tests. Add fixtures to
SymbolExtractorTests.cscovering:public new delegate int Handler();→ captured as delegate row.public new delegate T Gen<T>(T x);→ captured.public new unsafe delegate int* P();(delegate row also intersects with C#: methods with pointer / function-pointer return types are dropped from the symbol index (int*,void**,delegate*<...>,int*[]) #234 for pointer return; assert the delegate row itself fires and routes to :105 rather than dropping).public new enum Kind { A }→ captured as enum row.public new enum Kind : byte { A }(with explicit underlying type) → captured.public delegate int Handler();/file delegate int D();/public enum Kind { A }/file enum E { A }still captured.Why it matters
Nullable<T>-style wrappers,IEnumeratorvariants across generic/non-generic namespaces, WPF/WinForms inheritance hierarchies.definition Handler/definition NestedEnmreturns only the base-class declaration, not the derived override — an AI agent asked "where isDerived.Handlerdefined?" is told the answer isBase, which is structurally wrong.callers Handler/impact Handlercannot reach the derived row because the derived symbol is absent from the index.file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302 / C#file delegateis silently dropped by symbol extractor while plain /public/unsafedelegates are captured #303 / C#file interface Foo(C# 11 file-scoped interface) is silently dropped whilefile class/file struct/file enum/file recordare captured #335.Cross-language note
newmember-hiding modifier is C#-specific grammar. Java uses@Overridesemantics; Kotlin, Swift, Rust, TypeScript handle nested type hiding through different (or no) mechanisms.Scope
src/CodeIndex/Indexer/SymbolExtractor.cs:105— delegate regex modifier list — addnew(and optionallyfilefor C#file delegateis silently dropped by symbol extractor while plain /public/unsafedelegates are captured #303).src/CodeIndex/Indexer/SymbolExtractor.cs:75— enum regex modifier list — addnew.src/CodeIndex/Indexer/SymbolExtractor.cs:73(interface) — C#file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302 / C#file interface Foo(C# 11 file-scoped interface) is silently dropped whilefile class/file struct/file enum/file recordare captured #335's scope — so all three rows receive thenewaddition in one commit.tests/CodeIndex.Tests/SymbolExtractorTests.cs— fixtures listed in the Suggested direction (D) section, plus regressions for the existing non-newbaseline.DEVELOPER_GUIDE.mdlanguage-pattern reference table — C# delegate and enum row entries should listnewas a supported modifier.Related
file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302 —file interfacedropped. Same root cause family (modifier list omission). Interface regex:73. Already mentionsnewin its suggested fix.file interface Foo(C# 11 file-scoped interface) is silently dropped whilefile class/file struct/file enum/file recordare captured #335 — same as C#file interfaceis silently dropped by symbol extractor whilefile class/file struct/file record/file enumare captured #302, second filing. Both should reference each other and this ticket.file delegateis silently dropped by symbol extractor while plain /public/unsafedelegates are captured #303 —file delegatedropped. Same regex:105, different missing keyword (file). Consolidatingnew+fileon the delegate row is a natural single-commit fix.abstract/virtual/override/sealed/newmodifiers are silently dropped from the symbol index #334 — event modifier list too narrow. Same family on the event row.public partial int this[int i] { get; set; }) are silently dropped —partialnot in indexer-regex modifier list #350 — C# 13 partial indexers. Same family on the indexer row.public partial string Name { get; set; }) silently dropped —partialnot in property-regex modifier list #228 — C# 13 partial properties. Same family on the property rows.readonlyinstance indexers on structs (public readonly int this[int i] => ...) are silently dropped —readonlynot in indexer-regex modifier list #352 —readonlyindexer drops. Same family on the indexer row.partial,required,readonly) + tuple-suffix return type emit phantomfunction partial/function required/function readonlyrows via ctor-regex fallback #349 — contextual-keyword modifier phantoms via ctor-regex fallback. Different mechanism; none of thenew-hidden nested types in this issue produce a phantom (they all silently drop).Environment
/root/.local/bin/cdidx)./tmp/dogfood/cs-new-nested/N.cs(inline in Repro).CLOUD_BOOTSTRAP_PROMPT.md.