Refactor of Startup analyzer code
This commit is contained in:
parent
d98bde4fc7
commit
753e98c96d
|
|
@ -1,41 +1,42 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
internal class BuildServiceProviderValidator : StartupDiagnosticValidator
|
internal class BuildServiceProviderValidator
|
||||||
{
|
{
|
||||||
public static BuildServiceProviderValidator CreateAndInitialize(CompilationAnalysisContext context, ConcurrentBag<StartupComputedAnalysis> analyses)
|
private readonly StartupAnalysis _context;
|
||||||
|
|
||||||
|
public BuildServiceProviderValidator(StartupAnalysis context)
|
||||||
{
|
{
|
||||||
if (analyses == null)
|
_context = context;
|
||||||
{
|
}
|
||||||
throw new ArgumentNullException(nameof(analyses));
|
|
||||||
}
|
|
||||||
|
|
||||||
var validator = new BuildServiceProviderValidator();
|
public void AnalyzeSymbol(SymbolAnalysisContext context)
|
||||||
|
{
|
||||||
|
Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType);
|
||||||
|
Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol));
|
||||||
|
|
||||||
foreach (var serviceAnalysis in analyses.OfType<ServicesAnalysis>())
|
var type = (INamedTypeSymbol)context.Symbol;
|
||||||
|
|
||||||
|
foreach (var serviceAnalysis in _context.GetRelatedAnalyses<ServicesAnalysis>(type))
|
||||||
{
|
{
|
||||||
foreach (var serviceItem in serviceAnalysis.Services)
|
foreach (var serviceItem in serviceAnalysis.Services)
|
||||||
{
|
{
|
||||||
if (serviceItem.UseMethod.Name == "BuildServiceProvider")
|
if (serviceItem.UseMethod.Name == "BuildServiceProvider")
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
StartupAnalzyer.BuildServiceProviderShouldNotCalledInConfigureServicesMethod,
|
StartupAnalzyer.Diagnostics.BuildServiceProviderShouldNotCalledInConfigureServicesMethod,
|
||||||
serviceItem.Operation.Syntax.GetLocation(),
|
serviceItem.Operation.Syntax.GetLocation(),
|
||||||
serviceItem.UseMethod.Name,
|
serviceItem.UseMethod.Name,
|
||||||
serviceAnalysis.ConfigureServicesMethod.Name));
|
serviceAnalysis.ConfigureServicesMethod.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return validator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal abstract class ConfigureMethodAnalysis : StartupComputedAnalysis
|
|
||||||
{
|
|
||||||
protected ConfigureMethodAnalysis(IMethodSymbol configureMethod)
|
|
||||||
: base(configureMethod.ContainingType)
|
|
||||||
{
|
|
||||||
ConfigureMethod = configureMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMethodSymbol ConfigureMethod { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +1,23 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Operations;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
internal class MiddlewareAnalysis : ConfigureMethodAnalysis
|
internal class MiddlewareAnalysis
|
||||||
{
|
{
|
||||||
public static MiddlewareAnalysis CreateAndInitialize(StartupAnalysisContext context)
|
public MiddlewareAnalysis(IMethodSymbol configureMethod, ImmutableArray<MiddlewareItem> middleware)
|
||||||
{
|
{
|
||||||
if (context == null)
|
ConfigureMethod = configureMethod;
|
||||||
{
|
Middleware = middleware;
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
var symbols = context.StartupSymbols;
|
|
||||||
var analysis = new MiddlewareAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
|
|
||||||
|
|
||||||
var middleware = ImmutableArray.CreateBuilder<MiddlewareItem>();
|
|
||||||
|
|
||||||
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
|
|
||||||
{
|
|
||||||
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
|
|
||||||
// rather than invocation.Instance.
|
|
||||||
if (context.Operation is IInvocationOperation invocation &&
|
|
||||||
invocation.Instance == null &&
|
|
||||||
invocation.Arguments.Length >= 1 &&
|
|
||||||
invocation.Arguments[0].Parameter?.Type == symbols.IApplicationBuilder)
|
|
||||||
{
|
|
||||||
middleware.Add(new MiddlewareItem(invocation));
|
|
||||||
}
|
|
||||||
}, OperationKind.Invocation);
|
|
||||||
|
|
||||||
context.OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction(context =>
|
|
||||||
{
|
|
||||||
analysis.Middleware = middleware.ToImmutable();
|
|
||||||
});
|
|
||||||
|
|
||||||
return analysis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MiddlewareAnalysis(IMethodSymbol configureMethod)
|
public INamedTypeSymbol StartupType => ConfigureMethod.ContainingType;
|
||||||
: base(configureMethod)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableArray<MiddlewareItem> Middleware { get; private set; } = ImmutableArray<MiddlewareItem>.Empty;
|
public IMethodSymbol ConfigureMethod { get; }
|
||||||
|
|
||||||
|
public ImmutableArray<MiddlewareItem> Middleware { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class MiddlewareAnalyzer
|
||||||
|
{
|
||||||
|
private readonly StartupAnalysisBuilder _context;
|
||||||
|
|
||||||
|
public MiddlewareAnalyzer(StartupAnalysisBuilder context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AnalyzeConfigureMethod(OperationBlockStartAnalysisContext context)
|
||||||
|
{
|
||||||
|
var configureMethod = (IMethodSymbol)context.OwningSymbol;
|
||||||
|
var middleware = ImmutableArray.CreateBuilder<MiddlewareItem>();
|
||||||
|
|
||||||
|
// Note: this is a simple source-order implementation. We don't attempt perform data flow
|
||||||
|
// analysis in order to determine the actual order in which middleware are ordered.
|
||||||
|
//
|
||||||
|
// This can currently be confused by things like Map(...)
|
||||||
|
context.RegisterOperationAction(context =>
|
||||||
|
{
|
||||||
|
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
|
||||||
|
// rather than invocation.Instance.
|
||||||
|
if (context.Operation is IInvocationOperation invocation &&
|
||||||
|
invocation.Instance == null &&
|
||||||
|
invocation.Arguments.Length >= 1 &&
|
||||||
|
invocation.Arguments[0].Parameter?.Type == _context.StartupSymbols.IApplicationBuilder)
|
||||||
|
{
|
||||||
|
middleware.Add(new MiddlewareItem(invocation));
|
||||||
|
}
|
||||||
|
}, OperationKind.Invocation);
|
||||||
|
|
||||||
|
context.RegisterOperationBlockEndAction(context =>
|
||||||
|
{
|
||||||
|
_context.ReportAnalysis(new MiddlewareAnalysis(configureMethod, middleware.ToImmutable()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Analyzers;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
|
||||||
using Microsoft.CodeAnalysis.Operations;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal class MvcOptionsAnalysis : ConfigureServicesMethodAnalysis
|
|
||||||
{
|
|
||||||
public static MvcOptionsAnalysis CreateAndInitialize(StartupAnalysisContext context)
|
|
||||||
{
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
var analysis = new MvcOptionsAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
|
|
||||||
|
|
||||||
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
|
|
||||||
{
|
|
||||||
if (context.Operation is ISimpleAssignmentOperation operation &&
|
|
||||||
operation.Value.ConstantValue.HasValue &&
|
|
||||||
operation.Target is IPropertyReferenceOperation property &&
|
|
||||||
property.Member?.Name == SymbolNames.EnableEndpointRoutingProperty)
|
|
||||||
{
|
|
||||||
analysis.EndpointRoutingEnabled = operation.Value.ConstantValue.Value as bool?;
|
|
||||||
}
|
|
||||||
|
|
||||||
}, OperationKind.SimpleAssignment);
|
|
||||||
|
|
||||||
return analysis;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MvcOptionsAnalysis(IMethodSymbol configureServicesMethod)
|
|
||||||
: base(configureServicesMethod)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? EndpointRoutingEnabled { get; private set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
internal abstract class ConfigureServicesMethodAnalysis : StartupComputedAnalysis
|
internal class OptionsAnalysis
|
||||||
{
|
{
|
||||||
protected ConfigureServicesMethodAnalysis(IMethodSymbol configureServicesMethod)
|
public OptionsAnalysis(IMethodSymbol configureServicesMethod, ImmutableArray<OptionsItem> options)
|
||||||
: base(configureServicesMethod.ContainingType)
|
|
||||||
{
|
{
|
||||||
ConfigureServicesMethod = configureServicesMethod;
|
ConfigureServicesMethod = configureServicesMethod;
|
||||||
|
Options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public INamedTypeSymbol StartupType => ConfigureServicesMethod.ContainingType;
|
||||||
|
|
||||||
public IMethodSymbol ConfigureServicesMethod { get; }
|
public IMethodSymbol ConfigureServicesMethod { get; }
|
||||||
|
|
||||||
|
public ImmutableArray<OptionsItem> Options { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class OptionsAnalyzer
|
||||||
|
{
|
||||||
|
private readonly StartupAnalysisBuilder _context;
|
||||||
|
|
||||||
|
public OptionsAnalyzer(StartupAnalysisBuilder context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AnalyzeConfigureServices(OperationBlockStartAnalysisContext context)
|
||||||
|
{
|
||||||
|
var configureServicesMethod = (IMethodSymbol)context.OwningSymbol;
|
||||||
|
var options = ImmutableArray.CreateBuilder<OptionsItem>();
|
||||||
|
context.RegisterOperationAction(context =>
|
||||||
|
{
|
||||||
|
if (context.Operation is ISimpleAssignmentOperation operation &&
|
||||||
|
operation.Value.ConstantValue.HasValue &&
|
||||||
|
operation.Target is IPropertyReferenceOperation property &&
|
||||||
|
property.Property?.ContainingType?.Name != null &&
|
||||||
|
property.Property.ContainingType.Name.EndsWith("Options"))
|
||||||
|
{
|
||||||
|
options.Add(new OptionsItem(property.Property, operation.Value.ConstantValue.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}, OperationKind.SimpleAssignment);
|
||||||
|
|
||||||
|
context.RegisterOperationBlockEndAction(context =>
|
||||||
|
{
|
||||||
|
_context.ReportAnalysis(new OptionsAnalysis(configureServicesMethod, options.ToImmutable()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Analyzers;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal static class OptionsFacts
|
||||||
|
{
|
||||||
|
public static bool IsEndpointRoutingExplicitlyDisabled(OptionsAnalysis analysis)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < analysis.Options.Length; i++)
|
||||||
|
{
|
||||||
|
var item = analysis.Options[i];
|
||||||
|
if (string.Equals(item.OptionsType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), SymbolNames.MvcOptions) &&
|
||||||
|
string.Equals(item.Property.Name, SymbolNames.EnableEndpointRoutingProperty, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return item.ConstantValue as bool? == false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class OptionsItem
|
||||||
|
{
|
||||||
|
public OptionsItem(IPropertySymbol property, object constantValue)
|
||||||
|
{
|
||||||
|
Property = property;
|
||||||
|
ConstantValue = constantValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INamedTypeSymbol OptionsType => Property.ContainingType;
|
||||||
|
|
||||||
|
public IPropertySymbol Property { get; }
|
||||||
|
|
||||||
|
public object ConstantValue { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,52 +1,23 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Operations;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
internal class ServicesAnalysis : ConfigureServicesMethodAnalysis
|
internal class ServicesAnalysis
|
||||||
{
|
{
|
||||||
public static ServicesAnalysis CreateAndInitialize(StartupAnalysisContext context)
|
public ServicesAnalysis(IMethodSymbol configureServicesMethod, ImmutableArray<ServicesItem> services)
|
||||||
{
|
{
|
||||||
if (context == null)
|
ConfigureServicesMethod = configureServicesMethod;
|
||||||
{
|
Services = services;
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
var symbols = context.StartupSymbols;
|
|
||||||
var analysis = new ServicesAnalysis((IMethodSymbol)context.OperationBlockStartAnalysisContext.OwningSymbol);
|
|
||||||
|
|
||||||
var services = ImmutableArray.CreateBuilder<ServicesItem>();
|
|
||||||
context.OperationBlockStartAnalysisContext.RegisterOperationAction(context =>
|
|
||||||
{
|
|
||||||
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
|
|
||||||
// rather than invocation.Instance.
|
|
||||||
if (context.Operation is IInvocationOperation invocation &&
|
|
||||||
invocation.Instance == null &&
|
|
||||||
invocation.Arguments.Length >= 1 &&
|
|
||||||
invocation.Arguments[0].Parameter?.Type == symbols.IServiceCollection)
|
|
||||||
{
|
|
||||||
services.Add(new ServicesItem(invocation));
|
|
||||||
}
|
|
||||||
}, OperationKind.Invocation);
|
|
||||||
|
|
||||||
context.OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction(context =>
|
|
||||||
{
|
|
||||||
analysis.Services = services.ToImmutable();
|
|
||||||
});
|
|
||||||
|
|
||||||
return analysis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServicesAnalysis(IMethodSymbol configureServicesMethod)
|
public INamedTypeSymbol StartupType => ConfigureServicesMethod.ContainingType;
|
||||||
: base(configureServicesMethod)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImmutableArray<ServicesItem> Services { get; private set; } = ImmutableArray<ServicesItem>.Empty;
|
public IMethodSymbol ConfigureServicesMethod { get; }
|
||||||
|
|
||||||
|
public ImmutableArray<ServicesItem> Services { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class ServicesAnalyzer
|
||||||
|
{
|
||||||
|
private readonly StartupAnalysisBuilder _context;
|
||||||
|
|
||||||
|
public ServicesAnalyzer(StartupAnalysisBuilder context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AnalyzeConfigureServices(OperationBlockStartAnalysisContext context)
|
||||||
|
{
|
||||||
|
var configureServicesMethod = (IMethodSymbol)context.OwningSymbol;
|
||||||
|
var services = ImmutableArray.CreateBuilder<ServicesItem>();
|
||||||
|
context.RegisterOperationAction(context =>
|
||||||
|
{
|
||||||
|
// We're looking for usage of extension methods, so we need to look at the 'this' parameter
|
||||||
|
// rather than invocation.Instance.
|
||||||
|
if (context.Operation is IInvocationOperation invocation &&
|
||||||
|
invocation.Instance == null &&
|
||||||
|
invocation.Arguments.Length >= 1 &&
|
||||||
|
invocation.Arguments[0].Parameter?.Type == _context.StartupSymbols.IServiceCollection)
|
||||||
|
{
|
||||||
|
services.Add(new ServicesItem(invocation));
|
||||||
|
}
|
||||||
|
}, OperationKind.Invocation);
|
||||||
|
|
||||||
|
context.RegisterOperationBlockEndAction(context =>
|
||||||
|
{
|
||||||
|
_context.ReportAnalysis(new ServicesAnalysis(configureServicesMethod, services.ToImmutable()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class StartupAnalysis
|
||||||
|
{
|
||||||
|
private ImmutableDictionary<INamedTypeSymbol, ImmutableArray<object>> _analysesByType;
|
||||||
|
|
||||||
|
public StartupAnalysis(
|
||||||
|
StartupSymbols startupSymbols,
|
||||||
|
ImmutableDictionary<INamedTypeSymbol, ImmutableArray<object>> analysesByType)
|
||||||
|
{
|
||||||
|
StartupSymbols = startupSymbols;
|
||||||
|
_analysesByType = analysesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StartupSymbols StartupSymbols { get; }
|
||||||
|
|
||||||
|
public T? GetRelatedSingletonAnalysis<T>(INamedTypeSymbol type) where T : class
|
||||||
|
{
|
||||||
|
if (_analysesByType.TryGetValue(type, out var list))
|
||||||
|
{
|
||||||
|
for (var i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
if (list[i] is T item)
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableArray<T> GetRelatedAnalyses<T>(INamedTypeSymbol type) where T : class
|
||||||
|
{
|
||||||
|
var items = ImmutableArray.CreateBuilder<T>();
|
||||||
|
if (_analysesByType.TryGetValue(type, out var list))
|
||||||
|
{
|
||||||
|
for (var i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
if (list[i] is T item)
|
||||||
|
{
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.ToImmutable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class StartupAnalysisBuilder
|
||||||
|
{
|
||||||
|
private readonly Dictionary<INamedTypeSymbol, List<object>> _analysesByType;
|
||||||
|
private readonly StartupAnalzyer _analyzer;
|
||||||
|
private readonly object _lock;
|
||||||
|
|
||||||
|
public StartupAnalysisBuilder(StartupAnalzyer analyzer, StartupSymbols startupSymbols)
|
||||||
|
{
|
||||||
|
_analyzer = analyzer;
|
||||||
|
StartupSymbols = startupSymbols;
|
||||||
|
|
||||||
|
_analysesByType = new Dictionary<INamedTypeSymbol, List<object>>();
|
||||||
|
_lock = new object();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StartupSymbols StartupSymbols { get; }
|
||||||
|
|
||||||
|
public StartupAnalysis Build()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return new StartupAnalysis(
|
||||||
|
StartupSymbols,
|
||||||
|
_analysesByType.ToImmutableDictionary(
|
||||||
|
k => k.Key,
|
||||||
|
v => v.Value.ToImmutableArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReportAnalysis(ServicesAnalysis analysis)
|
||||||
|
{
|
||||||
|
ReportAnalysisCore(analysis.StartupType, analysis);
|
||||||
|
_analyzer.OnServicesAnalysisCompleted(analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReportAnalysis(OptionsAnalysis analysis)
|
||||||
|
{
|
||||||
|
ReportAnalysisCore(analysis.StartupType, analysis);
|
||||||
|
_analyzer.OnOptionsAnalysisCompleted(analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReportAnalysis(MiddlewareAnalysis analysis)
|
||||||
|
{
|
||||||
|
ReportAnalysisCore(analysis.StartupType, analysis);
|
||||||
|
_analyzer.OnMiddlewareAnalysisCompleted(analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportAnalysisCore(INamedTypeSymbol type, object analysis)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!_analysesByType.TryGetValue(type, out var list))
|
||||||
|
{
|
||||||
|
list = new List<object>();
|
||||||
|
_analysesByType.Add(type, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(analysis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal class StartupAnalysisContext
|
|
||||||
{
|
|
||||||
#pragma warning disable RS1012 // Start action has no registered actions.
|
|
||||||
public StartupAnalysisContext(OperationBlockStartAnalysisContext operationBlockStartAnalysisContext, StartupSymbols startupSymbols)
|
|
||||||
{
|
|
||||||
OperationBlockStartAnalysisContext = operationBlockStartAnalysisContext;
|
|
||||||
StartupSymbols = startupSymbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationBlockStartAnalysisContext OperationBlockStartAnalysisContext { get; }
|
|
||||||
|
|
||||||
public StartupSymbols StartupSymbols { get; }
|
|
||||||
#pragma warning restore RS1012 // Start action has no registered actions.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
|
@ -8,23 +9,39 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
||||||
{
|
{
|
||||||
internal readonly static DiagnosticDescriptor UnsupportedUseMvcWithEndpointRouting = new DiagnosticDescriptor(
|
internal static class Diagnostics
|
||||||
"MVC1005",
|
{
|
||||||
"Cannot use UseMvc with Endpoint Routing.",
|
public static readonly ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics;
|
||||||
"Using '{0}' to configure MVC is not supported while using Endpoint Routing. To continue using '{0}', please set 'MvcOptions.EnableEndpointRouting = false' inside '{1}'.",
|
|
||||||
"Usage",
|
|
||||||
DiagnosticSeverity.Warning,
|
|
||||||
isEnabledByDefault: true,
|
|
||||||
helpLinkUri: "https://aka.ms/YJggeFn");
|
|
||||||
|
|
||||||
internal readonly static DiagnosticDescriptor BuildServiceProviderShouldNotCalledInConfigureServicesMethod = new DiagnosticDescriptor(
|
static Diagnostics()
|
||||||
"MVC1007",
|
{
|
||||||
"Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'",
|
SupportedDiagnostics = ImmutableArray.Create<DiagnosticDescriptor>(new[]
|
||||||
"Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.",
|
{
|
||||||
"Design",
|
// ASP
|
||||||
DiagnosticSeverity.Warning,
|
BuildServiceProviderShouldNotCalledInConfigureServicesMethod,
|
||||||
isEnabledByDefault: true,
|
|
||||||
helpLinkUri: "https://aka.ms/AA5k895"
|
// MVC
|
||||||
);
|
UnsupportedUseMvcWithEndpointRouting,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly static DiagnosticDescriptor BuildServiceProviderShouldNotCalledInConfigureServicesMethod = new DiagnosticDescriptor(
|
||||||
|
"ASP0000",
|
||||||
|
"Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'",
|
||||||
|
"Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.",
|
||||||
|
"Design",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
helpLinkUri: "https://aka.ms/AA5k895");
|
||||||
|
|
||||||
|
internal readonly static DiagnosticDescriptor UnsupportedUseMvcWithEndpointRouting = new DiagnosticDescriptor(
|
||||||
|
"MVC1005",
|
||||||
|
"Cannot use UseMvc with Endpoint Routing.",
|
||||||
|
"Using '{0}' to configure MVC is not supported while using Endpoint Routing. To continue using '{0}', please set 'MvcOptions.EnableEndpointRounting = false' inside '{1}'.",
|
||||||
|
"Usage",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
helpLinkUri: "https://aka.ms/YJggeFn");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,42 @@ using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
|
// Events for testability. Allows us to unit test the data we gather from analysis.
|
||||||
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
||||||
{
|
{
|
||||||
internal event EventHandler<StartupComputedAnalysis>? AnalysisStarted;
|
|
||||||
|
|
||||||
private void OnAnalysisStarted(StartupComputedAnalysis analysis)
|
|
||||||
{
|
|
||||||
AnalysisStarted?.Invoke(this, analysis);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal event EventHandler<IMethodSymbol>? ConfigureServicesMethodFound;
|
internal event EventHandler<IMethodSymbol>? ConfigureServicesMethodFound;
|
||||||
|
|
||||||
private void OnConfigureServicesMethodFound(IMethodSymbol method)
|
internal void OnConfigureServicesMethodFound(IMethodSymbol method)
|
||||||
{
|
{
|
||||||
ConfigureServicesMethodFound?.Invoke(this, method);
|
ConfigureServicesMethodFound?.Invoke(this, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal event EventHandler<ServicesAnalysis>? ServicesAnalysisCompleted;
|
||||||
|
|
||||||
|
internal void OnServicesAnalysisCompleted(ServicesAnalysis analysis)
|
||||||
|
{
|
||||||
|
ServicesAnalysisCompleted?.Invoke(this, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal event EventHandler<OptionsAnalysis>? OptionsAnalysisCompleted;
|
||||||
|
|
||||||
|
internal void OnOptionsAnalysisCompleted(OptionsAnalysis analysis)
|
||||||
|
{
|
||||||
|
OptionsAnalysisCompleted?.Invoke(this, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
internal event EventHandler<IMethodSymbol>? ConfigureMethodFound;
|
internal event EventHandler<IMethodSymbol>? ConfigureMethodFound;
|
||||||
|
|
||||||
private void OnConfigureMethodFound(IMethodSymbol method)
|
internal void OnConfigureMethodFound(IMethodSymbol method)
|
||||||
{
|
{
|
||||||
ConfigureMethodFound?.Invoke(this, method);
|
ConfigureMethodFound?.Invoke(this, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal event EventHandler<MiddlewareAnalysis>? MiddlewareAnalysisCompleted;
|
||||||
|
|
||||||
|
internal void OnMiddlewareAnalysisCompleted(MiddlewareAnalysis analysis)
|
||||||
|
{
|
||||||
|
MiddlewareAnalysisCompleted?.Invoke(this, analysis);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
@ -12,46 +11,16 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
public partial class StartupAnalzyer : DiagnosticAnalyzer
|
||||||
{
|
{
|
||||||
#pragma warning disable RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer.
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => Diagnostics.SupportedDiagnostics;
|
||||||
private readonly static Func<StartupAnalysisContext, StartupComputedAnalysis>[] ConfigureServicesMethodAnalysisFactories = new Func<StartupAnalysisContext, StartupComputedAnalysis>[]
|
|
||||||
{
|
|
||||||
ServicesAnalysis.CreateAndInitialize,
|
|
||||||
MvcOptionsAnalysis.CreateAndInitialize,
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly static Func<StartupAnalysisContext, StartupComputedAnalysis>[] ConfigureMethodAnalysisFactories = new Func<StartupAnalysisContext, StartupComputedAnalysis>[]
|
|
||||||
{
|
|
||||||
MiddlewareAnalysis.CreateAndInitialize,
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly static Func<CompilationAnalysisContext, ConcurrentBag<StartupComputedAnalysis>, StartupDiagnosticValidator>[] DiagnosticValidatorFactories = new Func<CompilationAnalysisContext, ConcurrentBag<StartupComputedAnalysis>, StartupDiagnosticValidator>[]
|
|
||||||
{
|
|
||||||
UseMvcDiagnosticValidator.CreateAndInitialize,
|
|
||||||
BuildServiceProviderValidator.CreateAndInitialize
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma warning restore RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer.
|
|
||||||
|
|
||||||
public StartupAnalzyer()
|
|
||||||
{
|
|
||||||
SupportedDiagnostics = ImmutableArray.Create<DiagnosticDescriptor>(new[]
|
|
||||||
{
|
|
||||||
UnsupportedUseMvcWithEndpointRouting,
|
|
||||||
BuildServiceProviderShouldNotCalledInConfigureServicesMethod
|
|
||||||
});
|
|
||||||
|
|
||||||
// By default the analyzer will only run for files ending with Startup.cs
|
|
||||||
// Can be overriden for unit testing other file names
|
|
||||||
// Analzyer only runs for C# so limiting to *.cs file is fine
|
|
||||||
StartupFilePredicate = path => path.EndsWith("Startup.cs", StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Func<string, bool> StartupFilePredicate { get; set; }
|
|
||||||
|
|
||||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
|
|
||||||
|
|
||||||
public override void Initialize(AnalysisContext context)
|
public override void Initialize(AnalysisContext context)
|
||||||
{
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
context.RegisterCompilationStartAction(OnCompilationStart);
|
context.RegisterCompilationStartAction(OnCompilationStart);
|
||||||
}
|
}
|
||||||
|
|
@ -61,91 +30,61 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var symbols = new StartupSymbols(context.Compilation);
|
var symbols = new StartupSymbols(context.Compilation);
|
||||||
|
|
||||||
// Don't run analyzer if ASP.NET Core types cannot be found
|
// Don't run analyzer if ASP.NET Core types cannot be found
|
||||||
if (symbols.IServiceCollection == null || symbols.IApplicationBuilder == null)
|
if (!symbols.HasRequiredSymbols)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This analyzer is a general-purpose framework that functions by:
|
context.RegisterSymbolStartAction(context =>
|
||||||
// 1. Discovering Startup methods (ConfigureServices, Configure)
|
|
||||||
// 2. Launching additional analyses of these Startup methods and collecting results
|
|
||||||
// 3. Running a final pass to add diagnostics based on computed state
|
|
||||||
var analyses = new ConcurrentBag<StartupComputedAnalysis>();
|
|
||||||
|
|
||||||
context.RegisterOperationBlockStartAction(context =>
|
|
||||||
{
|
{
|
||||||
AnalyzeStartupMethods(context, symbols, analyses);
|
var type = (INamedTypeSymbol)context.Symbol;
|
||||||
});
|
if (!StartupFacts.IsStartupClass(symbols, type))
|
||||||
|
|
||||||
// Run after analyses have had a chance to finish to add diagnostics.
|
|
||||||
context.RegisterCompilationEndAction(analysisContext =>
|
|
||||||
{
|
|
||||||
RunAnalysis(analysisContext, analyses);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunAnalysis(CompilationAnalysisContext analysisContext, ConcurrentBag<StartupComputedAnalysis> analyses)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < DiagnosticValidatorFactories.Length; i++)
|
|
||||||
{
|
|
||||||
var validator = DiagnosticValidatorFactories[i].Invoke(analysisContext, analyses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AnalyzeStartupMethods(OperationBlockStartAnalysisContext context, StartupSymbols symbols, ConcurrentBag<StartupComputedAnalysis> analyses)
|
|
||||||
{
|
|
||||||
if (!IsStartupFile(context))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.OwningSymbol.Kind != SymbolKind.Method)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var startupAnalysisContext = new StartupAnalysisContext(context, symbols);
|
|
||||||
|
|
||||||
var method = (IMethodSymbol)context.OwningSymbol;
|
|
||||||
if (StartupFacts.IsConfigureServices(symbols, method))
|
|
||||||
{
|
|
||||||
for (var i = 0; i < ConfigureServicesMethodAnalysisFactories.Length; i++)
|
|
||||||
{
|
{
|
||||||
var analysis = ConfigureServicesMethodAnalysisFactories[i].Invoke(startupAnalysisContext);
|
// Not a startup class, nothing to do.
|
||||||
analyses.Add(analysis);
|
return;
|
||||||
|
|
||||||
OnAnalysisStarted(analysis);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnConfigureServicesMethodFound(method);
|
// This analyzer fans out a bunch of jobs. The context will capture the results of doing analysis
|
||||||
}
|
// on the startup code, so that other analyzers that run later can examine them.
|
||||||
|
var builder = new StartupAnalysisBuilder(this, symbols);
|
||||||
|
|
||||||
if (StartupFacts.IsConfigure(symbols, method))
|
var services = new ServicesAnalyzer(builder);
|
||||||
{
|
var options = new OptionsAnalyzer(builder);
|
||||||
for (var i = 0; i < ConfigureMethodAnalysisFactories.Length; i++)
|
var middleware = new MiddlewareAnalyzer(builder);
|
||||||
|
|
||||||
|
context.RegisterOperationBlockStartAction(context =>
|
||||||
{
|
{
|
||||||
var analysis = ConfigureMethodAnalysisFactories[i].Invoke(startupAnalysisContext);
|
if (context.OwningSymbol.Kind != SymbolKind.Method)
|
||||||
analyses.Add(analysis);
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OnAnalysisStarted(analysis);
|
var method = (IMethodSymbol)context.OwningSymbol;
|
||||||
}
|
if (StartupFacts.IsConfigureServices(symbols, method))
|
||||||
|
{
|
||||||
|
OnConfigureServicesMethodFound(method);
|
||||||
|
|
||||||
OnConfigureMethodFound(method);
|
services.AnalyzeConfigureServices(context);
|
||||||
}
|
options.AnalyzeConfigureServices(context);
|
||||||
}
|
}
|
||||||
#pragma warning disable RS1012 // Start action has no registered actions.
|
|
||||||
private bool IsStartupFile(OperationBlockStartAnalysisContext context)
|
if (StartupFacts.IsConfigure(symbols, method))
|
||||||
{
|
{
|
||||||
foreach (var location in context.OwningSymbol.Locations)
|
OnConfigureMethodFound(method);
|
||||||
{
|
|
||||||
if (location.IsInSource && StartupFilePredicate(location.SourceTree.FilePath))
|
middleware.AnalyzeConfigureMethod(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run after analyses have had a chance to finish to add diagnostics.
|
||||||
|
context.RegisterSymbolEndAction(context =>
|
||||||
{
|
{
|
||||||
return true;
|
var analysis = builder.Build();
|
||||||
}
|
new UseMvcAnalyzer(analysis).AnalyzeSymbol(context);
|
||||||
}
|
new BuildServiceProviderValidator(analysis).AnalyzeSymbol(context);
|
||||||
|
});
|
||||||
|
|
||||||
return false;
|
}, SymbolKind.NamedType);
|
||||||
}
|
}
|
||||||
#pragma warning restore RS1012 // Start action has no registered actions.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal abstract class StartupComputedAnalysis
|
|
||||||
{
|
|
||||||
protected StartupComputedAnalysis(INamedTypeSymbol enclosingType)
|
|
||||||
{
|
|
||||||
EnclosingType = enclosingType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public INamedTypeSymbol EnclosingType { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal abstract class StartupDiagnosticValidator
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,13 +2,41 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc.Analyzers;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
internal static class StartupFacts
|
internal static class StartupFacts
|
||||||
{
|
{
|
||||||
|
public static bool IsStartupClass(StartupSymbols symbols, INamedTypeSymbol type)
|
||||||
|
{
|
||||||
|
if (symbols == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(symbols));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's not good enough to just look for ConfigureServices or Configure as a hueristic.
|
||||||
|
// ConfigureServices might not appear in trivial cases, and Configure might be named ConfigureDevelopment
|
||||||
|
// or something similar.
|
||||||
|
//
|
||||||
|
// Since we already are analyzing the symbol it should be cheap to do a quick pass over the members.
|
||||||
|
var members = type.GetMembers();
|
||||||
|
for (var i = 0; i < members.Length; i++)
|
||||||
|
{
|
||||||
|
if (members[i] is IMethodSymbol method && (IsConfigureServices(symbols, method) || IsConfigure(symbols, method)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsConfigureServices(StartupSymbols symbols, IMethodSymbol symbol)
|
public static bool IsConfigureServices(StartupSymbols symbols, IMethodSymbol symbol)
|
||||||
{
|
{
|
||||||
if (symbol == null)
|
if (symbol == null)
|
||||||
|
|
@ -21,7 +49,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.Equals(symbol.Name, SymbolNames.ConfigureServicesMethod, StringComparison.Ordinal))
|
if (!string.Equals(symbol.Name, "ConfigureServices", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +79,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbol.Name == null || !symbol.Name.StartsWith(SymbolNames.ConfigureMethod, StringComparison.Ordinal))
|
if (symbol.Name == null || !symbol.Name.StartsWith("Configure", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +87,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
// IApplicationBuilder can appear in any parameter
|
// IApplicationBuilder can appear in any parameter
|
||||||
for (var i = 0; i < symbol.Parameters.Length; i++)
|
for (var i = 0; i < symbol.Parameters.Length; i++)
|
||||||
{
|
{
|
||||||
if (symbol.Parameters[i].Type == symbols.IApplicationBuilder)
|
if (symbol.Parameters[i].Type == symbols.IApplicationBuilder)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc.Analyzers;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
@ -10,15 +9,15 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
public StartupSymbols(Compilation compilation)
|
public StartupSymbols(Compilation compilation)
|
||||||
{
|
{
|
||||||
IApplicationBuilder = compilation.GetTypeByMetadataName(SymbolNames.IApplicationBuilder);
|
IApplicationBuilder = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Builder.IApplicationBuilder");
|
||||||
IServiceCollection = compilation.GetTypeByMetadataName(SymbolNames.IServiceCollection);
|
IServiceCollection = compilation.GetTypeByMetadataName("Microsoft.Extensions.DependencyInjection.IServiceCollection");
|
||||||
MvcOptions = compilation.GetTypeByMetadataName(SymbolNames.MvcOptions);
|
MvcOptions = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.MvcOptions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null;
|
||||||
|
|
||||||
public INamedTypeSymbol IApplicationBuilder { get; }
|
public INamedTypeSymbol IApplicationBuilder { get; }
|
||||||
|
|
||||||
public INamedTypeSymbol IServiceCollection { get; }
|
public INamedTypeSymbol IServiceCollection { get; }
|
||||||
|
|
||||||
public INamedTypeSymbol MvcOptions { get; }
|
public INamedTypeSymbol MvcOptions { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class UseMvcAnalyzer
|
||||||
|
{
|
||||||
|
private readonly StartupAnalysis _context;
|
||||||
|
|
||||||
|
public UseMvcAnalyzer(StartupAnalysis context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AnalyzeSymbol(SymbolAnalysisContext context)
|
||||||
|
{
|
||||||
|
Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType);
|
||||||
|
Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol));
|
||||||
|
|
||||||
|
var type = (INamedTypeSymbol)context.Symbol;
|
||||||
|
|
||||||
|
var optionsAnalysis = _context.GetRelatedSingletonAnalysis<OptionsAnalysis>(type);
|
||||||
|
if (optionsAnalysis == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the middleware analysis foreach of the Configure methods defined by this class and validate.
|
||||||
|
//
|
||||||
|
// Note that this doesn't attempt to handle inheritance scenarios.
|
||||||
|
foreach (var middlewareAnalysis in _context.GetRelatedAnalyses<MiddlewareAnalysis>(type))
|
||||||
|
{
|
||||||
|
foreach (var middlewareItem in middlewareAnalysis.Middleware)
|
||||||
|
{
|
||||||
|
if (middlewareItem.UseMethod.Name == "UseMvc" || middlewareItem.UseMethod.Name == "UseMvcWithDefaultRoute")
|
||||||
|
{
|
||||||
|
// Report a diagnostic if it's unclear that the user turned off Endpoint Routing.
|
||||||
|
if (!OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis))
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting,
|
||||||
|
middlewareItem.Operation.Syntax.GetLocation(),
|
||||||
|
middlewareItem.UseMethod.Name,
|
||||||
|
optionsAnalysis.ConfigureServicesMethod.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
internal class UseMvcDiagnosticValidator : StartupDiagnosticValidator
|
|
||||||
{
|
|
||||||
public static UseMvcDiagnosticValidator CreateAndInitialize(CompilationAnalysisContext context, ConcurrentBag<StartupComputedAnalysis> analyses)
|
|
||||||
{
|
|
||||||
if (analyses == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(analyses));
|
|
||||||
}
|
|
||||||
|
|
||||||
var validator = new UseMvcDiagnosticValidator();
|
|
||||||
|
|
||||||
foreach (var mvcOptionsAnalysis in analyses.OfType<MvcOptionsAnalysis>())
|
|
||||||
{
|
|
||||||
// Each analysis of the options is one-per-class (in user code). Find the middleware analysis foreach of the Configure methods
|
|
||||||
// defined by this class and validate.
|
|
||||||
//
|
|
||||||
// Note that this doesn't attempt to handle inheritance scenarios.
|
|
||||||
foreach (var middlewareAnalsysis in analyses.OfType<MiddlewareAnalysis>().Where(m => m.EnclosingType == mvcOptionsAnalysis.EnclosingType))
|
|
||||||
{
|
|
||||||
foreach (var middlewareItem in middlewareAnalsysis.Middleware)
|
|
||||||
{
|
|
||||||
if ((middlewareItem.UseMethod.Name == "UseMvc" || middlewareItem.UseMethod.Name == "UseMvcWithDefaultRoute") &&
|
|
||||||
|
|
||||||
// Report a diagnostic if it's unclear that the user turned off Endpoint Routing.
|
|
||||||
(mvcOptionsAnalysis.EndpointRoutingEnabled == true || mvcOptionsAnalysis.EndpointRoutingEnabled == null))
|
|
||||||
{
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
|
||||||
StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting,
|
|
||||||
middlewareItem.Operation.Syntax.GetLocation(),
|
|
||||||
middlewareItem.UseMethod.Name,
|
|
||||||
mvcOptionsAnalysis.ConfigureServicesMethod.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return validator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -17,14 +17,15 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public StartupAnalyzerTest()
|
public StartupAnalyzerTest()
|
||||||
{
|
{
|
||||||
StartupAnalyzer = new StartupAnalzyer();
|
StartupAnalyzer = new StartupAnalzyer();
|
||||||
StartupAnalyzer.StartupFilePredicate = path => path.Equals("Test.cs", StringComparison.Ordinal);
|
|
||||||
|
|
||||||
Runner = new MvcDiagnosticAnalyzerRunner(StartupAnalyzer);
|
Runner = new MvcDiagnosticAnalyzerRunner(StartupAnalyzer);
|
||||||
|
|
||||||
Analyses = new ConcurrentBag<StartupComputedAnalysis>();
|
Analyses = new ConcurrentBag<object>();
|
||||||
ConfigureServicesMethods = new ConcurrentBag<IMethodSymbol>();
|
ConfigureServicesMethods = new ConcurrentBag<IMethodSymbol>();
|
||||||
ConfigureMethods = new ConcurrentBag<IMethodSymbol>();
|
ConfigureMethods = new ConcurrentBag<IMethodSymbol>();
|
||||||
StartupAnalyzer.AnalysisStarted += (sender, analysis) => Analyses.Add(analysis);
|
StartupAnalyzer.ServicesAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis);
|
||||||
|
StartupAnalyzer.OptionsAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis);
|
||||||
|
StartupAnalyzer.MiddlewareAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis);
|
||||||
StartupAnalyzer.ConfigureServicesMethodFound += (sender, method) => ConfigureServicesMethods.Add(method);
|
StartupAnalyzer.ConfigureServicesMethodFound += (sender, method) => ConfigureServicesMethods.Add(method);
|
||||||
StartupAnalyzer.ConfigureMethodFound += (sender, method) => ConfigureMethods.Add(method);
|
StartupAnalyzer.ConfigureMethodFound += (sender, method) => ConfigureMethods.Add(method);
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
||||||
private MvcDiagnosticAnalyzerRunner Runner { get; }
|
private MvcDiagnosticAnalyzerRunner Runner { get; }
|
||||||
|
|
||||||
private ConcurrentBag<StartupComputedAnalysis> Analyses { get; }
|
private ConcurrentBag<object> Analyses { get; }
|
||||||
|
|
||||||
private ConcurrentBag<IMethodSymbol> ConfigureServicesMethods { get; }
|
private ConcurrentBag<IMethodSymbol> ConfigureServicesMethods { get; }
|
||||||
|
|
||||||
|
|
@ -87,8 +88,8 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var mvcOptionsAnalysis = Assert.Single(Analyses.OfType<MvcOptionsAnalysis>());
|
var optionsAnalysis = Assert.Single(Analyses.OfType<OptionsAnalysis>());
|
||||||
Assert.False(mvcOptionsAnalysis.EndpointRoutingEnabled);
|
Assert.True(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis));
|
||||||
|
|
||||||
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
||||||
|
|
@ -107,8 +108,8 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var mvcOptionsAnalysis = Assert.Single(Analyses.OfType<MvcOptionsAnalysis>());
|
var optionsAnalysis = Assert.Single(Analyses.OfType<OptionsAnalysis>());
|
||||||
Assert.False(mvcOptionsAnalysis.EndpointRoutingEnabled);
|
Assert.True(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis));
|
||||||
|
|
||||||
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
||||||
|
|
@ -130,8 +131,8 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var mvcOptionsAnalysis = Assert.Single(Analyses.OfType<MvcOptionsAnalysis>());
|
var optionsAnalysis = Assert.Single(Analyses.OfType<OptionsAnalysis>());
|
||||||
Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled);
|
Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis));
|
||||||
|
|
||||||
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
var middleware = Assert.Single(middlewareAnalysis.Middleware);
|
||||||
|
|
@ -141,7 +142,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
diagnostics,
|
diagnostics,
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -156,8 +157,8 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var mvcOptionsAnalysis = Assert.Single(Analyses.OfType<MvcOptionsAnalysis>());
|
var optionsAnalysis = Assert.Single(Analyses.OfType<OptionsAnalysis>());
|
||||||
Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled);
|
Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis));
|
||||||
|
|
||||||
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
var middlewareAnalysis = Assert.Single(Analyses.OfType<MiddlewareAnalysis>());
|
||||||
|
|
||||||
|
|
@ -173,7 +174,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
diagnostics,
|
diagnostics,
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -188,27 +189,28 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var mvcOptionsAnalysis = Assert.Single(Analyses.OfType<MvcOptionsAnalysis>());
|
var optionsAnalysis = Assert.Single(Analyses.OfType<OptionsAnalysis>());
|
||||||
Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled);
|
Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis));
|
||||||
|
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
||||||
},
|
},
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM2"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM2"], diagnostic.Location);
|
||||||
},
|
},
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM3"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM3"], diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider()
|
public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider()
|
||||||
{
|
{
|
||||||
|
|
@ -224,10 +226,11 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
Assert.Collection(diagnostics,
|
Assert.Collection(diagnostics,
|
||||||
diagnostic =>
|
diagnostic =>
|
||||||
{
|
{
|
||||||
Assert.Same(StartupAnalzyer.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, diagnostic.Descriptor);
|
Assert.Same(StartupAnalzyer.Diagnostics.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, diagnostic.Descriptor);
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestSource ReadSource(string fileName)
|
private TestSource ReadSource(string fileName)
|
||||||
{
|
{
|
||||||
return MvcTestSource.Read(nameof(StartupAnalyzerTest), fileName);
|
return MvcTestSource.Read(nameof(StartupAnalyzerTest), fileName);
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ namespace Microsoft.AspNetCore.Mvc.Api.Analyzers
|
||||||
SyntaxFactory.ParseName(ApiSymbolNames.ProducesResponseTypeAttribute)
|
SyntaxFactory.ParseName(ApiSymbolNames.ProducesResponseTypeAttribute)
|
||||||
.WithAdditionalAnnotations(Simplifier.Annotation),
|
.WithAdditionalAnnotations(Simplifier.Annotation),
|
||||||
SyntaxFactory.AttributeArgumentList().AddArguments(
|
SyntaxFactory.AttributeArgumentList().AddArguments(
|
||||||
|
|
||||||
SyntaxFactory.AttributeArgument(statusCodeSyntax)));
|
SyntaxFactory.AttributeArgument(statusCodeSyntax)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue