Remove experimental analyzers

This commit is contained in:
Pranav K 2018-09-06 11:37:47 -07:00
parent 9895593926
commit 013697ad89
No known key found for this signature in database
GPG Key ID: 1963DA6D96C3057A
23 changed files with 0 additions and 1901 deletions

View File

@ -105,12 +105,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{44546170-35BF-448F-88F5-4331AE67AEAE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test", "test\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test.csproj", "{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers", "src\Microsoft.AspNetCore.Mvc.Analyzers\Microsoft.AspNetCore.Mvc.Analyzers.csproj", "{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers.Experimental", "src\Microsoft.AspNetCore.Mvc.Analyzers.Experimental\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj", "{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Analyzers.Test", "test\Mvc.Analyzers.Test\Mvc.Analyzers.Test.csproj", "{829D9A67-2D07-4CE6-86C0-59F2549B0CFA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Views.TestCommon", "test\Microsoft.AspNetCore.Mvc.Views.TestCommon\Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj", "{0772E545-A674-4165-9469-E3D79D88A4A8}"
@ -507,18 +503,6 @@ Global
{28D4DA20-6E13-47F9-80AE-D6AA7699CC35}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{28D4DA20-6E13-47F9-80AE-D6AA7699CC35}.Release|x86.ActiveCfg = Release|Any CPU
{28D4DA20-6E13-47F9-80AE-D6AA7699CC35}.Release|x86.Build.0 = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Debug|x86.Build.0 = Debug|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|Any CPU.Build.0 = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|x86.ActiveCfg = Release|Any CPU
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD}.Release|x86.Build.0 = Release|Any CPU
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -531,18 +515,6 @@ Global
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Release|x86.ActiveCfg = Release|Any CPU
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3}.Release|x86.Build.0 = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|x86.ActiveCfg = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Debug|x86.Build.0 = Debug|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|Any CPU.Build.0 = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|x86.ActiveCfg = Release|Any CPU
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754}.Release|x86.Build.0 = Release|Any CPU
{829D9A67-2D07-4CE6-86C0-59F2549B0CFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{829D9A67-2D07-4CE6-86C0-59F2549B0CFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{829D9A67-2D07-4CE6-86C0-59F2549B0CFA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -641,9 +613,7 @@ Global
{CF322BE1-E1FE-4CFD-8FCA-16A14B905D53} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{0AB46520-F441-4E01-B444-08F4D23F8B1B} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{28D4DA20-6E13-47F9-80AE-D6AA7699CC35} = {44546170-35BF-448F-88F5-4331AE67AEAE}
{2E6CDE10-8F96-4B75-B0D9-808F6A01B8BD} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{30862895-C1FA-49F5-B69A-B0F9F2ECD0F3} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{F8FD2D6A-DCD1-4A7B-B599-B728A12A1754} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{829D9A67-2D07-4CE6-86C0-59F2549B0CFA} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{0772E545-A674-4165-9469-E3D79D88A4A8} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{92D959F2-66B8-490A-BA33-DA4421EBC948} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}

30
Mvc.sln
View File

@ -162,10 +162,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.An
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Analyzers.Test", "test\Mvc.Analyzers.Test\Mvc.Analyzers.Test.csproj", "{E3E09D2F-1FCF-4396-9B09-5A62CA8CC831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers.Experimental", "src\Microsoft.AspNetCore.Mvc.Analyzers.Experimental\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj", "{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test", "test\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test.csproj", "{E83D3745-9BCF-40E8-8D34-AFBA604C2439}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorPagesClassLibrary", "test\WebSites\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj", "{17122147-ADFD-41C8-87D9-CCC582CCA8F9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Views.TestCommon", "test\Microsoft.AspNetCore.Mvc.Views.TestCommon\Microsoft.AspNetCore.Mvc.Views.TestCommon.csproj", "{51E3E785-A9D1-4196-BAFE-A17FF4304B89}"
@ -858,30 +854,6 @@ Global
{E3E09D2F-1FCF-4396-9B09-5A62CA8CC831}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E3E09D2F-1FCF-4396-9B09-5A62CA8CC831}.Release|x86.ActiveCfg = Release|Any CPU
{E3E09D2F-1FCF-4396-9B09-5A62CA8CC831}.Release|x86.Build.0 = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|x86.ActiveCfg = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Debug|x86.Build.0 = Debug|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|Any CPU.Build.0 = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|x86.ActiveCfg = Release|Any CPU
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61}.Release|x86.Build.0 = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|x86.ActiveCfg = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Debug|x86.Build.0 = Debug|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|Any CPU.Build.0 = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|x86.ActiveCfg = Release|Any CPU
{E83D3745-9BCF-40E8-8D34-AFBA604C2439}.Release|x86.Build.0 = Release|Any CPU
{17122147-ADFD-41C8-87D9-CCC582CCA8F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17122147-ADFD-41C8-87D9-CCC582CCA8F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17122147-ADFD-41C8-87D9-CCC582CCA8F9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -1031,8 +1003,6 @@ Global
{8916DDCA-EC2A-4193-B9F3-78CAA1A96D5A} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{87A3E227-C45E-4141-A59F-402908E651FD} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{E3E09D2F-1FCF-4396-9B09-5A62CA8CC831} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{CBF23034-2249-4FE5-BD48-5F3CEAC0DF61} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{E83D3745-9BCF-40E8-8D34-AFBA604C2439} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{17122147-ADFD-41C8-87D9-CCC582CCA8F9} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{51E3E785-A9D1-4196-BAFE-A17FF4304B89} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{910F023A-88E3-4CB4-8793-AC4005C7B421} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}

View File

@ -1,56 +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.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ActionsMustNotBeAsyncVoidAnalyzer : ControllerAnalyzerBase
{
public static readonly string ReturnTypeKey = "ReturnType";
public ActionsMustNotBeAsyncVoidAnalyzer()
: base(ExperimentalDiagnosticDescriptors.MVC7003_ActionsMustNotBeAsyncVoid)
{
}
protected override void InitializeWorker(ControllerAnalyzerContext analyzerContext)
{
analyzerContext.Context.RegisterSyntaxNodeAction(context =>
{
var methodSyntax = (MethodDeclarationSyntax)context.Node;
var method = context.SemanticModel.GetDeclaredSymbol(methodSyntax, context.CancellationToken);
if (!analyzerContext.IsControllerAction(method))
{
return;
}
if (!method.IsAsync || !method.ReturnsVoid)
{
return;
}
var returnType = analyzerContext.SystemThreadingTask.ToMinimalDisplayString(
context.SemanticModel,
methodSyntax.ReturnType.SpanStart);
var properties = ImmutableDictionary.Create<string, string>(StringComparer.Ordinal)
.Add(ReturnTypeKey, returnType);
var location = methodSyntax.ReturnType.GetLocation();
context.ReportDiagnostic(Diagnostic.Create(
SupportedDiagnostic,
location,
properties: properties));
}, SyntaxKind.MethodDeclaration);
}
}
}

View File

@ -1,58 +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.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editing;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public class ActionsMustNotBeAsyncVoidFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
ImmutableArray.Create(ExperimentalDiagnosticDescriptors.MVC7003_ActionsMustNotBeAsyncVoid.Id);
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
if (context.Diagnostics.Length == 0)
{
return;
}
if (!context.Diagnostics[0].Properties.TryGetValue(ActionsMustNotBeAsyncVoidAnalyzer.ReturnTypeKey, out var returnTypeName))
{
return;
}
var rootNode = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
const string title = "Fix async void usage.";
context.RegisterCodeFix(
CodeAction.Create(
title,
createChangedDocument: CreateChangedDocumentAsync,
equivalenceKey: title),
context.Diagnostics);
async Task<Document> CreateChangedDocumentAsync(CancellationToken cancellationToken)
{
var returnTypeSyntax = rootNode.FindNode(context.Span);
var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken).ConfigureAwait(false);
editor.ReplaceNode(returnTypeSyntax, SyntaxFactory.IdentifierName(returnTypeName));
return editor.GetChangedDocument();
}
}
}
}

View File

@ -1,52 +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.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ApiActionsAreAttributeRoutedAnalyzer : ApiControllerAnalyzerBase
{
internal const string MethodNameKey = "MethodName";
public ApiActionsAreAttributeRoutedAnalyzer()
: base(ExperimentalDiagnosticDescriptors.MVC7000_ApiActionsMustBeAttributeRouted)
{
}
protected override void InitializeWorker(ApiControllerAnalyzerContext analyzerContext)
{
analyzerContext.Context.RegisterSymbolAction(context =>
{
var method = (IMethodSymbol)context.Symbol;
if (!analyzerContext.IsApiAction(method))
{
return;
}
foreach (var attribute in method.GetAttributes())
{
if (attribute.AttributeClass.IsAssignableFrom(analyzerContext.RouteAttribute))
{
return;
}
}
var properties = ImmutableDictionary.Create<string, string>(StringComparer.Ordinal)
.Add(MethodNameKey, method.Name);
var location = method.Locations.Length > 0 ? method.Locations[0] : Location.None;
context.ReportDiagnostic(Diagnostic.Create(
SupportedDiagnostic,
location,
properties: properties));
}, SymbolKind.Method);
}
}
}

View File

@ -1,187 +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.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public class ApiActionsAreAttributeRoutedFixProvider : CodeFixProvider
{
private static readonly RouteAttributeInfo[] RouteAttributes = new[]
{
new RouteAttributeInfo("HttpGet", TypeNames.HttpGetAttribute, new[] { "Get", "Find" }),
new RouteAttributeInfo("HttpPost", TypeNames.HttpPostAttribute, new[] { "Post", "Create", "Update" }),
new RouteAttributeInfo("HttpDelete", TypeNames.HttpDeleteAttribute, new[] { "Delete", "Remove" }),
new RouteAttributeInfo("HttpPut", TypeNames.HttpPutAttribute, new[] { "Put", "Create", "Update" }),
};
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
ImmutableArray.Create(ExperimentalDiagnosticDescriptors.MVC7000_ApiActionsMustBeAttributeRouted.Id);
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
if (context.Diagnostics.Length == 0)
{
return;
}
var rootNode = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
Debug.Assert(context.Diagnostics.Length == 1);
var diagnostic = context.Diagnostics[0];
var methodName = diagnostic.Properties[ApiActionsAreAttributeRoutedAnalyzer.MethodNameKey];
var matchedByKeyword = false;
foreach (var routeInfo in RouteAttributes)
{
foreach (var keyword in routeInfo.KeyWords)
{
// Determine if the method starts with a conventional key and only show relevant routes.
// For e.g. FindPetByCategory would result in HttpGet attribute.
if (methodName.StartsWith(keyword, StringComparison.Ordinal))
{
matchedByKeyword = true;
var title = $"Add {routeInfo.Name} attribute";
context.RegisterCodeFix(
CodeAction.Create(
title,
createChangedDocument: cancellationToken => CreateChangedDocumentAsync(routeInfo.Type, cancellationToken),
equivalenceKey: title),
context.Diagnostics);
}
}
}
if (!matchedByKeyword)
{
foreach (var routeInfo in RouteAttributes)
{
var title = $"Add {routeInfo.Name} attribute";
context.RegisterCodeFix(
CodeAction.Create(
title,
createChangedDocument: cancellationToken => CreateChangedDocumentAsync(routeInfo.Type, cancellationToken),
equivalenceKey: title),
context.Diagnostics);
}
}
async Task<Document> CreateChangedDocumentAsync(string attributeName, CancellationToken cancellationToken)
{
var methodNode = (MethodDeclarationSyntax)rootNode.FindNode(context.Span);
var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken).ConfigureAwait(false);
var compilation = editor.SemanticModel.Compilation;
var attributeMetadata = compilation.GetTypeByMetadataName(attributeName);
var fromRouteAttribute = compilation.GetTypeByMetadataName(TypeNames.FromRouteAttribute);
attributeName = attributeMetadata.ToMinimalDisplayString(editor.SemanticModel, methodNode.SpanStart);
// Remove the Attribute suffix from type names e.g. "HttpGetAttribute" -> "HttpGet"
if (attributeName.EndsWith("Attribute", StringComparison.Ordinal))
{
attributeName = attributeName.Substring(0, attributeName.Length - "Attribute".Length);
}
var method = editor.SemanticModel.GetDeclaredSymbol(methodNode);
var attribute = SyntaxFactory.Attribute(
SyntaxFactory.ParseName(attributeName));
var route = GetRoute(fromRouteAttribute, method);
if (!string.IsNullOrEmpty(route))
{
attribute = attribute.AddArgumentListArguments(
SyntaxFactory.AttributeArgument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(route))));
}
editor.AddAttribute(methodNode, attribute);
return editor.GetChangedDocument();
}
}
private static string GetRoute(ITypeSymbol fromRouteAttribute, IMethodSymbol method)
{
StringBuilder routeNameBuilder = null;
foreach (var parameter in method.Parameters)
{
if (IsIdParameter(parameter.Name) || parameter.HasAttribute(fromRouteAttribute))
{
if (routeNameBuilder == null)
{
routeNameBuilder = new StringBuilder(parameter.Name.Length + 2);
}
else
{
routeNameBuilder.Append("/");
}
routeNameBuilder
.Append("{")
.Append(parameter.Name)
.Append("}");
}
}
return routeNameBuilder?.ToString();
}
private static bool IsIdParameter(string name)
{
// Check if the parameter is named "id" (e.g. int id) or ends in Id (e.g. personId)
if (name == null || name.Length < 2)
{
return false;
}
if (string.Equals("id", name, StringComparison.Ordinal))
{
return true;
}
if (name.Length > 3 && name.EndsWith("Id", StringComparison.Ordinal) && char.IsLower(name[name.Length - 3]))
{
return true;
}
return false;
}
private readonly struct RouteAttributeInfo
{
public RouteAttributeInfo(string name, string type, string[] keywords)
{
Name = name;
Type = type;
KeyWords = keywords;
}
public string Name { get; }
public string Type { get; }
public string[] KeyWords { get; }
}
}
}

View File

@ -1,102 +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.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ApiActionsShouldUseActionResultOfTAnalyzer : ApiControllerAnalyzerBase
{
public static readonly string ReturnTypeKey = "ReturnType";
public ApiActionsShouldUseActionResultOfTAnalyzer()
: base(ExperimentalDiagnosticDescriptors.MVC7002_ApiActionsShouldReturnActionResultOf)
{
}
protected override void InitializeWorker(ApiControllerAnalyzerContext analyzerContext)
{
analyzerContext.Context.RegisterSyntaxNodeAction(context =>
{
var methodSyntax = (MethodDeclarationSyntax)context.Node;
if (methodSyntax.Body == null)
{
// Ignore expression bodied methods.
}
var method = context.SemanticModel.GetDeclaredSymbol(methodSyntax, context.CancellationToken);
if (!analyzerContext.IsApiAction(method))
{
return;
}
if (method.ReturnsVoid || method.ReturnType.Kind != SymbolKind.NamedType)
{
return;
}
var declaredReturnType = method.ReturnType;
var namedReturnType = (INamedTypeSymbol)method.ReturnType;
var isTaskOActionResult = false;
if (namedReturnType.ConstructedFrom?.IsAssignableFrom(analyzerContext.SystemThreadingTaskOfT) ?? false)
{
// Unwrap Task<T>.
isTaskOActionResult = true;
declaredReturnType = namedReturnType.TypeArguments[0];
}
if (!declaredReturnType.IsAssignableFrom(analyzerContext.IActionResult))
{
// Method signature does not look like IActionResult MyAction or SomeAwaitable<IActionResult>.
// Nothing to do here.
return;
}
// Method returns an IActionResult. Determine if the method block returns an ObjectResult
foreach (var returnStatement in methodSyntax.DescendantNodes().OfType<ReturnStatementSyntax>())
{
var returnType = context.SemanticModel.GetTypeInfo(returnStatement.Expression, context.CancellationToken);
if (returnType.Type == null || returnType.Type.Kind == SymbolKind.ErrorType)
{
continue;
}
ImmutableDictionary<string, string> properties = null;
if (returnType.Type.IsAssignableFrom(analyzerContext.ObjectResult))
{
// Check if the method signature looks like "return Ok(userModelInstance)". If so, we can infer the type of userModelInstance
if (returnStatement.Expression is InvocationExpressionSyntax invocation &&
invocation.ArgumentList.Arguments.Count == 1)
{
var typeInfo = context.SemanticModel.GetTypeInfo(invocation.ArgumentList.Arguments[0].Expression);
var desiredReturnType = analyzerContext.ActionResultOfT.Construct(typeInfo.Type);
if (isTaskOActionResult)
{
desiredReturnType = analyzerContext.SystemThreadingTaskOfT.Construct(desiredReturnType);
}
var desiredReturnTypeString = desiredReturnType.ToMinimalDisplayString(
context.SemanticModel,
methodSyntax.ReturnType.SpanStart);
properties = ImmutableDictionary.Create<string, string>(StringComparer.Ordinal)
.Add(ReturnTypeKey, desiredReturnTypeString);
}
context.ReportDiagnostic(Diagnostic.Create(
SupportedDiagnostic,
methodSyntax.ReturnType.GetLocation(),
properties: properties));
}
}
}, SyntaxKind.MethodDeclaration);
}
}
}

View File

@ -1,55 +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.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editing;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public class ApiActionsShouldUseActionResultOfTCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
ImmutableArray.Create(ExperimentalDiagnosticDescriptors.MVC7002_ApiActionsShouldReturnActionResultOf.Id);
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var rootNode = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
foreach (var diagnostic in context.Diagnostics)
{
if (diagnostic.Properties.TryGetValue("ReturnType", out var returnTypeName))
{
var title = $"Make return type {returnTypeName}";
context.RegisterCodeFix(
CodeAction.Create(
title,
createChangedDocument: cancellationToken => CreateChangedDocumentAsync(returnTypeName, cancellationToken),
equivalenceKey: title),
context.Diagnostics);
}
}
async Task<Document> CreateChangedDocumentAsync(string returnTypeName, CancellationToken cancellationToken)
{
var returnType = rootNode.FindNode(context.Span);
var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken).ConfigureAwait(false);
editor.ReplaceNode(returnType, SyntaxFactory.IdentifierName(returnTypeName));
return editor.GetChangedDocument();
}
}
}
}

View File

@ -1,39 +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.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public abstract class ApiControllerAnalyzerBase : DiagnosticAnalyzer
{
public ApiControllerAnalyzerBase(DiagnosticDescriptor diagnostic)
{
SupportedDiagnostics = ImmutableArray.Create(diagnostic);
}
protected DiagnosticDescriptor SupportedDiagnostic => SupportedDiagnostics[0];
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
public sealed override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(compilationContext =>
{
var analyzerContext = new ApiControllerAnalyzerContext(compilationContext);
// Only do work if ApiControllerAttribute is defined.
if (analyzerContext.ApiControllerAttribute == null)
{
return;
}
InitializeWorker(analyzerContext);
});
}
protected abstract void InitializeWorker(ApiControllerAnalyzerContext analyzerContext);
}
}

View File

@ -1,64 +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.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ApiControllerAnalyzerContext
{
#pragma warning disable RS1012 // Start action has no registered actions.
public ApiControllerAnalyzerContext(CompilationStartAnalysisContext context)
#pragma warning restore RS1012 // Start action has no registered actions.
{
Context = context;
ApiControllerAttribute = context.Compilation.GetTypeByMetadataName(TypeNames.ApiControllerAttribute);
}
public CompilationStartAnalysisContext Context { get; }
public INamedTypeSymbol ApiControllerAttribute { get; }
private INamedTypeSymbol _routeAttribute;
public INamedTypeSymbol RouteAttribute => GetType(TypeNames.IRouteTemplateProvider, ref _routeAttribute);
private INamedTypeSymbol _actionResultOfT;
public INamedTypeSymbol ActionResultOfT => GetType(TypeNames.ActionResultOfT, ref _actionResultOfT);
private INamedTypeSymbol _systemThreadingTask;
public INamedTypeSymbol SystemThreadingTask => GetType(TypeNames.Task, ref _systemThreadingTask);
private INamedTypeSymbol _systemThreadingTaskOfT;
public INamedTypeSymbol SystemThreadingTaskOfT => GetType(TypeNames.TaskOfT, ref _systemThreadingTaskOfT);
private INamedTypeSymbol _objectResult;
public INamedTypeSymbol ObjectResult => GetType(TypeNames.ObjectResult, ref _objectResult);
private INamedTypeSymbol _iActionResult;
public INamedTypeSymbol IActionResult => GetType(TypeNames.IActionResult, ref _iActionResult);
public INamedTypeSymbol _modelState;
public INamedTypeSymbol ModelStateDictionary => GetType(TypeNames.ModelStateDictionary, ref _modelState);
public INamedTypeSymbol _nonActionAttribute;
public INamedTypeSymbol NonActionAttribute => GetType(TypeNames.NonActionAttribute, ref _nonActionAttribute);
private INamedTypeSymbol GetType(string name, ref INamedTypeSymbol cache) =>
cache = cache ?? Context.Compilation.GetTypeByMetadataName(name);
public bool IsApiAction(IMethodSymbol method)
{
return
method.ContainingType.HasAttribute(ApiControllerAttribute, inherit: true) &&
method.DeclaredAccessibility == Accessibility.Public &&
method.MethodKind == MethodKind.Ordinary &&
!method.IsGenericMethod &&
!method.IsAbstract &&
!method.IsStatic &&
!method.HasAttribute(NonActionAttribute);
}
}
}

View File

@ -1,78 +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.Diagnostics;
using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
internal static class CodeAnalysisExtensions
{
public static bool HasAttribute(this ITypeSymbol typeSymbol, ITypeSymbol attribute, bool inherit)
{
while (typeSymbol != null)
{
if (typeSymbol.HasAttribute(attribute))
{
return true;
}
typeSymbol = typeSymbol.BaseType;
}
return false;
}
public static bool HasAttribute(this ISymbol symbol, ITypeSymbol attribute)
{
Debug.Assert(symbol != null);
Debug.Assert(attribute != null);
foreach (var declaredAttribute in symbol.GetAttributes())
{
if (declaredAttribute.AttributeClass == attribute)
{
return true;
}
}
return false;
}
public static bool IsAssignableFrom(this ITypeSymbol source, INamedTypeSymbol target)
{
Debug.Assert(source != null);
Debug.Assert(target != null);
if (source == target)
{
return true;
}
if (target.TypeKind == TypeKind.Interface)
{
foreach (var @interface in source.AllInterfaces)
{
if (@interface == target)
{
return true;
}
}
return false;
}
do
{
if (source == target)
{
return true;
}
source = source.BaseType;
} while (source != null);
return false;
}
}
}

View File

@ -1,39 +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.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public abstract class ControllerAnalyzerBase : DiagnosticAnalyzer
{
public ControllerAnalyzerBase(DiagnosticDescriptor diagnostic)
{
SupportedDiagnostics = ImmutableArray.Create(diagnostic);
}
protected DiagnosticDescriptor SupportedDiagnostic => SupportedDiagnostics[0];
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
public sealed override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(compilationContext =>
{
var analyzerContext = new ControllerAnalyzerContext(compilationContext);
// Only do work if ControllerAttribute is defined.
if (analyzerContext.ControllerAttribute == null)
{
return;
}
InitializeWorker(analyzerContext);
});
}
protected abstract void InitializeWorker(ControllerAnalyzerContext analyzerContext);
}
}

View File

@ -1,47 +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;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ControllerAnalyzerContext
{
#pragma warning disable RS1012 // Start action has no registered actions.
public ControllerAnalyzerContext(CompilationStartAnalysisContext context)
#pragma warning restore RS1012 // Start action has no registered actions.
{
Context = context;
ControllerAttribute = Context.Compilation.GetTypeByMetadataName(TypeNames.ControllerAttribute);
}
public CompilationStartAnalysisContext Context { get; }
public INamedTypeSymbol ControllerAttribute { get; }
private INamedTypeSymbol _systemThreadingTask;
public INamedTypeSymbol SystemThreadingTask => GetType(TypeNames.Task, ref _systemThreadingTask);
private INamedTypeSymbol _systemThreadingTaskOfT;
public INamedTypeSymbol SystemThreadingTaskOfT => GetType(TypeNames.TaskOfT, ref _systemThreadingTaskOfT);
public INamedTypeSymbol _nonActionAttribute;
public INamedTypeSymbol NonActionAttribute => GetType(TypeNames.NonActionAttribute, ref _nonActionAttribute);
private INamedTypeSymbol GetType(string name, ref INamedTypeSymbol cache) =>
cache = cache ?? Context.Compilation.GetTypeByMetadataName(name);
public bool IsControllerAction(IMethodSymbol method)
{
return
method.ContainingType.HasAttribute(ControllerAttribute, inherit: true) &&
method.DeclaredAccessibility == Accessibility.Public &&
method.MethodKind == MethodKind.Ordinary &&
!method.IsGenericMethod &&
!method.IsAbstract &&
!method.IsStatic &&
!method.HasAttribute(NonActionAttribute);
}
}
}

View File

@ -1,46 +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.Mvc.Analyzers
{
public static class ExperimentalDiagnosticDescriptors
{
public static readonly DiagnosticDescriptor MVC7000_ApiActionsMustBeAttributeRouted =
new DiagnosticDescriptor(
"MVC7000",
"Actions on types annotated with ApiControllerAttribute must be attribute routed.",
"Actions on types annotated with ApiControllerAttribute must be attribute routed.",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public static readonly DiagnosticDescriptor MVC7001_ApiActionsHaveBadModelStateFilter =
new DiagnosticDescriptor(
"MVC7001",
"Actions on types annotated with ApiControllerAttribute do not require explicit ModelState validity check.",
"Actions on types annotated with ApiControllerAttribute do not require explicit ModelState validity check.",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public static readonly DiagnosticDescriptor MVC7002_ApiActionsShouldReturnActionResultOf =
new DiagnosticDescriptor(
"MVC7002",
"Actions on types annotated with ApiControllerAttribute should return ActionResult<T>.",
"Actions on types annotated with ApiControllerAttribute should return ActionResult<T>.",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public static readonly DiagnosticDescriptor MVC7003_ActionsMustNotBeAsyncVoid =
new DiagnosticDescriptor(
"MVC7003",
"Controller actions must not have async void signature.",
"Controller actions must not have async void signature.",
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
}
}

View File

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>CSharp Analyzers for ASP.NET Core MVC.</Description>
<PackageTags>aspnetcore;aspnetcoremvc</PackageTags>
<VerifyVersion>false</VerifyVersion>
<VersionPrefix>$(ExperimentalVersionPrefix)</VersionPrefix>
<VersionSuffix>$(ExperimentalVersionSuffix)</VersionSuffix>
<PackageVersion>$(ExperimentalPackageVersion)</PackageVersion>
<TargetFramework>netstandard1.3</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<EnableApiCheck>false</EnableApiCheck>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion)" />
</ItemGroup>
<ItemGroup>
<SignedPackageFile Include="analyzers/dotnet/cs/$(TargetFileName)" Certificate="$(AssemblySigningCertName)" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)$(AssemblyName).dll" Pack="true" PackagePath="analyzers\dotnet\cs" />
</ItemGroup>
</Project>

View File

@ -1,38 +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.Mvc.Analyzers
{
internal static class TypeNames
{
public const string ControllerAttribute = "Microsoft.AspNetCore.Mvc.ControllerAttribute";
public const string ApiControllerAttribute = "Microsoft.AspNetCore.Mvc.ApiControllerAttribute";
public const string NonActionAttribute = "Microsoft.AspNetCore.Mvc.NonActionAttribute";
public const string IRouteTemplateProvider = "Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider";
public const string ActionResultOfT = "Microsoft.AspNetCore.Mvc.ActionResult`1";
public const string Task = "System.Threading.Tasks.Task";
public const string TaskOfT = "System.Threading.Tasks.Task`1";
public const string ObjectResult = "Microsoft.AspNetCore.Mvc.ObjectResult";
public const string IActionResult = "Microsoft.AspNetCore.Mvc.IActionResult";
public const string ModelStateDictionary = "Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary";
public const string HttpGetAttribute = "Microsoft.AspNetCore.Mvc.HttpGetAttribute";
public const string HttpPostAttribute = "Microsoft.AspNetCore.Mvc.HttpPostAttribute";
public const string HttpPutAttribute = "Microsoft.AspNetCore.Mvc.HttpPutAttribute";
public const string HttpDeleteAttribute = "Microsoft.AspNetCore.Mvc.HttpDeleteAttribute";
public const string FromRouteAttribute = "Microsoft.AspNetCore.Mvc.FromRouteAttribute";
}
}

View File

@ -1,173 +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.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ActionsMustNotBeAsyncVoidFacts : AnalyzerTestBase
{
private static DiagnosticDescriptor DiagnosticDescriptor = ExperimentalDiagnosticDescriptors.MVC7003_ActionsMustNotBeAsyncVoid;
protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; }
= new ActionsMustNotBeAsyncVoidAnalyzer();
protected override CodeFixProvider CodeFixProvider { get; }
= new ActionsMustNotBeAsyncVoidFixProvider();
[Fact]
public async Task NoDiagnosticsAreReturned_FoEmptyScenarios()
{
// Arrange
var test = @"";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenMethodIsNotAControllerAction()
{
// Arrange
var test =
@"
using System.Threading.Tasks;
public class UserViewModel
{
public async void Index() => await Task.Delay(10);
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task DiagnosticsAreReturned_WhenMethodIsAControllerAction()
{
// Arrange
var location = new DiagnosticLocation("Test.cs", 7, 18);
var test =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class HomeController : Controller
{
public async void Index()
{
await Response.Body.FlushAsync();
}
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class HomeController : Controller
{
public async Task Index()
{
await Response.Body.FlushAsync();
}
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(location, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task DiagnosticsAreReturned_WhenActionMethodIsExpressionBodied()
{
// Arrange
var location = new DiagnosticLocation("Test.cs", 7, 18);
var test =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class HomeController : Controller
{
public async void Index() => await Response.Body.FlushAsync();
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class HomeController : Controller
{
public async Task Index() => await Response.Body.FlushAsync();
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(location, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task CodeFix_ProducesFullyQualifiedNamespaces()
{
// Arrange
var location = new DiagnosticLocation("Test.cs", 6, 18);
var test =
@"
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public async void Index() => await Response.Body.FlushAsync();
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public async System.Threading.Tasks.Task Index() => await Response.Body.FlushAsync();
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(location, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
private void AssertDiagnostic(DiagnosticLocation expectedLocation, Diagnostic[] actualDiagnostics)
{
// Assert
Assert.Collection(
actualDiagnostics,
diagnostic =>
{
Assert.Equal(DiagnosticDescriptor.Id, diagnostic.Id);
Assert.Same(DiagnosticDescriptor, diagnostic.Descriptor);
AnalyzerAssert.DiagnosticLocation(expectedLocation, diagnostic.Location);
});
}
}
}

View File

@ -1,304 +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.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ApiActionsAreAttributeRoutedFacts : AnalyzerTestBase
{
private static DiagnosticDescriptor DiagnosticDescriptor = ExperimentalDiagnosticDescriptors.MVC7000_ApiActionsMustBeAttributeRouted;
protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; }
= new ApiActionsAreAttributeRoutedAnalyzer();
protected override CodeFixProvider CodeFixProvider { get; }
= new ApiActionsAreAttributeRoutedFixProvider();
[Fact]
public async Task NoDiagnosticsAreReturned_FoEmptyScenarios()
{
// Arrange
var test = @"";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenTypeIsNotApiController()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public IActionResult Index() => View();
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenApiControllerActionHasAttribute()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class PetController : Controller
{
[HttpGet]
public int GetPetId() => 0;
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_ForConstructors()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class PetController : Controller
{
public PetController(){ }
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_ForNonActions()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class PetController : Controller
{
private int GetPetIdPrivate() => 0;
protected int GetPetIdProtected() => 0;
public static IActionResult FindPetByStatus(int status) => null;
[NonAction]
public object Reset(int state) => null;
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task DiagnosticsAndCodeFixes_WhenApiControllerActionDoesNotHaveAttribute()
{
// Arrange
var expectedLocation = new DiagnosticLocation("Test.cs", 8, 16);
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController : Controller
{
public int GetPetId() => 0;
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController : Controller
{
[HttpGet]
public int GetPetId() => 0;
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(expectedLocation, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task CodeFixes_ApplyFullyQualifiedNames()
{
// Arrange
var test =
@"
[Microsoft.AspNetCore.Mvc.ApiController]
[Microsoft.AspNetCore.Mvc.Route]
public class PetController
{
public object GetPet() => null;
}";
var expectedFix =
@"
[Microsoft.AspNetCore.Mvc.ApiController]
[Microsoft.AspNetCore.Mvc.Route]
public class PetController
{
[Microsoft.AspNetCore.Mvc.HttpGet]
public object GetPet() => null;
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Theory]
[InlineData("id")]
[InlineData("petId")]
public async Task CodeFixes_WithIdParameter(string idParameter)
{
// Arrange
var test =
$@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{{
public IActionResult Post(string notid, int {idParameter}) => null;
}}";
var expectedFix =
$@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{{
[HttpPost(""{{{idParameter}}}"")]
public IActionResult Post(string notid, int {idParameter}) => null;
}}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task CodeFixes_WithRouteParameter()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{
public IActionResult DeletePetByStatus([FromRoute] Status status, [FromRoute] Category category) => null;
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{
[HttpDelete(""{status}/{category}"")]
public IActionResult DeletePetByStatus([FromRoute] Status status, [FromRoute] Category category) => null;
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task CodeFixes_WhenAttributeCannotBeInferred()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{
public IActionResult ModifyPet() => null;
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route]
public class PetController
{
[HttpPut]
public IActionResult ModifyPet() => null;
}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
// There isn't a good way to test all fixes simultaneously. We'll pick the last one to verify when we
// expect to have 4 fixes.
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics, codeFixIndex: 3);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
private void AssertDiagnostic(DiagnosticLocation expectedLocation, Diagnostic[] actualDiagnostics)
{
// Assert
Assert.Collection(
actualDiagnostics,
diagnostic =>
{
Assert.Equal(DiagnosticDescriptor.Id, diagnostic.Id);
Assert.Same(DiagnosticDescriptor, diagnostic.Descriptor);
AnalyzerAssert.DiagnosticLocation(expectedLocation, diagnostic.Location);
});
}
}
}

View File

@ -1,261 +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.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ApiActionsShouldUseActionResultOfTFacts : AnalyzerTestBase
{
private static DiagnosticDescriptor DiagnosticDescriptor = ExperimentalDiagnosticDescriptors.MVC7002_ApiActionsShouldReturnActionResultOf;
protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; }
= new ApiActionsShouldUseActionResultOfTAnalyzer();
protected override CodeFixProvider CodeFixProvider { get; }
= new ApiActionsShouldUseActionResultOfTCodeFixProvider();
[Fact]
public async Task NoDiagnosticsAreReturned_FoEmptyScenarios()
{
// Arrange
var test = @"";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenTypeIsNotApiController()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
public class HomeController: ControllerBase
{
public IActionResult Index() => View();
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_ForNonActions()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class PetController: ControllerBaseBase
{
private int GetPetIdPrivate() => 0;
protected int GetPetIdProtected() => 0;
public static IActionResult FindPetByStatus(int status) => null;
[NonAction]
public object Reset(int state) => null;
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenActionAreExpressionBodiedMembers()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class PetController: ControllerBase
{
public IActionResult GetPetId() => ModelState.IsValid ? OK(new object()) : BadResult();
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Theory]
[InlineData("Pet")]
[InlineData("List<Pet>")]
[InlineData("System.Threading.Task<Pet>")]
public async Task NoDiagnosticsAreReturned_WhenTypeReturnsNonObjectResult(string returnType)
{
// Arrange
var test =
$@"
using Microsoft.AspNetCore.Mvc;
public class Pet {{ }}
[ApiController]
public class PetController: ControllerBase
{{
public {returnType} GetPetId() => null;
}}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task NoDiagnosticsAreReturned_WhenTypeReturnsActionResultOfT()
{
// Arrange
var test =
@"
using Microsoft.AspNetCore.Mvc;
public class Pet { }
[ApiController]
public class PetController: ControllerBase
{
public ActionResult<Pet> GetPetId() => null;
}";
var project = CreateProject(test);
// Act
var result = await GetDiagnosticAsync(project);
// Assert
Assert.Empty(result);
}
[Fact]
public async Task DiagnosticsAreReturned_WhenActionsReturnIActionResult()
{
// Arrange
var expectedLocation = new DiagnosticLocation("Test.cs", 9, 12);
var test =
@"
using Microsoft.AspNetCore.Mvc;
public class Pet {}
[ApiController]
public class PetController: ControllerBase
{
public IActionResult GetPet()
{
return Ok(new Pet());
}
}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
public class Pet {}
[ApiController]
public class PetController: ControllerBase
{
public ActionResult<Pet> GetPet()
{
return Ok(new Pet());
}
}";
var project = CreateProject(test);
// Act
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(expectedLocation, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
[Fact]
public async Task DiagnosticsAreReturned_WhenActionReturnsAsyncIActionResult()
{
// Arrange
var expectedLocation = new DiagnosticLocation("Test.cs", 8, 18);
var test =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[ApiController]
public class PetController: ControllerBase
{
public async Task<IActionResult> GetPet()
{
await Task.Delay(0);
return Ok(new Pet());
}
}
public class Pet {}";
var expectedFix =
@"
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[ApiController]
public class PetController: ControllerBase
{
public async Task<ActionResult<Pet>> GetPet()
{
await Task.Delay(0);
return Ok(new Pet());
}
}
public class Pet {}";
var project = CreateProject(test);
// Act & Assert
var actualDiagnostics = await GetDiagnosticAsync(project);
AssertDiagnostic(expectedLocation, actualDiagnostics);
var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics);
Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true);
}
private void AssertDiagnostic(DiagnosticLocation expectedLocation, Diagnostic[] actualDiagnostics)
{
// Assert
Assert.Collection(
actualDiagnostics,
diagnostic =>
{
Assert.Equal(DiagnosticDescriptor.Id, diagnostic.Id);
Assert.Same(DiagnosticDescriptor, diagnostic.Descriptor);
AnalyzerAssert.DiagnosticLocation(expectedLocation, diagnostic.Location);
});
}
}
}

View File

@ -1,182 +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.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Testing;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.DependencyModel;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure
{
public abstract class AnalyzerTestBase : IDisposable
{
private static readonly object WorkspaceLock = new object();
public Workspace Workspace { get; private set; }
protected abstract DiagnosticAnalyzer DiagnosticAnalyzer { get; }
protected virtual CodeFixProvider CodeFixProvider { get; }
public IDictionary<string, DiagnosticLocation> MarkerLocations { get; } = new Dictionary<string, DiagnosticLocation>();
public DiagnosticLocation DefaultMarkerLocation { get; private set; }
protected Project CreateProjectFromFile([CallerMemberName] string fileName = "")
{
var solutionDirectory = TestPathUtilities.GetSolutionRootDirectory("Mvc");
var projectDirectory = Path.Combine(solutionDirectory, "test", GetType().Assembly.GetName().Name);
var filePath = Path.Combine(projectDirectory, "TestFiles", fileName + ".cs");
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"TestFile {fileName} could not be found at {filePath}.", filePath);
}
const string MarkerStart = "/*MM";
const string MarkerEnd = "*/";
var lines = File.ReadAllLines(filePath);
for (var i = 0; i < lines.Length; i++)
{
var line = lines[i];
var markerStartIndex = line.IndexOf(MarkerStart, StringComparison.Ordinal);
if (markerStartIndex != -1)
{
var markerEndIndex = line.IndexOf(MarkerEnd, markerStartIndex, StringComparison.Ordinal);
var markerName = line.Substring(markerStartIndex + 2, markerEndIndex - markerStartIndex - 2);
var resultLocation = new DiagnosticLocation(i + 1, markerStartIndex + 1); ;
if (DefaultMarkerLocation == null)
{
DefaultMarkerLocation = resultLocation;
}
MarkerLocations[markerName] = resultLocation;
line = line.Substring(0, markerStartIndex) + line.Substring(markerEndIndex + MarkerEnd.Length);
}
lines[i] = line;
}
var inputSource = string.Join(Environment.NewLine, lines);
return CreateProject(inputSource);
}
protected Project CreateProject(string source)
{
var projectId = ProjectId.CreateNewId(debugName: "TestProject");
var newFileName = "Test.cs";
var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
var metadataReferences = DependencyContext.Load(GetType().Assembly)
.CompileLibraries
.SelectMany(c => c.ResolveReferencePaths())
.Select(path => MetadataReference.CreateFromFile(path))
.Cast<MetadataReference>()
.ToList();
lock (WorkspaceLock)
{
if (Workspace == null)
{
Workspace = new AdhocWorkspace();
}
}
var solution = Workspace
.CurrentSolution
.AddProject(projectId, "TestProject", "TestProject", LanguageNames.CSharp)
.AddMetadataReferences(projectId, metadataReferences)
.AddDocument(documentId, newFileName, SourceText.From(source));
return solution.GetProject(projectId);
}
protected async Task<Diagnostic[]> GetDiagnosticAsync(Project project)
{
var compilation = await project.GetCompilationAsync();
var compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create(DiagnosticAnalyzer));
var diagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync();
return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
}
protected Task<string> ApplyCodeFixAsync(
Project project,
Diagnostic[] analyzerDiagnostic,
int codeFixIndex = 0)
{
var diagnostic = analyzerDiagnostic.Single();
return ApplyCodeFixAsync(project, diagnostic, codeFixIndex);
}
protected async Task<string> ApplyCodeFixAsync(
Project project,
Diagnostic analyzerDiagnostic,
int codeFixIndex = 0)
{
if (CodeFixProvider == null)
{
throw new InvalidOperationException($"{nameof(CodeFixProvider)} has not been assigned.");
}
var document = project.Documents.Single();
var actions = new List<CodeAction>();
var context = new CodeFixContext(document, analyzerDiagnostic, (a, d) => actions.Add(a), CancellationToken.None);
await CodeFixProvider.RegisterCodeFixesAsync(context);
if (actions.Count == 0)
{
throw new InvalidOperationException("CodeFix produced no actions to apply.");
}
var updatedSolution = await ApplyFixAsync(actions[codeFixIndex]);
// Todo: figure out why this doesn't work.
// var updatedProject = updatedSolution.GetProject(project.Id);
// await EnsureCompilable(updatedProject);
var updatedDocument = updatedSolution.GetDocument(document.Id);
var sourceText = await updatedDocument.GetTextAsync();
return sourceText.ToString();
}
private static async Task EnsureCompilable(Project project)
{
var compilation = await project
.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.GetCompilationAsync();
var diagnostics = compilation.GetDiagnostics();
if (diagnostics.Length != 0)
{
var message = string.Join(
Environment.NewLine,
diagnostics.Select(d => CSharpDiagnosticFormatter.Instance.Format(d)));
throw new InvalidOperationException($"Compilation failed:{Environment.NewLine}{message}");
}
}
private static async Task<Solution> ApplyFixAsync(CodeAction codeAction)
{
var operations = await codeAction.GetOperationsAsync(CancellationToken.None);
return Assert.Single(operations.OfType<ApplyChangesOperation>()).ChangedSolution;
}
public void Dispose()
{
Workspace?.Dispose();
}
}
}

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
<ItemGroup>
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Analyzers.Experimental\Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Analyzer.Testing" Version="$(MicrosoftAspNetCoreAnalyzerTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,3 +0,0 @@
{
"shadowCopy": false
}

View File

@ -9,13 +9,5 @@
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(FeatureBranchVersionSuffix)' != ''">$(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))</VersionSuffix>
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
<ExperimentalVersionPrefix>0.2.0</ExperimentalVersionPrefix>
<ExperimentalVersionSuffix>preview3</ExperimentalVersionSuffix>
<ExperimentalPackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(ExperimentalVersionSuffix)' == 'rtm' ">$(ExperimentalVersionPrefix)</ExperimentalPackageVersion>
<ExperimentalPackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(ExperimentalVersionSuffix)' != 'rtm' ">$(ExperimentalVersionPrefix)-$(ExperimentalVersionSuffix)-final</ExperimentalPackageVersion>
<ExperimentalVersionSuffix Condition="'$(ExperimentalVersionSuffix)' != '' And '$(FeatureBranchVersionSuffix)' != ''">$(FeatureBranchVersionPrefix)$(ExperimentalVersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))</ExperimentalVersionSuffix>
<ExperimentalVersionSuffix Condition="'$(ExperimentalVersionSuffix)' != '' And '$(BuildNumber)' != ''">$(ExperimentalVersionSuffix)-$(BuildNumber)</ExperimentalVersionSuffix>
</PropertyGroup>
</Project>