Skip to content

Commit 6d108a8

Browse files
author
Anh Thi Dao
committed
Implement code fixer for issue dotnet#35815
1 parent 941600d commit 6d108a8

File tree

5 files changed

+287
-115
lines changed

5 files changed

+287
-115
lines changed

AspNetCore.sln

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ EndProject
1818
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mvc", "Mvc", "{819B136D-B8B6-46AE-8C4F-5469BBBFC46B}"
1919
EndProject
2020
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc", "src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj", "{D4FBDF11-7A65-4205-8AF6-ABC190EFCF50}"
21+
ProjectSection(ProjectDependencies) = postProject
22+
{456056B5-3888-47AB-AFD7-4F16A8A1454F} = {456056B5-3888-47AB-AFD7-4F16A8A1454F}
23+
EndProjectSection
2124
EndProject
2225
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SignalR", "SignalR", "{5BC5A805-DCA0-41DF-91B8-520B5DAD57DA}"
2326
EndProject
@@ -287,12 +290,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IISIntegration", "IISIntegr
287290
EndProject
288291
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration", "src\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{737D2BC0-092E-404B-BB73-EACAEDBF0E65}"
289292
ProjectSection(ProjectDependencies) = postProject
290-
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B} = {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}
293+
{09D9D1D6-2951-4E14-BC35-76A23CF9391A} = {09D9D1D6-2951-4E14-BC35-76A23CF9391A}
294+
{1533E271-F61B-441B-8B74-59FB61DF0552} = {1533E271-F61B-441B-8B74-59FB61DF0552}
291295
{55494E58-E061-4C4C-A0A8-837008E72F85} = {55494E58-E061-4C4C-A0A8-837008E72F85}
292296
{7F87406C-A3C8-4139-A68D-E4C344294A67} = {7F87406C-A3C8-4139-A68D-E4C344294A67}
293-
{1533E271-F61B-441B-8B74-59FB61DF0552} = {1533E271-F61B-441B-8B74-59FB61DF0552}
294297
{D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863}
295-
{09D9D1D6-2951-4E14-BC35-76A23CF9391A} = {09D9D1D6-2951-4E14-BC35-76A23CF9391A}
298+
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B} = {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}
296299
EndProjectSection
297300
EndProject
298301
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{7004082D-53E9-45C2-B2DE-EB3CE448B64F}"
@@ -413,12 +416,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IIS", "IIS", "{33CAD745-591
413416
EndProject
414417
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IIS", "src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj", "{9E01AF6A-F748-4490-B45B-8558D1E701B4}"
415418
ProjectSection(ProjectDependencies) = postProject
416-
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B} = {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}
419+
{09D9D1D6-2951-4E14-BC35-76A23CF9391A} = {09D9D1D6-2951-4E14-BC35-76A23CF9391A}
420+
{1533E271-F61B-441B-8B74-59FB61DF0552} = {1533E271-F61B-441B-8B74-59FB61DF0552}
417421
{55494E58-E061-4C4C-A0A8-837008E72F85} = {55494E58-E061-4C4C-A0A8-837008E72F85}
418422
{7F87406C-A3C8-4139-A68D-E4C344294A67} = {7F87406C-A3C8-4139-A68D-E4C344294A67}
419-
{1533E271-F61B-441B-8B74-59FB61DF0552} = {1533E271-F61B-441B-8B74-59FB61DF0552}
420423
{D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863}
421-
{09D9D1D6-2951-4E14-BC35-76A23CF9391A} = {09D9D1D6-2951-4E14-BC35-76A23CF9391A}
424+
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B} = {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}
422425
EndProjectSection
423426
EndProject
424427
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Transport.Sockets", "Transport.Sockets", "{94174359-D57E-467E-8AC7-167A10260F34}"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CodeActions;
9+
using Microsoft.CodeAnalysis.CodeFixes;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
using Microsoft.CodeAnalysis.CSharp;
12+
using Microsoft.CodeAnalysis.Editing;
13+
14+
namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder.Fixers;
15+
public class WebApplicationBuilderFixer : CodeFixProvider
16+
{
17+
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(new[]
18+
{
19+
// Add other diagnostic descriptor id's
20+
DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder.Id
21+
});
22+
23+
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
24+
25+
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
26+
{
27+
foreach (var diagnostic in context.Diagnostics)
28+
{
29+
context.RegisterCodeFix(
30+
CodeAction.Create("Fix Configure methods",
31+
cancellationToken => FixConfigureMethods(diagnostic, context.Document, cancellationToken),
32+
equivalenceKey: DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder.Id),
33+
diagnostic);
34+
}
35+
36+
return Task.CompletedTask;
37+
}
38+
39+
private static async Task<Document> FixConfigureMethods(Diagnostic diagnostic, Document document, CancellationToken cancellationToken)
40+
{
41+
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken);
42+
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
43+
44+
if (root == null)
45+
{
46+
return document;
47+
}
48+
49+
var invocation = root.FindNode(diagnostic.Location.SourceSpan);
50+
51+
if (invocation is InvocationExpressionSyntax { } invocationExpression)
52+
{
53+
var expression = invocationExpression.Expression as MemberAccessExpressionSyntax;
54+
var configuration = SyntaxFactory.IdentifierName("Configuration");
55+
var builder = expression.Expression as MemberAccessExpressionSyntax;
56+
57+
builder = builder.WithName(configuration);
58+
expression = expression.WithExpression(builder);
59+
60+
var initArgument = invocationExpression.ArgumentList.Arguments[0];
61+
var lambdaExpression = initArgument.Expression as SimpleLambdaExpressionSyntax;
62+
var body = lambdaExpression.ExpressionBody as InvocationExpressionSyntax;
63+
var argument = body.ArgumentList;
64+
var bodyExpression = body.Expression as MemberAccessExpressionSyntax;
65+
var method = bodyExpression.Name;
66+
67+
expression = expression.WithName(method);
68+
invocationExpression = invocationExpression.WithExpression(expression);
69+
invocationExpression = invocationExpression.WithArgumentList(argument);
70+
71+
editor.ReplaceNode(invocation, invocationExpression);
72+
}
73+
74+
return editor.GetChangedDocument();
75+
}
76+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp.Testing;
6+
using Microsoft.CodeAnalysis.Testing;
7+
using Microsoft.CodeAnalysis.Testing.Verifiers;
8+
9+
namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
10+
11+
public static class CSharpWebApplicationBuilderAnalyzerVerifier<TAnalyzer>
12+
where TAnalyzer : WebApplicationBuilderAnalyzer, new()
13+
{
14+
public static DiagnosticResult Diagnostic(string diagnosticId = null)
15+
=> CSharpWebApplicationBuilderAnalyzerVerifier<WebApplicationBuilderAnalyzer>.Diagnostic(diagnosticId);
16+
17+
public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
18+
=> new DiagnosticResult(descriptor);
19+
20+
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
21+
{
22+
var test = new Test { TestCode = source };
23+
test.ExpectedDiagnostics.AddRange(expected);
24+
return test.RunAsync();
25+
}
26+
27+
public class Test : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier> { }
28+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
6+
using Microsoft.AspNetCore.Analyzers.WebApplicationBuilder.Fixers;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp.Testing;
9+
using Microsoft.CodeAnalysis.Testing;
10+
using Microsoft.CodeAnalysis.Testing.Verifiers;
11+
12+
namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
13+
14+
public static class CSharpWebApplicationBuilderCodeFixVerifier<TAnalyzer, TCodeFix>
15+
where TAnalyzer : WebApplicationBuilderAnalyzer, new()
16+
where TCodeFix : WebApplicationBuilderFixer, new()
17+
{
18+
public static DiagnosticResult Diagnostic(string diagnosticId = null)
19+
=> CSharpCodeFixVerifier<TAnalyzer, TCodeFix, XUnitVerifier>.Diagnostic(diagnosticId);
20+
21+
public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
22+
=> new DiagnosticResult(descriptor);
23+
24+
public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
25+
{
26+
var test = new CSharpWebApplicationBuilderAnalyzerVerifier<TAnalyzer>.Test { TestCode = source };
27+
test.ExpectedDiagnostics.AddRange(expected);
28+
return test.RunAsync();
29+
}
30+
31+
public static Task VerifyCodeFixAsync(string source, string fixedSource)
32+
=> VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
33+
34+
public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
35+
=> VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
36+
37+
public static Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)
38+
=> VerifyCodeFixAsync(source, expected, fixedSource, string.Empty);
39+
40+
public static Task VerifyCodeFixAsync(string sources, DiagnosticResult[] expected, string fixedSources, string usageSource = "")
41+
{
42+
var test = new WebApplicationBuilderAnalyzerTest
43+
{
44+
TestState =
45+
{
46+
Sources = { sources, usageSource },
47+
// We need to set the output type to an exe to properly
48+
// support top-level programs in the tests. Otherwise,
49+
// the test infra will assume we are trying to build a library.
50+
OutputKind = OutputKind.ConsoleApplication
51+
},
52+
FixedState =
53+
{
54+
Sources = { fixedSources, usageSource }
55+
}
56+
};
57+
58+
test.TestState.ExpectedDiagnostics.AddRange(expected);
59+
return test.RunAsync();
60+
}
61+
62+
public class WebApplicationBuilderAnalyzerTest : CSharpCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier>
63+
{
64+
public WebApplicationBuilderAnalyzerTest()
65+
{
66+
// We populate the ReferenceAssemblies used in the tests with the locally-built AspNetCore
67+
// assemblies that are referenced in a minimal app to ensure that there are no reference
68+
// errors during the build. The value used here should be updated on each TFM change.
69+
ReferenceAssemblies = ReferenceAssemblies.Net.Net70.AddAssemblies(ImmutableArray.Create(
70+
TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Hosting.WebHostBuilderExtensions).Assembly.Location),
71+
TrimAssemblyExtension(typeof(Microsoft.Extensions.Hosting.IHostBuilder).Assembly.Location),
72+
TrimAssemblyExtension(typeof(Microsoft.Extensions.Configuration.IConfigurationBuilder).Assembly.Location),
73+
TrimAssemblyExtension(typeof(Microsoft.Extensions.Configuration.JsonConfigurationExtensions).Assembly.Location),
74+
TrimAssemblyExtension(typeof(Microsoft.Extensions.Configuration.ConfigurationManager).Assembly.Location),
75+
TrimAssemblyExtension(typeof(Microsoft.Extensions.Hosting.HostingHostBuilderExtensions).Assembly.Location),
76+
TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.ConfigureHostBuilder).Assembly.Location),
77+
TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Hosting.HostingAbstractionsWebHostBuilderExtensions).Assembly.Location)));
78+
79+
string TrimAssemblyExtension(string fullPath) => fullPath.Replace(".dll", string.Empty);
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)