Skip to content

Commit 621326b

Browse files
authored
Merge pull request #42351 from dotnet/WebApplicationBuilderAnalyzers
Implementing analyzer to prefer WebApplicationBuilder.Configuration over Configure methods
2 parents 2750c62 + 47e9ecb commit 621326b

File tree

4 files changed

+278
-3
lines changed

4 files changed

+278
-3
lines changed

Diff for: src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs

+9
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,13 @@ internal static class DiagnosticDescriptors
8888
DiagnosticSeverity.Warning,
8989
isEnabledByDefault: true,
9090
helpLinkUri: "https://aka.ms/aspnet/analyzers");
91+
92+
internal static readonly DiagnosticDescriptor DisallowConfigureAppConfigureHostBuilder = new(
93+
"ASP0013",
94+
"Suggest using WebApplicationBuilder.Configuration over Configure methods",
95+
"Suggest using WebApplicationBuilder.Configuration instead of {0}",
96+
"Usage",
97+
DiagnosticSeverity.Warning,
98+
isEnabledByDefault: true,
99+
helpLinkUri: "https://aka.ms/aspnet/analyzers");
91100
}

Diff for: src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs

+59-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ public class WebApplicationBuilderAnalyzer : DiagnosticAnalyzer
2121
DiagnosticDescriptors.DoNotUseConfigureWebHostWithConfigureHostBuilder,
2222
DiagnosticDescriptors.DoNotUseConfigureWithConfigureWebHostBuilder,
2323
DiagnosticDescriptors.DoNotUseUseStartupWithConfigureWebHostBuilder,
24-
DiagnosticDescriptors.DoNotUseHostConfigureLogging
24+
DiagnosticDescriptors.DoNotUseHostConfigureLogging,
25+
DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder
2526
});
2627

2728
public override void Initialize(AnalysisContext context)
@@ -50,6 +51,14 @@ public override void Initialize(AnalysisContext context)
5051
wellKnownTypes.HostingHostBuilderExtensions,
5152
wellKnownTypes.WebHostBuilderExtensions
5253
};
54+
INamedTypeSymbol[] configureAppTypes =
55+
{
56+
wellKnownTypes.ConfigureHostBuilder,
57+
wellKnownTypes.ConfigureWebHostBuilder,
58+
wellKnownTypes.WebHostBuilderExtensions,
59+
wellKnownTypes.HostingHostBuilderExtensions,
60+
};
61+
INamedTypeSymbol[] configureHostTypes = { wellKnownTypes.ConfigureHostBuilder };
5362

5463
compilationStartAnalysisContext.RegisterOperationAction(operationAnalysisContext =>
5564
{
@@ -103,7 +112,7 @@ public override void Initialize(AnalysisContext context)
103112
DiagnosticDescriptors.DoNotUseUseStartupWithConfigureWebHostBuilder,
104113
invocation));
105114
}
106-
115+
107116
//var builder = WebApplication.CreateBuilder(args);
108117
//builder.Host.ConfigureLogging(x => {})
109118
if (IsDisallowedMethod(
@@ -135,6 +144,54 @@ public override void Initialize(AnalysisContext context)
135144
DiagnosticDescriptors.DoNotUseHostConfigureLogging,
136145
invocation));
137146
}
147+
148+
// var builder = WebApplication.CreateBuilder();
149+
// builder.WebHost.ConfigureAppConfiguration(builder => {});
150+
if (IsDisallowedMethod(
151+
operationAnalysisContext,
152+
invocation,
153+
targetMethod,
154+
wellKnownTypes.ConfigureWebHostBuilder,
155+
"ConfigureAppConfiguration",
156+
configureAppTypes))
157+
{
158+
operationAnalysisContext.ReportDiagnostic(
159+
CreateDiagnostic(
160+
DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder,
161+
invocation));
162+
}
163+
164+
// var builder = WebApplication.CreateBuilder();
165+
// builder.Host.ConfigureAppConfiguration(builder => {});
166+
if (IsDisallowedMethod(
167+
operationAnalysisContext,
168+
invocation,
169+
targetMethod,
170+
wellKnownTypes.ConfigureHostBuilder,
171+
"ConfigureAppConfiguration",
172+
configureAppTypes))
173+
{
174+
operationAnalysisContext.ReportDiagnostic(
175+
CreateDiagnostic(
176+
DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder,
177+
invocation));
178+
}
179+
180+
// var builder = WebApplication.CreateBuilder();
181+
// builder.Host.ConfigureHostConfiguration(builder => {});
182+
if (IsDisallowedMethod(
183+
operationAnalysisContext,
184+
invocation,
185+
targetMethod,
186+
wellKnownTypes.ConfigureHostBuilder,
187+
"ConfigureHostConfiguration",
188+
configureHostTypes))
189+
{
190+
operationAnalysisContext.ReportDiagnostic(
191+
CreateDiagnostic(
192+
DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder,
193+
invocation));
194+
}
138195

139196
static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, IInvocationOperation operation)
140197
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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.Globalization;
5+
using Microsoft.AspNetCore.Analyzer.Testing;
6+
7+
namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
8+
public partial class DisallowConfigureAppConfigureHostBuilderTest
9+
{
10+
private TestDiagnosticAnalyzerRunner Runner { get; } = new(new WebApplicationBuilderAnalyzer());
11+
12+
[Fact]
13+
public async Task ConfigurationBuilderRunsWithoutDiagnostic()
14+
{
15+
// Arrange
16+
var source = @"
17+
using Microsoft.AspNetCore.Builder;
18+
using Microsoft.Extensions.Configuration;
19+
var builder = WebApplication.CreateBuilder(args);
20+
builder.Configuration.AddJsonFile(""foo.json"", optional: true);
21+
";
22+
// Act
23+
var diagnostic = await Runner.GetDiagnosticsAsync(source);
24+
25+
// Assert
26+
Assert.Empty(diagnostic);
27+
}
28+
29+
[Fact]
30+
public async Task ConfigureAppHostBuilderProducesDiagnostic()
31+
{
32+
// Arrange
33+
var source = TestSource.Read(@"
34+
using Microsoft.AspNetCore.Builder;
35+
using Microsoft.Extensions.Hosting;
36+
using Microsoft.Extensions.Configuration;
37+
var builder = WebApplication.CreateBuilder(args);
38+
builder.Host./*MM*/ConfigureAppConfiguration(builder =>
39+
{
40+
builder.AddJsonFile(""foo.json"", optional: true);
41+
});
42+
");
43+
44+
// Act
45+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
46+
47+
// Assert
48+
var diagnostic = Assert.Single(diagnostics);
49+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
50+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
51+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
52+
}
53+
54+
[Fact]
55+
public async Task ConfigureHostHostBuilderProducesDiagnostic()
56+
{
57+
// Arrange
58+
var source = TestSource.Read(@"
59+
using Microsoft.AspNetCore.Builder;
60+
using Microsoft.Extensions.Configuration;
61+
var builder = WebApplication.CreateBuilder(args);
62+
builder.Host./*MM*/ConfigureHostConfiguration(builder =>
63+
{
64+
builder.AddJsonFile(""foo.json"", optional: true);
65+
});
66+
");
67+
68+
// Act
69+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
70+
71+
// Assert
72+
var diagnostic = Assert.Single(diagnostics);
73+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
74+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
75+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureHostConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
76+
}
77+
78+
[Fact]
79+
public async Task ConfigureAppWebHostBuilderProducesDiagnostic()
80+
{
81+
// Arrange
82+
var source = TestSource.Read(@"
83+
using Microsoft.AspNetCore.Builder;
84+
using Microsoft.AspNetCore.Hosting;
85+
using Microsoft.Extensions.Configuration;
86+
var builder = WebApplication.CreateBuilder(args);
87+
builder.WebHost./*MM*/ConfigureAppConfiguration(builder =>
88+
{
89+
builder.AddJsonFile(""foo.json"", optional: true);
90+
});
91+
");
92+
93+
// Act
94+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
95+
96+
// Assert
97+
var diagnostic = Assert.Single(diagnostics);
98+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
99+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
100+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
101+
}
102+
103+
[Fact]
104+
public async Task ConfigureAppWebHostBuilderWithContextProducesDiagnostic()
105+
{
106+
// Arrange
107+
var source = TestSource.Read(@"
108+
using Microsoft.AspNetCore.Builder;
109+
using Microsoft.AspNetCore.Hosting;
110+
using Microsoft.Extensions.Configuration;
111+
var builder = WebApplication.CreateBuilder(args);
112+
builder.WebHost./*MM*/ConfigureAppConfiguration((context, webHostBuilder) => { });
113+
");
114+
115+
// Act
116+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
117+
118+
// Assert
119+
var diagnostic = Assert.Single(diagnostics);
120+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
121+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
122+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
123+
}
124+
[Fact]
125+
public async Task ConfigureAppWebHostBuilderProducesDiagnosticInMain()
126+
{
127+
// Arrange
128+
var source = TestSource.Read(@"
129+
using Microsoft.AspNetCore.Builder;
130+
using Microsoft.AspNetCore.Hosting;
131+
using Microsoft.Extensions.Configuration;
132+
public static class Test
133+
{
134+
public static void Main(string[]args) {
135+
var builder = WebApplication.CreateBuilder(args);
136+
builder.WebHost./*MM*/ConfigureAppConfiguration(builder => { });
137+
}
138+
}
139+
");
140+
141+
// Act
142+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
143+
144+
// Assert
145+
var diagnostic = Assert.Single(diagnostics);
146+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
147+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
148+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
149+
}
150+
[Fact]
151+
public async Task ConfigureAppWebHostOnBuilderProducesDiagnosticInMain()
152+
{
153+
// Arrange
154+
var source = TestSource.Read(@"
155+
using Microsoft.AspNetCore.Builder;
156+
using Microsoft.AspNetCore.Hosting;
157+
using Microsoft.Extensions.Configuration;
158+
public static class Test
159+
{
160+
public static void Main(string[]args) {
161+
var builder = WebApplication.CreateBuilder(args);
162+
var webhost = builder.WebHost;
163+
webhost./*MM*/ConfigureAppConfiguration(builder => { });
164+
}
165+
}
166+
");
167+
168+
// Act
169+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
170+
171+
// Assert
172+
var diagnostic = Assert.Single(diagnostics);
173+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic.Descriptor);
174+
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
175+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic.GetMessage(CultureInfo.InvariantCulture));
176+
}
177+
[Fact]
178+
public async Task TwoInvocationsProduceTwoDiagnostic()
179+
{
180+
// Arrange
181+
var source = TestSource.Read(@"
182+
using Microsoft.AspNetCore.Builder;
183+
using Microsoft.AspNetCore.Hosting;
184+
using Microsoft.Extensions.Configuration;
185+
var builder = WebApplication.CreateBuilder(args);
186+
builder.Host./*MM1*/ConfigureHostConfiguration(builder =>
187+
{
188+
builder.AddJsonFile(""foo.json"", optional: true);
189+
});
190+
builder.WebHost./*MM2*/ConfigureAppConfiguration(builder =>
191+
{
192+
builder.AddJsonFile(""foo.json"", optional: true);
193+
});
194+
");
195+
196+
// Act
197+
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
198+
199+
// Assert
200+
Assert.Equal(2, diagnostics.Length);
201+
var diagnostic1 = diagnostics[0];
202+
var diagnostic2 = diagnostics[1];
203+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic1.Descriptor);
204+
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic1.Location);
205+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureHostConfiguration", diagnostic1.GetMessage(CultureInfo.InvariantCulture));
206+
Assert.Same(DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder, diagnostic2.Descriptor);
207+
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM2"], diagnostic2.Location);
208+
Assert.Equal("Suggest using WebApplicationBuilder.Configuration instead of ConfigureAppConfiguration", diagnostic2.GetMessage(CultureInfo.InvariantCulture));
209+
}
210+
}

Diff for: src/Http/samples/MinimalSample/Program.cs

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
Username: {Environment.UserName}
1919
Date and Time: {DateTime.Now}
2020
""");
21-
2221
var outer = app.MapGroup("/outer");
2322
var inner = outer.MapGroup("/inner");
2423

0 commit comments

Comments
 (0)