Skip to content

Commit 69f4b6d

Browse files
authored
Add StartupAnalzyer (#9100)
1 parent 4ac9001 commit 69f4b6d

28 files changed

+1081
-0
lines changed

src/Mvc/Mvc.Analyzers/src/DiagnosticDescriptors.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,8 @@ public static class DiagnosticDescriptors
5252
DiagnosticSeverity.Warning,
5353
isEnabledByDefault: true,
5454
helpLinkUri: "https://aka.ms/AA20pbc");
55+
56+
57+
// MVC1005 reserved for startup
5558
}
5659
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
6+
namespace Microsoft.AspNetCore.Analyzers
7+
{
8+
internal abstract class ConfigureMethodAnalysis : StartupComputedAnalysis
9+
{
10+
protected ConfigureMethodAnalysis(IMethodSymbol configureMethod)
11+
: base(configureMethod.ContainingType)
12+
{
13+
ConfigureMethod = configureMethod;
14+
}
15+
16+
public IMethodSymbol ConfigureMethod { get; }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
6+
namespace Microsoft.AspNetCore.Analyzers
7+
{
8+
internal abstract class ConfigureServicesMethodAnalysis : StartupComputedAnalysis
9+
{
10+
protected ConfigureServicesMethodAnalysis(IMethodSymbol configureServicesMethod)
11+
: base(configureServicesMethod.ContainingType)
12+
{
13+
ConfigureServicesMethod = configureServicesMethod;
14+
}
15+
16+
public IMethodSymbol ConfigureServicesMethod { get; }
17+
}
18+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Immutable;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.Operations;
8+
9+
namespace Microsoft.AspNetCore.Analyzers
10+
{
11+
internal class MiddlewareAnalysis : ConfigureMethodAnalysis
12+
{
13+
public static MiddlewareAnalysis CreateAndInitialize(StartupAnalysisContext context)
14+
{
15+
if (context == null)
16+
{
17+
throw new ArgumentNullException(nameof(context));
18+
}
19+
20+
var symbols = context.StartupSymbols;
21+
var analysis = new MiddlewareAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
22+
23+
var middleware = ImmutableArray.CreateBuilder<MiddlewareItem>();
24+
25+
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
26+
{
27+
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
28+
// rather than invocation.Instance.
29+
if (context.Operation is IInvocationOperation invocation &&
30+
invocation.Instance == null &&
31+
invocation.Arguments.Length >= 1 &&
32+
invocation.Arguments[0].Parameter?.Type == symbols.IApplicationBuilder)
33+
{
34+
middleware.Add(new MiddlewareItem(invocation));
35+
}
36+
}, OperationKind.Invocation);
37+
38+
context.OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction(context =>
39+
{
40+
analysis.Middleware = middleware.ToImmutable();
41+
});
42+
43+
return analysis;
44+
}
45+
46+
public MiddlewareAnalysis(IMethodSymbol configureMethod)
47+
: base(configureMethod)
48+
{
49+
}
50+
51+
public ImmutableArray<MiddlewareItem> Middleware { get; private set; } = ImmutableArray<MiddlewareItem>.Empty;
52+
}
53+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.Operations;
6+
7+
namespace Microsoft.AspNetCore.Analyzers
8+
{
9+
internal class MiddlewareItem
10+
{
11+
public MiddlewareItem(IInvocationOperation operation)
12+
{
13+
Operation = operation;
14+
}
15+
16+
public IInvocationOperation Operation { get; }
17+
18+
public IMethodSymbol UseMethod => Operation.TargetMethod;
19+
}
20+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.AspNetCore.Mvc.Analyzers;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.Diagnostics;
8+
using Microsoft.CodeAnalysis.Operations;
9+
10+
namespace Microsoft.AspNetCore.Analyzers
11+
{
12+
internal class MvcOptionsAnalysis : ConfigureServicesMethodAnalysis
13+
{
14+
public static MvcOptionsAnalysis CreateAndInitialize(StartupAnalysisContext context)
15+
{
16+
if (context == null)
17+
{
18+
throw new ArgumentNullException(nameof(context));
19+
}
20+
21+
var analysis = new MvcOptionsAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
22+
23+
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
24+
{
25+
if (context.Operation is ISimpleAssignmentOperation operation &&
26+
operation.Value.ConstantValue.HasValue &&
27+
operation.Target is IPropertyReferenceOperation property &&
28+
property.Member?.Name == SymbolNames.EnableEndpointRoutingProperty)
29+
{
30+
analysis.EndpointRoutingEnabled = operation.Value.ConstantValue.Value as bool?;
31+
}
32+
33+
}, OperationKind.SimpleAssignment);
34+
35+
return analysis;
36+
}
37+
38+
public MvcOptionsAnalysis(IMethodSymbol configureServicesMethod)
39+
: base(configureServicesMethod)
40+
{
41+
}
42+
43+
public bool? EndpointRoutingEnabled { get; private set; }
44+
}
45+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Immutable;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.Operations;
8+
9+
namespace Microsoft.AspNetCore.Analyzers
10+
{
11+
internal class ServicesAnalysis : ConfigureServicesMethodAnalysis
12+
{
13+
public static ServicesAnalysis CreateAndInitialize(StartupAnalysisContext context)
14+
{
15+
if (context == null)
16+
{
17+
throw new ArgumentNullException(nameof(context));
18+
}
19+
20+
var symbols = context.StartupSymbols;
21+
var analysis = new ServicesAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
22+
23+
var services = ImmutableArray.CreateBuilder<ServicesItem>();
24+
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
25+
{
26+
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
27+
// rather than invocation.Instance.
28+
if (context.Operation is IInvocationOperation invocation &&
29+
invocation.Instance == null &&
30+
invocation.Arguments.Length >= 1 &&
31+
invocation.Arguments[0].Parameter?.Type == symbols.IServiceCollection)
32+
{
33+
services.Add(new ServicesItem(invocation));
34+
}
35+
}, OperationKind.Invocation);
36+
37+
context.OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction(context =>
38+
{
39+
analysis.Services = services.ToImmutable();
40+
});
41+
42+
return analysis;
43+
}
44+
45+
public ServicesAnalysis(IMethodSymbol configureServicesMethod)
46+
: base(configureServicesMethod)
47+
{
48+
}
49+
50+
public ImmutableArray<ServicesItem> Services { get; private set; } = ImmutableArray<ServicesItem>.Empty;
51+
}
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.Operations;
6+
7+
namespace Microsoft.AspNetCore.Analyzers
8+
{
9+
internal class ServicesItem
10+
{
11+
public ServicesItem(IInvocationOperation operation)
12+
{
13+
Operation = operation;
14+
}
15+
16+
public IInvocationOperation Operation { get; }
17+
18+
public IMethodSymbol UseMethod => Operation.TargetMethod;
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis.Diagnostics;
5+
6+
namespace Microsoft.AspNetCore.Analyzers
7+
{
8+
internal class StartupAnalysisContext
9+
{
10+
public StartupAnalysisContext(OperationBlockStartAnalysisContext operationBlockStartAnalysisContext, StartupSymbols startupSymbols)
11+
{
12+
OperationBlockStartAnalysisContext = operationBlockStartAnalysisContext;
13+
StartupSymbols = startupSymbols;
14+
}
15+
16+
public OperationBlockStartAnalysisContext OperationBlockStartAnalysisContext { get; }
17+
18+
public StartupSymbols StartupSymbols { get; }
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
7+
namespace Microsoft.AspNetCore.Analyzers
8+
{
9+
public partial class StartupAnalzyer : DiagnosticAnalyzer
10+
{
11+
internal readonly static DiagnosticDescriptor UnsupportedUseMvcWithEndpointRouting = new DiagnosticDescriptor(
12+
"MVC1005",
13+
"Cannot use UseMvc with Endpoint Routing.",
14+
"Using '{0}' to configure MVC is not supported while using Endpoint Routing. To continue using '{0}', please set 'MvcOptions.EnableEndpointRounting = false' inside '{1}'.",
15+
"Usage",
16+
DiagnosticSeverity.Warning,
17+
isEnabledByDefault: true,
18+
helpLinkUri: "https://aka.ms/YJggeFn");
19+
}
20+
}

0 commit comments

Comments
 (0)