Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code fix for MSTEST0019 #3812

Merged
merged 14 commits into from
Sep 12, 2024

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,7 @@
<data name="ReplaceWithConstructorFix" xml:space="preserve">
<value>Replace TestInitialize method with constructor</value>
</data>
<data name="ReplaceWithTestInitializeFix" xml:space="preserve">
<value>Replace constructor with TestInitialize method</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Immutable;
using System.Composition;

using Analyzer.Utilities;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Text;

using MSTest.Analyzers.Helpers;

namespace MSTest.Analyzers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ClassInitializeShouldBeValidFixer))]
[Shared]
public sealed class PreferTestInitializeOverConstructorFixer : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; }
= ImmutableArray.Create(DiagnosticIds.PreferTestInitializeOverConstructorRuleId);

public override FixAllProvider GetFixAllProvider()
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
=> WellKnownFixAllProviders.BatchFixer;

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

Diagnostic diagnostic = context.Diagnostics[0];
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;

SyntaxToken syntaxToken = root.FindToken(diagnosticSpan.Start);
if (syntaxToken.Parent is null)
{
return;
}

// Find the constructor declaration identified by the diagnostic.
ConstructorDeclarationSyntax constructorDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType<ConstructorDeclarationSyntax>().FirstOrDefault();
if (constructorDeclaration == null)
{
return;
}

context.RegisterCodeFix(
CodeAction.Create(
CodeFixResources.ReplaceWithTestInitializeFix,
c => ReplaceConstructorWithTestInitializeAsync(context.Document, constructorDeclaration, c),
nameof(PreferTestInitializeOverConstructorFixer)),
diagnostic);
}

private static async Task<Document> ReplaceConstructorWithTestInitializeAsync(Document document, ConstructorDeclarationSyntax constructorDeclaration, CancellationToken cancellationToken)
{
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);

// Find the class containing the constructor
if (constructorDeclaration.Parent is ClassDeclarationSyntax containingClass)
{
// Check if a TestInitialize method already exists
MethodDeclarationSyntax? existingTestInitialize = containingClass.Members
engyebrahim marked this conversation as resolved.
Show resolved Hide resolved
.OfType<MethodDeclarationSyntax>()
.FirstOrDefault(m => m.AttributeLists
.SelectMany(attrList => attrList.Attributes)
.Any(attr => attr.Name.ToString().Contains("TestInitialize")));

// Move the body of the constructor
BlockSyntax? constructorBody = constructorDeclaration.Body;

if (existingTestInitialize != null)
{
// If TestInitialize exists, append the constructor body to it
StatementSyntax[]? constructorStatements = constructorBody?.Statements.ToArray();
engyebrahim marked this conversation as resolved.
Show resolved Hide resolved
MethodDeclarationSyntax newTestInitialize;
if (existingTestInitialize.Body != null)
{
BlockSyntax newTestInitializeBody = existingTestInitialize.Body.AddStatements(constructorStatements ?? Array.Empty<StatementSyntax>());
newTestInitialize = existingTestInitialize.WithBody(newTestInitializeBody);
}
else
{
newTestInitialize = existingTestInitialize.WithBody(constructorBody);
}

editor.ReplaceNode(existingTestInitialize, newTestInitialize);
}
else
{
// Create a new TestInitialize method with the constructor body
MethodDeclarationSyntax testInitializeMethod = SyntaxFactory.MethodDeclaration(SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "TestInitialize")
.WithAttributeLists(
SyntaxFactory.SingletonList(
SyntaxFactory.AttributeList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("TestInitialize"))))))
.WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)))
.WithBody(constructorBody);

editor.AddMember(containingClass, testInitializeMethod);
}

// Remove the constructor
editor.RemoveNode(constructorDeclaration);
}

return editor.GetChangedDocument();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Nahradit metodu Dispose metodou TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Oprava podpisu testovací třídy</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">„Dispose“ durch eine TestCleanup-Methode ersetzen</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Testklassensignatur korrigieren</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Reemplazar 'Dispose' por un método TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Corregir firma de clase de prueba</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Replace 'Dispose' with a TestCleanup method</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Correction de la signature de classe de test</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Sostituire "Dispose" con un metodo TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Correggi la firma della classe di test</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">'Dispose' を TestCleanup メソッドに置き換える</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">テスト クラスのシグネチャの修正</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">'Dispose'를 TestCleanup 메서드로 바꾸기</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">테스트 클래스 서명 수정</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Zastąp metodę „Dispose” metodą TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Napraw podpis klasy testowej</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Substituir "Dispose" por um método TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Correção da assinatura de classe do teste</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">Заменить "Dispose" методом TestCleanup</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Исправить подпись класса теста</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">'Dispose' öğesini bir TestCleanup yöntemiyle değiştir</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">Test sınıfı imzasını düzeltme</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">将“Dispose”替换为 TestCleanup 方法</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">修复测试类签名</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="translated">以 TestCleanup 方法取代 'Dispose'</target>
<note />
</trans-unit>
<trans-unit id="ReplaceWithTestInitializeFix">
<source>Replace constructor with TestInitialize method</source>
<target state="new">Replace constructor with TestInitialize method</target>
<note />
</trans-unit>
<trans-unit id="TestClassShouldBeValidFix">
<source>Fix test class signature</source>
<target state="translated">修正測試類別簽章</target>
Expand Down
Loading