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:
Ryan Nowak 2019-07-17 13:20:57 -07:00
parent b84132638f
commit 010ffe6121
35 changed files with 61 additions and 144 deletions

View File

@ -15,6 +15,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<Nullable>Enable</Nullable>
</PropertyGroup>
<ItemGroup>

View File

@ -2,7 +2,6 @@
// 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
@ -14,8 +13,8 @@ namespace Microsoft.AspNetCore.Analyzers
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))
if (string.Equals(item.OptionsType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), SymbolNames.MvcOptions.MetadataName) &&
string.Equals(item.Property.Name, SymbolNames.MvcOptions.EnableEndpointRoutingPropertyName, StringComparison.Ordinal))
{
return item.ConstantValue as bool? == false;
}

View File

@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Analyzers
{
IApplicationBuilder = compilation.GetTypeByMetadataName(SymbolNames.IApplicationBuilder.MetadataName);
IServiceCollection = compilation.GetTypeByMetadataName(SymbolNames.IServiceCollection.MetadataName);
MvcOptions = compilation.GetTypeByMetadataName(SymbolNames.MvcOptions.MetadataName);
}
public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null;
@ -18,5 +19,7 @@ namespace Microsoft.AspNetCore.Analyzers
public INamedTypeSymbol IApplicationBuilder { get; }
public INamedTypeSymbol IServiceCollection { get; }
public INamedTypeSymbol MvcOptions { get; }
}
}

View File

@ -41,5 +41,12 @@ namespace Microsoft.AspNetCore.Analyzers
public const string UseSignalRMethodName = "UseSignalR";
}
public static class MvcOptions
{
public const string MetadataName = "Microsoft.AspNetCore.Mvc.MvcOptions";
public const string EnableEndpointRoutingPropertyName = "EnableEndpointRouting";
}
}
}

View File

@ -17,6 +17,11 @@ namespace Microsoft.AspNetCore.Analyzers
public TestSource Read(string source)
{
if (!source.EndsWith(".cs"))
{
source = source + ".cs";
}
var filePath = Path.Combine(ProjectDirectory, "TestFiles", GetType().Name, source);
if (!File.Exists(filePath))
{

View File

@ -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>());
}
}
}

View File

@ -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.
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Xunit;
namespace Microsoft.AspNetCore.Analyzers
{
public class StartupAnalyzerTest
public class StartupAnalyzerTest : AnalyzerTestBase
{
public StartupAnalyzerTest()
{
StartupAnalyzer = new StartupAnalzyer();
Runner = new MvcDiagnosticAnalyzerRunner(StartupAnalyzer);
Runner = new AnalyzersDiagnosticAnalyzerRunner(StartupAnalyzer);
Analyses = new ConcurrentBag<object>();
ConfigureServicesMethods = new ConcurrentBag<IMethodSymbol>();
@ -32,7 +30,7 @@ namespace Microsoft.AspNetCore.Analyzers
private StartupAnalzyer StartupAnalyzer { get; }
private MvcDiagnosticAnalyzerRunner Runner { get; }
private AnalyzersDiagnosticAnalyzerRunner Runner { get; }
private ConcurrentBag<object> Analyses { get; }
@ -44,7 +42,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_Standard()
{
// Arrange
var source = ReadSource("StartupSignatures_Standard");
var source = Read("StartupSignatures_Standard");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -60,7 +58,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_MoreVariety()
{
// Arrange
var source = ReadSource("StartupSignatures_MoreVariety");
var source = Read("StartupSignatures_MoreVariety");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -82,7 +80,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_MvcOptionsAnalysis_UseMvc_FindsEndpointRoutingDisabled()
{
// Arrange
var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled");
var source = Read("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -102,7 +100,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_MvcOptionsAnalysis_AddMvcOptions_FindsEndpointRoutingDisabled()
{
// Arrange
var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled");
var source = Read("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled");
// Act
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)
{
// Arrange
var source = ReadSource(sourceFileName);
var source = Read(sourceFileName);
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -151,7 +149,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleMiddleware()
{
// Arrange
var source = ReadSource("MvcOptions_UseMvcWithOtherMiddleware");
var source = Read("MvcOptions_UseMvcWithOtherMiddleware");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -183,7 +181,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleUseMvc()
{
// Arrange
var source = ReadSource("MvcOptions_UseMvcMultiple");
var source = Read("MvcOptions_UseMvcMultiple");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -215,7 +213,7 @@ namespace Microsoft.AspNetCore.Analyzers
public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider()
{
// Arrange
var source = ReadSource("ConfigureServices_BuildServiceProvider");
var source = Read("ConfigureServices_BuildServiceProvider");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
@ -230,10 +228,5 @@ namespace Microsoft.AspNetCore.Analyzers
AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location);
});
}
private TestSource ReadSource(string fileName)
{
return MvcTestSource.Read(nameof(StartupAnalyzerTest), fileName);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}