🔍 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
-
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
-
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
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
🔍 Duplicate Code Detected: Inconsistent and Duplicated Patterns in Assembly-Level Fixture Analyzers
Analysis of commit 741048f
Assignee:
@copilotSummary
AssemblyInitializeShouldBeValidAnalyzerandAssemblyCleanupShouldBeValidAnalyzer(plusClassInitializeShouldBeValidAnalyzer) each contain copy-pasted initialization boilerplate that manually callsTryGetOrCreateTypeByMetadataNameinstead of using the existingFixtureMethodAnalyzerHelper.RegisterFixtureMethodSymbolAction/TryGetFixtureMethodSymbolshelpers 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 byTestInitializeShouldBeValidAnalyzer/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 analyzersSeverity: 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) — callsTryGetOrCreateTypeByMetadataName3 times inlinesrc/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs(lines 40–49) — callsTryGetFixtureMethodSymbolsbut then re-fetchestestContextSymbolseparatelysrc/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs(lines 40–56) — callsTryGetOrCreateTypeByMetadataName3 times inlinesrc/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs(lines 40–49) — callsTryGetFixtureMethodSymbolsbut then re-fetchestestContextSymbol+inheritanceBehaviorSymbolseparatelyLocations (new pattern — for reference):
src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs(line 41) — single call toRegisterFixtureMethodSymbolActionsrc/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs(line 41) — single call toRegisterFixtureMethodSymbolActionCode Sample — old pattern (AssemblyInitializeShouldBeValidAnalyzer):
Pattern 2:
testContextSymbolre-fetched separately afterTryGetFixtureMethodSymbolsin Cleanup analyzersAssemblyCleanupShouldBeValidAnalyzerandClassCleanupShouldBeValidAnalyzerboth callTryGetFixtureMethodSymbols(which already resolves most symbols) but then immediately callGetOrCreateTypeByMetadataNamea second time to gettestContextSymbol, which is not included inFixtureMethodSymbols. This is partially addressed by the helper but not fully.Impact Analysis
FixtureMethodSymbolsorRegisterFixtureMethodSymbolActionis changed (e.g., to add a new symbol), analyzers using the old pattern will not benefit automatically.Refactoring Recommendations
Migrate old-pattern analyzers to
RegisterFixtureMethodSymbolActionAssemblyInitializeShouldBeValidAnalyzerandClassInitializeShouldBeValidAnalyzercan callRegisterFixtureMethodSymbolAction; theTestContextsymbol they need can be resolved inside the lambda.Extend
FixtureMethodSymbolsto optionally carryTestContextSymbolFixtureMethodSymbolsrecord inFixtureMethodAnalyzerHelper.cscould be extended with an optionalTestContextSymbolfield to eliminate the secondGetOrCreateTypeByMetadataNamecall in the Cleanup analyzers.TestContextis needed.Implementation Checklist
AssemblyInitializeShouldBeValidAnalyzerto useRegisterFixtureMethodSymbolActionClassInitializeShouldBeValidAnalyzerto useRegisterFixtureMethodSymbolActionFixtureMethodSymbolsto includeTestContextSymboland update the Cleanup analyzersAnalysis Metadata
Add this agentic workflows to your repo
To install this agentic workflow, run