Skip to content

Prevent C# function extraction from silently stopping partway through a file #5182

Description

@Widthdom

Summary

The C# symbol extractor can silently stop emitting function symbols partway through a valid file while continuing to emit later classes, structs, enums, fields, and properties.

Two production files currently demonstrate the same cutoff pattern:

  • SymbolExtractor.CSharpScanner.cs emits no functions after line 443;
  • DbDebug.cs emits no functions after line 278.

This combines the original scanner-tail and missing-extension-method findings because ExecuteTrackedReader and TrackedRead are only two members of a much broader function-emission cutoff in DbDebug.cs. One extractor fix and regression matrix should cover both files.

Environment

  • origin/main: c642bc4c3dda4ef659df8e6b699e10f61da3c836
  • cdidx v1.44.3, locally built
  • fresh repository index: 1,430 files, 52,258 symbols, 517,039 references
  • index_complete: true, reference_graph_complete: true, no extraction cap hits
  • build: 0 warnings / 0 errors
  • full net8.0 and net9.0 suites: 0 failures

Reproduction

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll outline \
  src/CodeIndex/Indexer/Symbols/SymbolExtractor.CSharpScanner.cs \
  --kind function --outline-fields name,line,signature \
  --db .cdidx/codeindex.db --json

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll definition \
  CanContinueScanningSameLineBraceBody --exact-name \
  --path 'src/CodeIndex/Indexer/Symbols/SymbolExtractor.CSharpScanner.cs' \
  --db .cdidx/codeindex.db --json

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll outline \
  src/CodeIndex/Database/DbDebug.cs --kind function \
  --db .cdidx/codeindex.db --json

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll definition \
  ExecuteTrackedReader --exact-name --strict-not-found \
  --db .cdidx/codeindex.db

Actual behavior

SymbolExtractor.CSharpScanner.cs has 4,402 lines, but only eight function rows. The final one is FindCSharpBraceRange at lines 281-443. Valid class-level methods beginning with StripLeadingCSharpAttributeLists at line 445 and CanContinueScanningSameLineBraceBody at line 763 are absent. The latter declaration is visible in excerpt and its semantic token is marked as a method declaration, yet definition returns E018_QUERY_NOT_FOUND.

In DbDebug.cs, GetStatementOperation at lines 269-278 is the final emitted function. Later methods are missing, including:

  • WriteSlowQueryToStderr at line 280;
  • ExecuteTrackedReader at line 647;
  • TrackedRead at line 658;
  • QueryProfileEntry.AddElapsed at line 682;
  • QueryProfileEntry.MarkCompletedIfSlow at line 686.

The missing extension-method declaration is itself stored as an unresolved call-like reference, and its real calls remain name-only unresolved evidence.

Expected behavior

  • Every valid method after the trigger remains independently extractable.
  • One failed or skipped declaration candidate cannot poison the remainder of a file.
  • Declarations are not emitted as unresolved self-like calls.
  • outline, definition, inspect, references, and graph commands agree on the restored identities.
  • If bounded extraction genuinely cannot continue, the index reports a bounded diagnostic rather than silently claiming complete coverage.

Root-cause evidence

Relevant implementation areas include:

  • C# method patterns in src/CodeIndex/Indexer/Symbols/SymbolExtractor.Patterns.cs;
  • multiline method-header construction and candidate advancement in SymbolExtractor.CSharpScanner.cs;
  • BuildCSharpMatchLines and leading-attribute/lexical state handling;
  • method/property header merging and bounded-regex failure handling.

The PR should first reduce each cutoff to a small fixture and establish the exact state transition. Increasing a regex timeout without explaining the persistent tail suppression is not sufficient.

Focused PR scope

One PR should:

  1. add minimal fixtures reproducing both cutoff shapes;
  2. repair the shared declaration-local state, loop advancement, or bounded-match failure;
  3. restore later method definitions with correct containers, ranges, and signatures;
  4. remove declaration-as-reference artifacts;
  5. restore identity-backed call/reference rows where conservative resolution is possible;
  6. add a diagnostic for any remaining unrecoverable bounded failure;
  7. add the required bilingual changelog fragment.

Non-goals

  • Do not special-case these filenames or method names.
  • Do not add a Roslyn dependency.
  • Do not redesign graph fallback confidence in this PR; that is tracked separately.
  • Do not broadly rewrite the C# extractor.
  • Do not replace a visible omission with unbounded lookahead or catastrophic regex work.

Implementation cautions

  • Reset only declaration-local state at proven member boundaries; preserve string, comment, attribute, and multiline-header state inside a valid construct.
  • Preserve calls before local declarations, generics, tuple/nullable returns, ref/out/this parameters, expression bodies, constructors, operators, and partial methods.
  • Ensure timeout/failure diagnostics remain sanitized and bounded.
  • Use semantic tokens as corroborating evidence, not as a second divergent method parser.

Required tests

  • A long multiline method followed by several simple methods.
  • A normal method followed by a second top-level class with methods.
  • A static extension class with consecutive this receiver methods.
  • Block- and expression-bodied methods, constructors, operators, local functions, and properties after the trigger.
  • Multiline generic, tuple, and nullable signatures.
  • Strings, comments, collection expressions, ranges, indexers, and attributes near the boundary.
  • An adversarial bounded-match failure that does not suppress later methods.

Assert complete outline/definition/inspect parity, correct ranges and containers, no declaration-as-unresolved-call rows, no new phantom functions, and no cap/timeout regression.

Acceptance criteria

  • CanContinueScanningSameLineBraceBody, ExecuteTrackedReader, TrackedRead, AddElapsed, and MarkCompletedIfSlow are indexed as functions.
  • Functions after lines 443 and 278 in the two dogfood files are no longer absent.
  • Later structural symbols remain unchanged.
  • The index does not claim complete extraction after an unrecoverable silent cutoff.
  • Full net8.0 and net9.0 suites pass.

Duplicate preflight

Open and closed issues were searched for the affected method names, C# scanner tail loss, function-emission cutoff, and extension-method extraction. No exact existing issue was found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codebugSomething isn't workingtests

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions