diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/SpecifyRouteAttribute.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/SpecifyRouteAttribute.cs index 869095b1e3d..a3244e1a974 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/SpecifyRouteAttribute.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/SpecifyRouteAttribute.cs @@ -43,7 +43,7 @@ protected override void Initialize(SonarAnalysisContext context) => } compilationStart.RegisterSymbolStartAction(symbolStart => { - if (symbolStart.Symbol.GetAttributes().Any(x => x.AttributeClass.Is(KnownType.Microsoft_AspNetCore_Mvc_RouteAttribute))) + if (symbolStart.Symbol.GetAttributesWithInherited().Any(x => x.AttributeClass.Is(KnownType.Microsoft_AspNetCore_Mvc_RouteAttribute))) { return; } @@ -53,7 +53,7 @@ protected override void Initialize(SonarAnalysisContext context) => var methodDeclaration = (MethodDeclarationSyntax)nodeContext.Node; if (nodeContext.SemanticModel.GetDeclaredSymbol(methodDeclaration, nodeContext.Cancel) is { } method && method.IsControllerMethod() - && method.GetAttributes().Any(x => !CanBeIgnored(x.GetAttributeRouteTemplate(RouteTemplateAttributes)))) + && method.GetAttributesWithInherited().Any(x => !CanBeIgnored(x.GetAttributeRouteTemplate(RouteTemplateAttributes)))) { secondaryLocations.Push(methodDeclaration.Identifier.GetLocation()); } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/SpecifyRouteAttribute.CSharp12.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/SpecifyRouteAttribute.CSharp12.cs index a1cbb206f65..404123e7c7d 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/SpecifyRouteAttribute.CSharp12.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/SpecifyRouteAttribute.CSharp12.cs @@ -190,3 +190,30 @@ public class DerivedController : Controller { } [Controller] public class Endpoint { } + +[Route("api/[controller]")] +public class ControllerWithRouteAttribute : Controller { } + +public class ControllerWithInheritedRoute : ControllerWithRouteAttribute // Compliant, attribute is inherited +{ + [HttpGet("Test")] // Route: api/ControllerWithInheritedRoute/Test + public string Index() => "Hi!"; +} + +public class BaseControllerWithActionWithRoute : Controller // Noncompliant +{ + [HttpGet("Test")] //Route: /Test (AmbiguousMatchException raised because of the override in ControllerOverridesActionWithRoute) + public virtual string Index() => "Hi!"; // Secondary + + // Route: BaseControllerWithActionWithRoute/Index/1 + public virtual string Index(int id) => "Hi!"; // Compliant +} + +public class ControllerOverridesActionWithRoute : BaseControllerWithActionWithRoute // Noncompliant +{ + // Route: /Test (AmbiguousMatchException raised because the base method is also in scope) + public override string Index() => "Hi!"; // Secondary + + // Route: ControllerOverridesActionWithRoute/Index/1 + public override string Index(int id) => "Hi!"; // Compliant +}