Summary
C# same-name local functions are resolved as a file-wide overload family instead of by lexical scope.
In ConsoleUi.Help.cs, two enclosing methods each declare a local WriteHelpLine. Calls inside each enclosing method are marked ambiguous between both definitions. A parameter named WriteHelpLine in PrintFlagReference shadows either local function, yet calls through that delegate parameter are also linked to both local definitions.
All 19 observed references are ambiguous and both local-function definitions report zero identity-scoped callers.
Environment
origin/main: c642bc4c3dda4ef659df8e6b699e10f61da3c836
cdidx v1.44.3, locally built
- fresh, complete graph with no cap hits
- full net8.0 and net9.0 suites pass
Reproduction
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll definition \
WriteHelpLine --lang csharp --exact-name \
--path 'src/CodeIndex/Cli/ConsoleUi.Help.cs' \
--fields path,line,kind,name,container_kind,container_name,signature \
--db .cdidx/codeindex.db --json --limit 10
dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll references \
WriteHelpLine --lang csharp --exact-name \
--path 'src/CodeIndex/Cli/ConsoleUi.Help.cs' \
--fields path,line,column,container_name,reference_kind,target_symbol_id,resolution_state,resolution_candidate_count \
--db .cdidx/codeindex.db --json --limit 30
Actual behavior
Definitions are extracted correctly:
line 48: WriteHelpLine, container PrintUsageFull
line 85: WriteHelpLine, container PrintFlagUsage
Lexically unique calls inside PrintUsageFull and PrintFlagUsage have resolution_state: ambiguous and resolution_candidate_count: 2.
PrintFlagReference(Action<string> WriteHelpLine) declares a delegate parameter with the same name. Calls through that parameter at lines 167-193 are also linked to both unrelated local functions.
Expected behavior
- Calls inside
PrintUsageFull resolve only to the line-48 local function.
- Calls inside
PrintFlagUsage resolve only to the line-85 local function.
- Calls through the
PrintFlagReference parameter do not target either local function.
- Method-group conversions resolve to the local function in their enclosing scope.
- References, callers, inspect bundles, impact roots, and selectors agree.
Root-cause evidence
Rows already contain useful containment: each local definition and call has its actual enclosing callable. Candidate resolution still groups by leaf name/signature without applying lexical visibility or shadowing first.
Relevant areas include C# candidate generation and scoring in src/CodeIndex/Database/DbWriter.References.cs, callable containment/ranges from the symbol extractor, and graph identity projection.
Focused PR scope
One PR should add deterministic lexical-scope filtering for:
- local-function definitions;
- calls and method-group references inside their containing callable;
- nested local functions;
- parameters and local variables that shadow callable names.
Non-goals
- Do not implement full C# semantic binding.
- Do not change constructor arity or global overload identity.
- Do not use declaration order as a visibility gate: local functions may be called before declaration.
- Do not guess when scope/range evidence is incomplete.
Implementation guidance and cautions
- Use stable enclosing symbol IDs and source ranges, not display container names alone.
- Respect nested and sibling block scopes and choose the innermost legal binding.
- Apply parameter/local shadowing before file- or type-wide name-family fallback.
- Distinguish invocation and method-group conversion while preserving the same lexical target.
- Filter lexically first, then keep overload resolution conservative.
- Prevent collisions between identical enclosing method names in unrelated types/files.
- Share scope selection across persistence and query/LSP paths.
Required tests
- Two methods with identical local-function names and calls before/after declaration.
- Nested local functions and local-function overloads.
- Delegate parameter and local-variable shadowing.
- Method-group conversions.
- Nested blocks, sibling blocks, and identical enclosing names in unrelated types.
- Lambdas, expression-bodied members, and incomplete ranges remaining ambiguous.
Assert references, callers, inspect, selectors, impact roots, and LSP position parity.
Acceptance criteria
- Calls in
PrintUsageFull and PrintFlagUsage bind only to their own local function.
- Calls through the delegate parameter bind to neither local function.
- Both local functions receive correct caller/reference evidence.
- No cross-method local-function edge remains.
- True overload ambiguity remains explicitly ambiguous.
Regression history
This is a focused follow-up to #4840, which established innermost callable containment for C# calls, local functions, and lambdas. Current rows carry that containment, but target resolution does not enforce it. Regression tests and the bilingual changelog should reference #4840.
No exact prior issue for same-name local-function target resolution and delegate-parameter shadowing was found.
Summary
C# same-name local functions are resolved as a file-wide overload family instead of by lexical scope.
In
ConsoleUi.Help.cs, two enclosing methods each declare a localWriteHelpLine. Calls inside each enclosing method are marked ambiguous between both definitions. A parameter namedWriteHelpLineinPrintFlagReferenceshadows either local function, yet calls through that delegate parameter are also linked to both local definitions.All 19 observed references are ambiguous and both local-function definitions report zero identity-scoped callers.
Environment
origin/main:c642bc4c3dda4ef659df8e6b699e10f61da3c836cdidx v1.44.3, locally builtReproduction
Actual behavior
Definitions are extracted correctly:
Lexically unique calls inside
PrintUsageFullandPrintFlagUsagehaveresolution_state: ambiguousandresolution_candidate_count: 2.PrintFlagReference(Action<string> WriteHelpLine)declares a delegate parameter with the same name. Calls through that parameter at lines 167-193 are also linked to both unrelated local functions.Expected behavior
PrintUsageFullresolve only to the line-48 local function.PrintFlagUsageresolve only to the line-85 local function.PrintFlagReferenceparameter do not target either local function.Root-cause evidence
Rows already contain useful containment: each local definition and call has its actual enclosing callable. Candidate resolution still groups by leaf name/signature without applying lexical visibility or shadowing first.
Relevant areas include C# candidate generation and scoring in
src/CodeIndex/Database/DbWriter.References.cs, callable containment/ranges from the symbol extractor, and graph identity projection.Focused PR scope
One PR should add deterministic lexical-scope filtering for:
Non-goals
Implementation guidance and cautions
Required tests
Assert references, callers, inspect, selectors, impact roots, and LSP position parity.
Acceptance criteria
PrintUsageFullandPrintFlagUsagebind only to their own local function.Regression history
This is a focused follow-up to #4840, which established innermost callable containment for C# calls, local functions, and lambdas. Current rows carry that containment, but target resolution does not enforce it. Regression tests and the bilingual changelog should reference #4840.
No exact prior issue for same-name local-function target resolution and delegate-parameter shadowing was found.