Skip to content

Commit fafb89e

Browse files
committed
WIP
1 parent a400348 commit fafb89e

File tree

5 files changed

+958
-0
lines changed

5 files changed

+958
-0
lines changed

src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,13 @@ internal static class DiagnosticDescriptors
248248
DiagnosticSeverity.Info,
249249
isEnabledByDefault: true,
250250
helpLinkUri: AnalyzersLink);
251+
252+
internal static readonly DiagnosticDescriptor UseCreateHostBuilderInsteadOfCreateWebHostBuilder = new(
253+
"ASP0029",
254+
CreateLocalizableResourceString(nameof(Resources.Analyzer_UseCreateHostBuilderInsteadOfCreateWebHostBuilder_Title)),
255+
CreateLocalizableResourceString(nameof(Resources.Analyzer_UseCreateHostBuilderInsteadOfCreateWebHostBuilder_Message)),
256+
Usage,
257+
DiagnosticSeverity.Warning,
258+
isEnabledByDefault: true,
259+
helpLinkUri: AnalyzersLink);
251260
}

src/Framework/AspNetCoreAnalyzers/src/Analyzers/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,10 @@
333333
<data name="Analyzer_KestrelShouldListenOnIPv6AnyInsteadOfIpAny_Message" xml:space="preserve">
334334
<value>If the server does not specifically reject IPv6, IPAddress.IPv6Any is preferred over IPAddress.Any usage for safety and performance reasons. See https://aka.ms/aspnetcore-warnings/ASP0028 for more details.</value>
335335
</data>
336+
<data name="Analyzer_UseCreateHostBuilderInsteadOfCreateWebHostBuilder_Title" xml:space="preserve">
337+
<value>Use Host.CreateDefaultBuilder instead of WebHost.CreateDefaultBuilder</value>
338+
</data>
339+
<data name="Analyzer_UseCreateHostBuilderInsteadOfCreateWebHostBuilder_Message" xml:space="preserve">
340+
<value>WebHost is deprecated. Use Host.CreateDefaultBuilder and ConfigureWebHostDefaults instead.</value>
341+
</data>
336342
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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.Linq;
6+
using Microsoft.AspNetCore.App.Analyzers.Infrastructure;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
using Microsoft.CodeAnalysis.Operations;
12+
13+
namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
14+
15+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
16+
public sealed class UseCreateHostBuilderInsteadOfCreateWebHostBuilderAnalyzer : DiagnosticAnalyzer
17+
{
18+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
19+
DiagnosticDescriptors.UseCreateHostBuilderInsteadOfCreateWebHostBuilder
20+
);
21+
22+
public override void Initialize(AnalysisContext context)
23+
{
24+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
25+
context.EnableConcurrentExecution();
26+
context.RegisterCompilationStartAction(context =>
27+
{
28+
var compilation = context.Compilation;
29+
var wellKnownTypes = WellKnownTypes.GetOrCreate(compilation);
30+
31+
context.RegisterOperationAction(context =>
32+
{
33+
var invocation = (IInvocationOperation)context.Operation;
34+
var targetMethod = invocation.TargetMethod;
35+
36+
// Check if this is WebHost.CreateDefaultBuilder
37+
if (IsWebHostCreateDefaultBuilderCall(targetMethod))
38+
{
39+
var diagnostic = Diagnostic.Create(
40+
DiagnosticDescriptors.UseCreateHostBuilderInsteadOfCreateWebHostBuilder,
41+
invocation.Syntax.GetLocation()
42+
);
43+
context.ReportDiagnostic(diagnostic);
44+
}
45+
}, OperationKind.Invocation);
46+
47+
context.RegisterSyntaxNodeAction(context =>
48+
{
49+
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
50+
var semantic = context.SemanticModel;
51+
var symbol = semantic.GetDeclaredSymbol(methodDeclaration);
52+
53+
// Check if this method returns IWebHostBuilder
54+
if (symbol != null && IsWebHostBuilderReturnType(symbol))
55+
{
56+
// Check if the method body contains WebHost.CreateDefaultBuilder
57+
if (ContainsWebHostCreateDefaultBuilder(methodDeclaration))
58+
{
59+
var diagnostic = Diagnostic.Create(
60+
DiagnosticDescriptors.UseCreateHostBuilderInsteadOfCreateWebHostBuilder,
61+
methodDeclaration.ReturnType.GetLocation()
62+
);
63+
context.ReportDiagnostic(diagnostic);
64+
}
65+
}
66+
}, SyntaxKind.MethodDeclaration);
67+
});
68+
}
69+
70+
private static bool IsWebHostCreateDefaultBuilderCall(IMethodSymbol method)
71+
{
72+
// Check if this is WebHost.CreateDefaultBuilder (not other WebHost methods)
73+
if (method.Name == "CreateDefaultBuilder" &&
74+
method.ContainingType?.Name == "WebHost" &&
75+
method.ContainingType?.ContainingNamespace?.ToDisplayString() == "Microsoft.AspNetCore")
76+
{
77+
return true;
78+
}
79+
80+
return false;
81+
}
82+
83+
private static bool IsWebHostBuilderReturnType(IMethodSymbol method)
84+
{
85+
// Check if the return type is IWebHostBuilder
86+
var returnType = method.ReturnType;
87+
return returnType.Name == "IWebHostBuilder" &&
88+
returnType.ContainingNamespace?.ToDisplayString() == "Microsoft.AspNetCore.Hosting";
89+
}
90+
91+
private static bool ContainsWebHostCreateDefaultBuilder(MethodDeclarationSyntax methodDeclaration)
92+
{
93+
// Check if the method contains WebHost.CreateDefaultBuilder calls
94+
var descendants = methodDeclaration.DescendantNodes().OfType<InvocationExpressionSyntax>();
95+
96+
foreach (var invocation in descendants)
97+
{
98+
if (invocation.Expression is MemberAccessExpressionSyntax memberAccess &&
99+
memberAccess.Expression is IdentifierNameSyntax identifier &&
100+
identifier.Identifier.ValueText == "WebHost" &&
101+
memberAccess.Name.Identifier.ValueText == "CreateDefaultBuilder")
102+
{
103+
return true;
104+
}
105+
}
106+
107+
return false;
108+
}
109+
}

0 commit comments

Comments
 (0)