Skip to content

[duplicate-code] Duplicate Code: TestInitializeShouldBeValidAnalyzer and TestCleanupShouldBeValidAnalyzer have identical AnalyzeSymbol implementa [Content truncated due to length] #9025

@Evangelink

Description

@Evangelink

🔍 Duplicate Code Detected: Identical AnalyzeSymbol in TestInitialize and TestCleanup Analyzers

Analysis of commit 741048f

Assignee: @copilot

Summary

TestInitializeShouldBeValidAnalyzer and TestCleanupShouldBeValidAnalyzer are near-identical classes (66 lines each). Their AnalyzeSymbol method bodies are byte-for-byte identical — both call HasValidFixtureMethodSignature with exactly the same arguments (shouldBeStatic: false, allowGenericType: true, FixtureParameterMode.MustNotHaveTestContext, testContextSymbol: null, fixtureAllowInheritedTestClass: true). The only structural difference between the two classes is the name of the MSTest attribute they validate.

Duplication Details

Pattern: Identical AnalyzeSymbol method body across two fixture lifecycle analyzers

  • Severity: Medium

  • Occurrences: 2 identical implementations (66-line files, 10-line duplicated method body)

  • Locations:

    • src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs (lines 53–65)
    • src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs (lines 53–65)
  • Code Sample (identical in both files — only the first parameter name differs):

private static void AnalyzeSymbol(
    SymbolAnalysisContext context,
    INamedTypeSymbol testInitialize_or_testCleanupAttributeSymbol,  // name differs
    INamedTypeSymbol? taskSymbol,
    INamedTypeSymbol? valueTaskSymbol,
    INamedTypeSymbol testClassAttributeSymbol,
    bool canDiscoverInternals)
{
    var methodSymbol = (IMethodSymbol)context.Symbol;
    if (methodSymbol.HasAttribute(testInitialize_or_testCleanupAttributeSymbol)
        && !methodSymbol.HasValidFixtureMethodSignature(
            taskSymbol, valueTaskSymbol, canDiscoverInternals,
            shouldBeStatic: false,
            allowGenericType: true,
            FixtureParameterMode.MustNotHaveTestContext,
            testContextSymbol: null,
            testClassAttributeSymbol,
            fixtureAllowInheritedTestClass: true,
            out bool isFixable))
    {
        context.ReportDiagnostic(isFixable
            ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name)
            : methodSymbol.CreateDiagnostic(Rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name));
    }
}

The Initialize method is also structurally identical (both delegate to FixtureMethodAnalyzerHelper.RegisterFixtureMethodSymbolAction with the same lambda shape), and the class-level boilerplate (SupportedDiagnostics, DiagnosticDescriptorHelper.Create call shape) is copy-pasted.

Impact Analysis

  • Maintainability: Any change to the fixture validation logic (e.g., updating HasValidFixtureMethodSignature arguments) must be applied to both files in sync.
  • Bug Risk: If one file is updated and the other is not, the two analyzers will silently diverge.
  • Code Bloat: ~20 lines of identical code across two files in the same project.

Refactoring Recommendations

  1. Extend FixtureMethodAnalyzerHelper with a shared AnalyzeInstanceFixtureMethod helper
    • Add a static method to src/Analyzers/MSTest.Analyzers/Helpers/FixtureMethodAnalyzerHelper.cs:
    internal static void AnalyzeInstanceFixtureMethod(
        SymbolAnalysisContext context,
        INamedTypeSymbol fixtureAttributeSymbol,
        INamedTypeSymbol? taskSymbol,
        INamedTypeSymbol? valueTaskSymbol,
        INamedTypeSymbol testClassAttributeSymbol,
        bool canDiscoverInternals,
        DiagnosticDescriptor rule)
    {
        var methodSymbol = (IMethodSymbol)context.Symbol;
        if (methodSymbol.HasAttribute(fixtureAttributeSymbol)
            && !methodSymbol.HasValidFixtureMethodSignature(
                taskSymbol, valueTaskSymbol, canDiscoverInternals,
                shouldBeStatic: false, allowGenericType: true,
                FixtureParameterMode.MustNotHaveTestContext, testContextSymbol: null,
                testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable))
        {
            context.ReportDiagnostic(isFixable
                ? methodSymbol.CreateDiagnostic(rule, methodSymbol.Name)
                : methodSymbol.CreateDiagnostic(rule, DiagnosticDescriptorHelper.CannotFixProperties, methodSymbol.Name));
        }
    }
    • Both TestInitializeShouldBeValidAnalyzer and TestCleanupShouldBeValidAnalyzer can then collapse their private AnalyzeSymbol to a single line delegating to the helper.
    • Estimated effort: Low (~30 lines changed)
    • Benefits: Single implementation of instance-fixture validation logic; changes propagate automatically

Implementation Checklist

  • Review duplication findings
  • Add AnalyzeInstanceFixtureMethod (or equivalent) to FixtureMethodAnalyzerHelper
  • Update TestInitializeShouldBeValidAnalyzer and TestCleanupShouldBeValidAnalyzer to call the shared method
  • Run analyzer unit tests to verify no behaviour change
  • Verify no functionality broken

Analysis Metadata

  • Analyzed Files: 2 (TestInitializeShouldBeValidAnalyzer.cs, TestCleanupShouldBeValidAnalyzer.cs)
  • Detection Method: Semantic code analysis (file diff)
  • 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