diff --git a/Mvc.NoFun.sln b/Mvc.NoFun.sln index d8e44d40bf..3995a5463a 100644 --- a/Mvc.NoFun.sln +++ b/Mvc.NoFun.sln @@ -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} diff --git a/Mvc.sln b/Mvc.sln index 2bcf1d1580..775683a5fa 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -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} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidAnalyzer.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidAnalyzer.cs deleted file mode 100644 index e3a489a960..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidAnalyzer.cs +++ /dev/null @@ -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(StringComparer.Ordinal) - .Add(ReturnTypeKey, returnType); - - var location = methodSyntax.ReturnType.GetLocation(); - context.ReportDiagnostic(Diagnostic.Create( - SupportedDiagnostic, - location, - properties: properties)); - - }, SyntaxKind.MethodDeclaration); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidFixProvider.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidFixProvider.cs deleted file mode 100644 index 8adf0c0288..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ActionsMustNotBeAsyncVoidFixProvider.cs +++ /dev/null @@ -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 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 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(); - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedAnalyzer.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedAnalyzer.cs deleted file mode 100644 index 32b7a4e3ac..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedAnalyzer.cs +++ /dev/null @@ -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(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); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedFixProvider.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedFixProvider.cs deleted file mode 100644 index f89827b8f0..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsAreAttributeRoutedFixProvider.cs +++ /dev/null @@ -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 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 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; } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTAnalyzer.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTAnalyzer.cs deleted file mode 100644 index 9125d40692..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTAnalyzer.cs +++ /dev/null @@ -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. - isTaskOActionResult = true; - declaredReturnType = namedReturnType.TypeArguments[0]; - } - - if (!declaredReturnType.IsAssignableFrom(analyzerContext.IActionResult)) - { - // Method signature does not look like IActionResult MyAction or SomeAwaitable. - // Nothing to do here. - return; - } - - // Method returns an IActionResult. Determine if the method block returns an ObjectResult - foreach (var returnStatement in methodSyntax.DescendantNodes().OfType()) - { - var returnType = context.SemanticModel.GetTypeInfo(returnStatement.Expression, context.CancellationToken); - if (returnType.Type == null || returnType.Type.Kind == SymbolKind.ErrorType) - { - continue; - } - - ImmutableDictionary 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(StringComparer.Ordinal) - .Add(ReturnTypeKey, desiredReturnTypeString); - } - - context.ReportDiagnostic(Diagnostic.Create( - SupportedDiagnostic, - methodSyntax.ReturnType.GetLocation(), - properties: properties)); - } - } - }, SyntaxKind.MethodDeclaration); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTCodeFixProvider.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTCodeFixProvider.cs deleted file mode 100644 index 7dc5b5ec14..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiActionsShouldUseActionResultOfTCodeFixProvider.cs +++ /dev/null @@ -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 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 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(); - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerBase.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerBase.cs deleted file mode 100644 index b70d1409de..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerBase.cs +++ /dev/null @@ -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 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); - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerContext.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerContext.cs deleted file mode 100644 index 1ab36e3dac..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ApiControllerAnalyzerContext.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/CodeAnalysisExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/CodeAnalysisExtensions.cs deleted file mode 100644 index a8ae5b8a0f..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/CodeAnalysisExtensions.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerBase.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerBase.cs deleted file mode 100644 index 71c497fa29..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerBase.cs +++ /dev/null @@ -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 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); - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerContext.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerContext.cs deleted file mode 100644 index 8a9d403a8a..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/ControllerAnalyzerContext.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/DiagnosticDescriptors.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/DiagnosticDescriptors.cs deleted file mode 100644 index 14f56b5918..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/DiagnosticDescriptors.cs +++ /dev/null @@ -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.", - "Actions on types annotated with ApiControllerAttribute should return ActionResult.", - "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); - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj deleted file mode 100644 index 9c4237c2ab..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - CSharp Analyzers for ASP.NET Core MVC. - aspnetcore;aspnetcoremvc - - false - $(ExperimentalVersionPrefix) - $(ExperimentalVersionSuffix) - $(ExperimentalPackageVersion) - - netstandard1.3 - false - false - false - - - - - - - - - - - - - - - diff --git a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/TypeNames.cs b/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/TypeNames.cs deleted file mode 100644 index 810c63a7bd..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Analyzers.Experimental/TypeNames.cs +++ /dev/null @@ -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"; - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ActionsMustNotBeAsyncVoidFacts.cs b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ActionsMustNotBeAsyncVoidFacts.cs deleted file mode 100644 index d3fafb9f5c..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ActionsMustNotBeAsyncVoidFacts.cs +++ /dev/null @@ -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); - }); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsAreAttributeRoutedFacts.cs b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsAreAttributeRoutedFacts.cs deleted file mode 100644 index d7968d7928..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsAreAttributeRoutedFacts.cs +++ /dev/null @@ -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); - }); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsShouldUseActionResultOfTFacts.cs b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsShouldUseActionResultOfTFacts.cs deleted file mode 100644 index 1cdd409ddc..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/ApiActionsShouldUseActionResultOfTFacts.cs +++ /dev/null @@ -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")] - [InlineData("System.Threading.Task")] - 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 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 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 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> 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); - }); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Infrastructure/AnalyzerTestBase.cs b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Infrastructure/AnalyzerTestBase.cs deleted file mode 100644 index 1351b24c94..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Infrastructure/AnalyzerTestBase.cs +++ /dev/null @@ -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 MarkerLocations { get; } = new Dictionary(); - - 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() - .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 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 ApplyCodeFixAsync( - Project project, - Diagnostic[] analyzerDiagnostic, - int codeFixIndex = 0) - { - var diagnostic = analyzerDiagnostic.Single(); - return ApplyCodeFixAsync(project, diagnostic, codeFixIndex); - } - - protected async Task 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(); - 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 ApplyFixAsync(CodeAction codeAction) - { - var operations = await codeAction.GetOperationsAsync(CancellationToken.None); - return Assert.Single(operations.OfType()).ChangedSolution; - } - - public void Dispose() - { - Workspace?.Dispose(); - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test.csproj b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test.csproj deleted file mode 100644 index 86f4228e01..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - $(StandardTestTfms) - true - - - - - - - - - - - - - - - diff --git a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/xunit.runner.json b/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/xunit.runner.json deleted file mode 100644 index 1c72a421ad..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Analyzers.Experimental.Test/xunit.runner.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "shadowCopy": false -} diff --git a/version.props b/version.props index e01d0a1143..7bf6e5246c 100644 --- a/version.props +++ b/version.props @@ -9,13 +9,5 @@ $(VersionPrefix)-$(VersionSuffix)-final $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) $(VersionSuffix)-$(BuildNumber) - - 0.2.0 - preview3 - - $(ExperimentalVersionPrefix) - $(ExperimentalVersionPrefix)-$(ExperimentalVersionSuffix)-final - $(FeatureBranchVersionPrefix)$(ExperimentalVersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) - $(ExperimentalVersionSuffix)-$(BuildNumber)