Skip to content

[duplicate-code] Duplicate Code: AssemblyInitializeShouldBeValidAnalyzer and AssemblyCleanupShouldBeValidAnalyzer use inconsistent, partially-dup [Content truncated due to length] #9026

@Evangelink

Description

@Evangelink

🔍 Duplicate Code Detected: Inconsistent and Duplicated Patterns in Assembly-Level Fixture Analyzers

Analysis of commit 741048f

Assignee: @copilot

Summary

AssemblyInitializeShouldBeValidAnalyzer and AssemblyCleanupShouldBeValidAnalyzer (plus ClassInitializeShouldBeValidAnalyzer) each contain copy-pasted initialization boilerplate that manually calls TryGetOrCreateTypeByMetadataName instead of using the existing FixtureMethodAnalyzerHelper.RegisterFixtureMethodSymbolAction / TryGetFixtureMethodSymbols helpers that were introduced for the same purpose. This leaves two different patterns in the codebase for doing the same job: the modern helper-based approach used by TestInitializeShouldBeValidAnalyzer / TestCleanupShouldBeValidAnalyzer, and the older inline approach still used by the assembly/class-level analyzers.

Duplication Details

Pattern 1: Inline symbol resolution in Initialize — duplicated across 3 assembly/class analyzers

  • Severity: Medium

  • Occurrences: 3 analyzers using the old pattern (vs 2 that already use the helper)

  • Locations (old pattern):

    • src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs (lines 38–52) — calls TryGetOrCreateTypeByMetadataName 3 times inline
    • src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs (lines 40–49) — calls TryGetFixtureMethodSymbols but then re-fetches testContextSymbol separately
    • src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs (lines 40–56) — calls TryGetOrCreateTypeByMetadataName 3 times inline
    • src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs (lines 40–49) — calls TryGetFixtureMethodSymbols but then re-fetches testContextSymbol + inheritanceBehaviorSymbol separately
  • Locations (new pattern — for reference):

    • src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs (line 41) — single call to RegisterFixtureMethodSymbolAction
    • src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs (line 41) — single call to RegisterFixtureMethodSymbolAction
  • Code Sample — old pattern (AssemblyInitializeShouldBeValidAnalyzer):

context.RegisterCompilationStartAction(context =>
{
    if (context.Compilation.TryGetOrCreateTypeByMetadataName(
            WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyInitializeAttribute,
            out INamedTypeSymbol? assemblyInitializeAttributeSymbol)
        && context.Compilation.TryGetOrCreateTypeByMetadataName(
            WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext,
            out INamedTypeSymbol? testContextSymbol)
        && context.Compilation.TryGetOrCreateTypeByMetadataName(
            WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute,
            out INamedTypeSymbol? testClassAttributeSymbol))
    {
        INamedTypeSymbol? taskSymbol = ...;
        INamedTypeSymbol? valueTaskSymbol = ...;
        bool canDiscoverInternals = ...;
        context.RegisterSymbolAction(...);
    }
});
  • Code Sample — new pattern (TestInitializeShouldBeValidAnalyzer):
context.RegisterCompilationStartAction(context =>
    FixtureMethodAnalyzerHelper.RegisterFixtureMethodSymbolAction(
        context,
        WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute,
        static (symbolContext, symbols) => AnalyzeSymbol(
            symbolContext, symbols.FixtureAttributeSymbol,
            symbols.TaskSymbol, symbols.ValueTaskSymbol,
            symbols.TestClassAttributeSymbol, symbols.CanDiscoverInternals)));

Pattern 2: testContextSymbol re-fetched separately after TryGetFixtureMethodSymbols in Cleanup analyzers

  • AssemblyCleanupShouldBeValidAnalyzer and ClassCleanupShouldBeValidAnalyzer both call TryGetFixtureMethodSymbols (which already resolves most symbols) but then immediately call GetOrCreateTypeByMetadataName a second time to get testContextSymbol, which is not included in FixtureMethodSymbols. This is partially addressed by the helper but not fully.

Impact Analysis

  • Maintainability: Two different coding styles for the same initialization task make it harder to understand the codebase and to update all analyzers consistently.
  • Bug Risk: If FixtureMethodSymbols or RegisterFixtureMethodSymbolAction is changed (e.g., to add a new symbol), analyzers using the old pattern will not benefit automatically.
  • Code Bloat: ~15 additional lines per analyzer that could be replaced by a single helper call.

Refactoring Recommendations

  1. Migrate old-pattern analyzers to RegisterFixtureMethodSymbolAction

    • AssemblyInitializeShouldBeValidAnalyzer and ClassInitializeShouldBeValidAnalyzer can call RegisterFixtureMethodSymbolAction; the TestContext symbol they need can be resolved inside the lambda.
    • Estimated effort: Low (mechanical substitution per file)
    • Benefits: Consistent pattern across all 6 fixture lifecycle analyzers
  2. Extend FixtureMethodSymbols to optionally carry TestContextSymbol

    • The FixtureMethodSymbols record in FixtureMethodAnalyzerHelper.cs could be extended with an optional TestContextSymbol field to eliminate the second GetOrCreateTypeByMetadataName call in the Cleanup analyzers.
    • Alternatively, add an overload that accepts a flag indicating whether TestContext is needed.
    • Estimated effort: Low
    • Benefits: Fully eliminates post-helper re-fetching

Implementation Checklist

  • Review duplication findings
  • Migrate AssemblyInitializeShouldBeValidAnalyzer to use RegisterFixtureMethodSymbolAction
  • Migrate ClassInitializeShouldBeValidAnalyzer to use RegisterFixtureMethodSymbolAction
  • Optionally extend FixtureMethodSymbols to include TestContextSymbol and update the Cleanup analyzers
  • Run all analyzer unit tests
  • Verify no functionality broken

Analysis Metadata

  • Analyzed Files: 4 (AssemblyInitialize/Cleanup, ClassInitialize/Cleanup ShouldBeValidAnalyzer)
  • Detection Method: Semantic code analysis (pattern comparison)
  • Commit: 741048f
  • Analysis Date: 2026-06-11

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Duplicate Code Detector workflow.{ai_credits_suffix} · [◷]( · )

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
  • expires on Jun 13, 2026, 6:01 AM UTC

Metadata

Metadata

Labels

type/automationCreated or maintained by an agentic workflow.type/tech-debtCode health, refactoring, simplification.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions