Consolidate general-purpose analyzers
We have some non-MVC analyzers hanging out in the Mvc.Analyzers package. Part of: #12288
This commit is contained in:
parent
b84132638f
commit
010ffe6121
|
|
@ -15,6 +15,7 @@
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||||
|
<Nullable>Enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Mvc.Analyzers;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
@ -14,8 +13,8 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
for (var i = 0; i < analysis.Options.Length; i++)
|
for (var i = 0; i < analysis.Options.Length; i++)
|
||||||
{
|
{
|
||||||
var item = analysis.Options[i];
|
var item = analysis.Options[i];
|
||||||
if (string.Equals(item.OptionsType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), SymbolNames.MvcOptions) &&
|
if (string.Equals(item.OptionsType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), SymbolNames.MvcOptions.MetadataName) &&
|
||||||
string.Equals(item.Property.Name, SymbolNames.EnableEndpointRoutingProperty, StringComparison.Ordinal))
|
string.Equals(item.Property.Name, SymbolNames.MvcOptions.EnableEndpointRoutingPropertyName, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return item.ConstantValue as bool? == false;
|
return item.ConstantValue as bool? == false;
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
IApplicationBuilder = compilation.GetTypeByMetadataName(SymbolNames.IApplicationBuilder.MetadataName);
|
IApplicationBuilder = compilation.GetTypeByMetadataName(SymbolNames.IApplicationBuilder.MetadataName);
|
||||||
IServiceCollection = compilation.GetTypeByMetadataName(SymbolNames.IServiceCollection.MetadataName);
|
IServiceCollection = compilation.GetTypeByMetadataName(SymbolNames.IServiceCollection.MetadataName);
|
||||||
|
MvcOptions = compilation.GetTypeByMetadataName(SymbolNames.MvcOptions.MetadataName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null;
|
public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null;
|
||||||
|
|
@ -18,5 +19,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public INamedTypeSymbol IApplicationBuilder { get; }
|
public INamedTypeSymbol IApplicationBuilder { get; }
|
||||||
|
|
||||||
public INamedTypeSymbol IServiceCollection { get; }
|
public INamedTypeSymbol IServiceCollection { get; }
|
||||||
|
|
||||||
|
public INamedTypeSymbol MvcOptions { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,12 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
||||||
public const string UseSignalRMethodName = "UseSignalR";
|
public const string UseSignalRMethodName = "UseSignalR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MvcOptions
|
||||||
|
{
|
||||||
|
public const string MetadataName = "Microsoft.AspNetCore.Mvc.MvcOptions";
|
||||||
|
|
||||||
|
public const string EnableEndpointRoutingPropertyName = "EnableEndpointRouting";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
||||||
public TestSource Read(string source)
|
public TestSource Read(string source)
|
||||||
{
|
{
|
||||||
|
if (!source.EndsWith(".cs"))
|
||||||
|
{
|
||||||
|
source = source + ".cs";
|
||||||
|
}
|
||||||
|
|
||||||
var filePath = Path.Combine(ProjectDirectory, "TestFiles", GetType().Name, source);
|
var filePath = Path.Combine(ProjectDirectory, "TestFiles", GetType().Name, source);
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Analyzer.Testing;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
{
|
||||||
|
internal class AnalyzersDiagnosticAnalyzerRunner : DiagnosticAnalyzerRunner
|
||||||
|
{
|
||||||
|
public AnalyzersDiagnosticAnalyzerRunner(DiagnosticAnalyzer analyzer)
|
||||||
|
{
|
||||||
|
Analyzer = analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticAnalyzer Analyzer { get; }
|
||||||
|
|
||||||
|
public Task<Diagnostic[]> GetDiagnosticsAsync(string source)
|
||||||
|
{
|
||||||
|
return GetDiagnosticsAsync(sources: new[] { source }, Analyzer, Array.Empty<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Diagnostic[]> GetDiagnosticsAsync(Project project)
|
||||||
|
{
|
||||||
|
return GetDiagnosticsAsync(new[] { project }, Analyzer, Array.Empty<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
// 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.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Analyzer.Testing;
|
using Microsoft.AspNetCore.Analyzer.Testing;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
namespace Microsoft.AspNetCore.Analyzers
|
||||||
{
|
{
|
||||||
public class StartupAnalyzerTest
|
public class StartupAnalyzerTest : AnalyzerTestBase
|
||||||
{
|
{
|
||||||
public StartupAnalyzerTest()
|
public StartupAnalyzerTest()
|
||||||
{
|
{
|
||||||
StartupAnalyzer = new StartupAnalzyer();
|
StartupAnalyzer = new StartupAnalzyer();
|
||||||
|
|
||||||
Runner = new MvcDiagnosticAnalyzerRunner(StartupAnalyzer);
|
Runner = new AnalyzersDiagnosticAnalyzerRunner(StartupAnalyzer);
|
||||||
|
|
||||||
Analyses = new ConcurrentBag<object>();
|
Analyses = new ConcurrentBag<object>();
|
||||||
ConfigureServicesMethods = new ConcurrentBag<IMethodSymbol>();
|
ConfigureServicesMethods = new ConcurrentBag<IMethodSymbol>();
|
||||||
|
|
@ -32,7 +30,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
|
|
||||||
private StartupAnalzyer StartupAnalyzer { get; }
|
private StartupAnalzyer StartupAnalyzer { get; }
|
||||||
|
|
||||||
private MvcDiagnosticAnalyzerRunner Runner { get; }
|
private AnalyzersDiagnosticAnalyzerRunner Runner { get; }
|
||||||
|
|
||||||
private ConcurrentBag<object> Analyses { get; }
|
private ConcurrentBag<object> Analyses { get; }
|
||||||
|
|
||||||
|
|
@ -44,7 +42,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_Standard()
|
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_Standard()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("StartupSignatures_Standard");
|
var source = Read("StartupSignatures_Standard");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -60,7 +58,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_MoreVariety()
|
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_MoreVariety()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("StartupSignatures_MoreVariety");
|
var source = Read("StartupSignatures_MoreVariety");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -82,7 +80,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_MvcOptionsAnalysis_UseMvc_FindsEndpointRoutingDisabled()
|
public async Task StartupAnalyzer_MvcOptionsAnalysis_UseMvc_FindsEndpointRoutingDisabled()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled");
|
var source = Read("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -102,7 +100,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_MvcOptionsAnalysis_AddMvcOptions_FindsEndpointRoutingDisabled()
|
public async Task StartupAnalyzer_MvcOptionsAnalysis_AddMvcOptions_FindsEndpointRoutingDisabled()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled");
|
var source = Read("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -125,7 +123,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_MvcOptionsAnalysis_FindsEndpointRoutingEnabled(string sourceFileName, string mvcMiddlewareName)
|
public async Task StartupAnalyzer_MvcOptionsAnalysis_FindsEndpointRoutingEnabled(string sourceFileName, string mvcMiddlewareName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource(sourceFileName);
|
var source = Read(sourceFileName);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -151,7 +149,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleMiddleware()
|
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleMiddleware()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("MvcOptions_UseMvcWithOtherMiddleware");
|
var source = Read("MvcOptions_UseMvcWithOtherMiddleware");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -183,7 +181,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleUseMvc()
|
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleUseMvc()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("MvcOptions_UseMvcMultiple");
|
var source = Read("MvcOptions_UseMvcMultiple");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -215,7 +213,7 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider()
|
public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var source = ReadSource("ConfigureServices_BuildServiceProvider");
|
var source = Read("ConfigureServices_BuildServiceProvider");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
|
||||||
|
|
@ -230,10 +228,5 @@ namespace Microsoft.AspNetCore.Analyzers
|
||||||
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestSource ReadSource(string fileName)
|
|
||||||
{
|
|
||||||
return MvcTestSource.Read(nameof(StartupAnalyzerTest), fileName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,99 +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.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Analyzers
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (symbol == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.DeclaredAccessibility != Accessibility.Public)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.Equals(symbol.Name, "ConfigureServices", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.Parameters.Length != 1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.Parameters[0].Type != symbols.IServiceCollection)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsConfigure(StartupSymbols symbols, IMethodSymbol symbol)
|
|
||||||
{
|
|
||||||
if (symbol == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.DeclaredAccessibility != Accessibility.Public)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol.Name == null || !symbol.Name.StartsWith("Configure", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IApplicationBuilder can appear in any parameter
|
|
||||||
for (var i = 0; i < symbol.Parameters.Length; i++)
|
|
||||||
{
|
|
||||||
if (symbol.Parameters[i].Type == symbols.IApplicationBuilder)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +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 class StartupSymbols
|
|
||||||
{
|
|
||||||
public StartupSymbols(Compilation compilation)
|
|
||||||
{
|
|
||||||
IApplicationBuilder = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Builder.IApplicationBuilder");
|
|
||||||
IServiceCollection = compilation.GetTypeByMetadataName("Microsoft.Extensions.DependencyInjection.IServiceCollection");
|
|
||||||
MvcOptions = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Mvc.MvcOptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null;
|
|
||||||
|
|
||||||
public INamedTypeSymbol IApplicationBuilder { get; }
|
|
||||||
public INamedTypeSymbol IServiceCollection { get; }
|
|
||||||
public INamedTypeSymbol MvcOptions { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue