Skip to content

Preserve test.method classification across multiline C# attribute blocks #5192

Description

@Widthdom

Summary

A C# method decorated with [Theory] is classified as function instead of test.method when following [InlineData(...)] attributes span multiple physical lines or contain complex literals.

The method declaration and range are extracted correctly, but the test classifier walks attributes one physical line at a time and stops at a continuation line that does not begin with [. This is a residual of #1961, which introduced the test.method distinction.

Environment

  • origin/main: c642bc4c3dda4ef659df8e6b699e10f61da3c836
  • cdidx v1.44.3, locally built
  • fresh repository-wide index
  • full net8.0 and net9.0 suites pass

Reproduction

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll symbols \
  --name SymbolIdentity_NonCSharpBroadAmbiguityDoesNotPersistCrossProductOrAttributeCallers \
  --lang csharp \
  --path tests/CodeIndex.Tests/DbReaderSymbolIdentityTests.cs \
  --db .cdidx/codeindex.db --json=array

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll symbols \
  --name GetUnusedSymbols_MultilineAttributeLiteralWithBracketInString_KeepsReflectionContext \
  --lang csharp --path tests/CodeIndex.Tests/DbReaderTests.cs \
  --db .cdidx/codeindex.db --json=array

The first method has [Theory] at line 538, multiline InlineData blocks through line 550, and its declaration at line 551. The second has [Theory] at line 7797, complex multiline attributes through line 7814, and its declaration at line 7815.

Both rows currently return:

{"kind":"function"}

Nearby simple [Fact] and [Theory] methods remain test.method.

Expected behavior

Both methods are xUnit theories and must be emitted as test.method. Classification must survive a contiguous attribute block regardless of physical line breaks or bracket-like content inside correctly lexed literals/comments.

Root cause

IsCSharpTestMethod in src/CodeIndex/Indexer/Symbols/SymbolExtractor.DeclarationMetadata.cs:122 walks backward by physical line. Each preceding line must begin with [ and contain ] on the same line. A continuation such as a string argument terminates the scan before it reaches [Theory].

CSharpLineHasTestMethodAttribute is also physical-line-oriented and cannot represent one multiline attribute list as a lexical unit.

Focused PR scope

One PR should replace the line-only backward scan with a bounded, lexical, attribute-block-aware classifier while preserving the existing C# test-attribute taxonomy.

Non-goals

  • Do not add a Roslyn dependency.
  • Do not redesign symbol kinds or infer tests from method names alone.
  • Do not broaden this PR to non-C# frameworks/languages.
  • Do not parse arbitrary attribute constructor semantics.

Implementation guidance and cautions

  • Reuse existing C# lexical/string/comment machinery where practical.
  • Balance complete attribute blocks, ignoring brackets, commas, quotes, and comment markers inside normal, verbatim, raw, interpolated, and character literals.
  • Keep line/character/item budgets explicit.
  • Stop at real declaration/member boundaries and prevent a prior member's [Theory] leaking forward.
  • Preserve target-specified attributes such as return:, assembly:, and module: without treating them as method test markers.
  • Continue recognizing namespaced and Attribute-suffixed forms already supported.

Required tests

  • One and several multiline InlineData attributes.
  • Attributes split across brackets, parentheses, and argument lines.
  • Comments between attributes.
  • Namespaced Xunit.TheoryAttribute.
  • Normal, verbatim, raw, and interpolated strings containing brackets and triple-quote runs.
  • Same-line multiple attributes and non-test multiline attributes.
  • Member boundaries that prevent marker leakage.
  • Existing Fact, Theory, MSTest, and NUnit variants.

Assert outline, symbols --kind test.method, inspect, and exclude-tests/unused behavior.

Acceptance criteria

  • The methods at lines 551 and 7815 are emitted as test.method.
  • Existing simple classifications remain correct.
  • Complex literals do not create phantom attributes.
  • Attribute ownership never crosses a member boundary.
  • The scan remains bounded.

Regression history

This is a direct residual of #1961 and should be referenced in regression tests and the bilingual changelog. No newer exact 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