Skip to content

Commit ec6d4c6

Browse files
authored
Add codefix for MSTEST0020 (#3798)
1 parent 77c969e commit ec6d4c6

17 files changed

+349
-6
lines changed

src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs

+10-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx

+3
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,7 @@
153153
<data name="ReplaceWithTestCleanuFix" xml:space="preserve">
154154
<value>Replace 'Dispose' with a TestCleanup method</value>
155155
</data>
156+
<data name="ReplaceWithConstructorFix" xml:space="preserve">
157+
<value>Replace TestInitialize method with constructor</value>
158+
</data>
156159
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Collections.Immutable;
5+
using System.Composition;
6+
7+
using Analyzer.Utilities;
8+
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CodeActions;
11+
using Microsoft.CodeAnalysis.CodeFixes;
12+
using Microsoft.CodeAnalysis.CSharp;
13+
using Microsoft.CodeAnalysis.CSharp.Syntax;
14+
using Microsoft.CodeAnalysis.Editing;
15+
using Microsoft.CodeAnalysis.Text;
16+
17+
using MSTest.Analyzers.Helpers;
18+
19+
namespace MSTest.Analyzers;
20+
21+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ClassInitializeShouldBeValidFixer))]
22+
[Shared]
23+
public sealed class PreferConstructorOverTestInitializeFixer : CodeFixProvider
24+
{
25+
public override ImmutableArray<string> FixableDiagnosticIds { get; }
26+
= ImmutableArray.Create(DiagnosticIds.PreferConstructorOverTestInitializeRuleId);
27+
28+
public override FixAllProvider GetFixAllProvider()
29+
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
30+
=> WellKnownFixAllProviders.BatchFixer;
31+
32+
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
33+
{
34+
SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
35+
36+
Diagnostic diagnostic = context.Diagnostics[0];
37+
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
38+
39+
SyntaxToken syntaxToken = root.FindToken(diagnosticSpan.Start);
40+
if (syntaxToken.Parent is null)
41+
{
42+
return;
43+
}
44+
45+
// Find the method declaration identified by the diagnostic.
46+
MethodDeclarationSyntax methodDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>().FirstOrDefault();
47+
if (methodDeclaration == null)
48+
{
49+
return;
50+
}
51+
52+
context.RegisterCodeFix(
53+
CodeAction.Create(
54+
CodeFixResources.ReplaceWithConstructorFix,
55+
c => ReplaceTestInitializeWithConstructorAsync(context.Document, methodDeclaration, c),
56+
nameof(PreferConstructorOverTestInitializeFixer)),
57+
diagnostic);
58+
}
59+
60+
private static async Task<Document> ReplaceTestInitializeWithConstructorAsync(Document document, MethodDeclarationSyntax testInitializeMethod, CancellationToken cancellationToken)
61+
{
62+
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
63+
64+
// Find the class containing the method
65+
if (testInitializeMethod.Parent is ClassDeclarationSyntax containingClass)
66+
{
67+
ConstructorDeclarationSyntax existingConstructor = containingClass.Members
68+
.OfType<ConstructorDeclarationSyntax>()
69+
.FirstOrDefault();
70+
71+
// Move the body of the TestInitialize method
72+
BlockSyntax? testInitializeBody = testInitializeMethod.Body;
73+
74+
if (existingConstructor != null)
75+
{
76+
StatementSyntax[]? testInitializeStatements = testInitializeBody?.Statements.ToArray();
77+
ConstructorDeclarationSyntax newConstructor;
78+
79+
// If a constructor already exists, append the body of the TestInitialize method to it
80+
if (existingConstructor.Body != null)
81+
{
82+
BlockSyntax newConstructorBody = existingConstructor.Body.AddStatements(testInitializeStatements ?? Array.Empty<StatementSyntax>());
83+
newConstructor = existingConstructor.WithBody(newConstructorBody);
84+
}
85+
else
86+
{
87+
newConstructor = existingConstructor.WithBody(testInitializeBody);
88+
}
89+
90+
editor.ReplaceNode(existingConstructor, newConstructor);
91+
}
92+
else
93+
{
94+
// Create a new constructor with the TestInitialize body if one doesn't exist
95+
ConstructorDeclarationSyntax constructor = SyntaxFactory.ConstructorDeclaration(containingClass.Identifier)
96+
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
97+
.WithBody(testInitializeBody);
98+
99+
editor.AddMember(containingClass, constructor);
100+
}
101+
102+
// Remove the TestInitialize method
103+
editor.RemoveNode(testInitializeMethod);
104+
}
105+
106+
return editor.GetChangedDocument();
107+
}
108+
}

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Přidat [TestClass]</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">„[TestClass]“ hinzufügen</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Agregar '[TestClass]'</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Ajouter « [TestClass] »</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Aggiungi '[TestClass]'</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">'[TestClass]' の追加</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">'[TestClass]' 추가</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Dodaj element „[TestClass]”</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Adicionar '[TestClass]'</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">Добавить '[TestClass]'</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">'[TestClass]' Ekle</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">添加“[TestClass]”</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<target state="translated">新增 '[TestClass]'</target>
3333
<note />
3434
</trans-unit>
35+
<trans-unit id="ReplaceWithConstructorFix">
36+
<source>Replace TestInitialize method with constructor</source>
37+
<target state="new">Replace TestInitialize method with constructor</target>
38+
<note />
39+
</trans-unit>
3540
<trans-unit id="ReplaceWithFailAssertionFix">
3641
<source>Replace the assertion with 'Assert.Fail()'</source>
3742
<target state="new">Replace the assertion with 'Assert.Fail()'</target>

0 commit comments

Comments
 (0)