Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix S4529: Should not highlight constructors, properties and methods with NonActionAttribute #2109

Merged
merged 2 commits into from
Nov 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ public static class AspNetMvcHelper
private static readonly ImmutableArray<KnownType> controllerTypes =
ImmutableArray.Create(
KnownType.Microsoft_AspNetCore_Mvc_ControllerBase,
KnownType.System_Web_Mvc_Controller
);
KnownType.System_Web_Mvc_Controller);

private static readonly ImmutableArray<KnownType> nonActionTypes =
ImmutableArray.Create(
KnownType.Microsoft_AspNetCore_Mvc_NonActionAttribute,
KnownType.System_Web_Mvc_NonActionAttribute);


private static readonly ImmutableArray<KnownType> nonControllerAttributeTypes =
ImmutableArray.Create(KnownType.Microsoft_AspNetCore_Mvc_NonControllerAttribute);
Expand All @@ -43,7 +48,9 @@ public static class AspNetMvcHelper
/// controller method.
/// </summary>
public static bool IsControllerMethod(this IMethodSymbol methodSymbol) =>
methodSymbol.MethodKind == MethodKind.Ordinary &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put this line before the effective accessibility check as it is simpler (faster).

methodSymbol.GetEffectiveAccessibility() == Accessibility.Public &&
!methodSymbol.GetAttributes().Any(d => d.AttributeClass.IsAny(nonActionTypes)) &&
IsControllerType(methodSymbol?.ContainingType);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal sealed class KnownType
internal static readonly KnownType Microsoft_AspNetCore_Mvc_Controller = new KnownType("Microsoft.AspNetCore.Mvc.Controller");
internal static readonly KnownType Microsoft_AspNetCore_Mvc_ControllerBase = new KnownType("Microsoft.AspNetCore.Mvc.ControllerBase");
internal static readonly KnownType Microsoft_AspNetCore_Mvc_ControllerAttribute = new KnownType("Microsoft.AspNetCore.Mvc.ControllerAttribute");
internal static readonly KnownType Microsoft_AspNetCore_Mvc_NonActionAttribute = new KnownType("Microsoft.AspNetCore.Mvc.NonActionAttribute");
internal static readonly KnownType Microsoft_AspNetCore_Mvc_NonControllerAttribute = new KnownType("Microsoft.AspNetCore.Mvc.NonControllerAttribute");
internal static readonly KnownType Microsoft_AspNetCore_Mvc_VirtualFileResult = new KnownType("Microsoft.AspNetCore.Mvc.VirtualFileResult");
internal static readonly KnownType Microsoft_AspNetCore_Routing_VirtualPathData = new KnownType("Microsoft.AspNetCore.Routing.VirtualPathData");
Expand Down Expand Up @@ -346,6 +347,7 @@ internal sealed class KnownType
internal static readonly KnownType System_Web_HttpRequestBase = new KnownType("System.Web.HttpRequestBase");
internal static readonly KnownType System_Web_HttpResponseBase = new KnownType("System.Web.HttpResponseBase");
internal static readonly KnownType System_Web_HttpServerUtilityBase = new KnownType("System.Web.HttpServerUtilityBase");
internal static readonly KnownType System_Web_Mvc_NonActionAttribute = new KnownType("System.Web.Mvc.NonActionAttribute");
internal static readonly KnownType System_Web_Mvc_Controller = new KnownType("System.Web.Mvc.Controller");
internal static readonly KnownType System_Web_Mvc_HttpPostAttribute = new KnownType("System.Web.Mvc.HttpPostAttribute");
internal static readonly KnownType System_Web_Mvc_UrlHelper = new KnownType("System.Web.Mvc.UrlHelper");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ public void PublicFoo() { }
protected void ProtectedFoo() { }
internal void InternalFoo() { }
private void PrivateFoo() { }
private class Bar : System.Web.Mvb.Controller
private class Bar : System.Web.Mvc.Controller
{
public void InnerFoo() { }
}
[System.Web.Mvc.NonActionAttribute]
public void PublicNonAction() { }
}
";
var compilation = TestHelper.Compile(code,
Expand All @@ -54,12 +56,14 @@ public void InnerFoo() { }
var internalFoo = compilation.GetMethodSymbol("InternalFoo");
var privateFoo = compilation.GetMethodSymbol("PrivateFoo");
var innerFoo = compilation.GetMethodSymbol("InnerFoo");
var publicNonAction = compilation.GetMethodSymbol("PublicNonAction");

AspNetMvcHelper.IsControllerMethod(publicFoo).Should().Be(true);
AspNetMvcHelper.IsControllerMethod(protectedFoo).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(internalFoo).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(privateFoo).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(innerFoo).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(publicNonAction).Should().Be(false);
}

[DataTestMethod]
Expand All @@ -71,6 +75,8 @@ public void Controller_Methods_Are_EntryPoints(string aspNetMvcVersion)
public class Foo : System.Web.Mvc.Controller
{
public void PublicFoo() { }
[System.Web.Mvc.NonActionAttribute]
public void PublicNonAction() { }
}
public class Controller
{
Expand All @@ -87,52 +93,58 @@ public void PublicDiz() { }
var publicFoo = compilation.GetMethodSymbol("PublicFoo");
var publicBar = compilation.GetMethodSymbol("PublicBar");
var publicDiz = compilation.GetMethodSymbol("PublicDiz");
var publicNonAction = compilation.GetMethodSymbol("PublicNonAction");

AspNetMvcHelper.IsControllerMethod(publicFoo).Should().Be(true);
AspNetMvcHelper.IsControllerMethod(publicBar).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(publicDiz).Should().Be(false);
AspNetMvcHelper.IsControllerMethod(publicNonAction).Should().Be(false);
}

[DataTestMethod]
[DataRow("3.0.20105.1")]
[DataRow("2.1.3")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain why you did this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is for ASP.NET MVC Core (the attributes are not present in the desktop version of ASP.NET MVC)

[DataRow(Constants.NuGetLatestVersion)]
public void Methods_In_Classes_With_ControllerAttribute_Are_EntryPoints(string aspNetMvcVersion)
{
const string code = @"
// The Attribute suffix is required because we don't have a reference
// to ASP.NET Core and we cannot do type checking in the test project.
// We will need to convert this test project to .NET Core to do that.
[Microsoft.AspNetCore.Mvc.ControllerAttribute]
public class Foo
{
public void PublicFoo() { }
[Microsoft.AspNetCore.Mvc.NonActionAttribute]
public void PublicNonAction() { }
}
";
var compilation = TestHelper.Compile(code,
additionalReferences: NuGetMetadataReference.MicrosoftAspNetMvc(aspNetMvcVersion).ToArray());
additionalReferences:
FrameworkMetadataReference.Netstandard
.Union(NuGetMetadataReference.MicrosoftAspNetCoreMvcCore(aspNetMvcVersion))
.ToArray());

var publicFoo = compilation.GetMethodSymbol("PublicFoo");
var publicNonAction = compilation.GetMethodSymbol("PublicNonAction");

AspNetMvcHelper.IsControllerMethod(publicFoo).Should().Be(true);
AspNetMvcHelper.IsControllerMethod(publicNonAction).Should().Be(false);
}

[DataTestMethod]
[DataRow("3.0.20105.1")]
[DataRow("2.1.3")]
[DataRow(Constants.NuGetLatestVersion)]
public void Methods_In_Classes_With_NonControllerAttribute_Are_Not_EntryPoints(string aspNetMvcVersion)
{
const string code = @"
// The Attribute suffix is required because we don't have a reference
// to ASP.NET Core and we cannot do type checking in the test project.
// We will need to convert this test project to .NET Core to do that.
[Microsoft.AspNetCore.Mvc.NonControllerAttribute]
public class Foo : Microsoft.AspNetCore.Mvc.ControllerBase
{
public void PublicFoo() { }
}
";
var compilation = TestHelper.Compile(code,
additionalReferences: NuGetMetadataReference.MicrosoftAspNetMvc(aspNetMvcVersion).ToArray());
additionalReferences:
FrameworkMetadataReference.Netstandard
.Union(NuGetMetadataReference.MicrosoftAspNetCoreMvcCore(aspNetMvcVersion))
.ToArray());

var publicFoo = compilation.GetMethodSymbol("PublicFoo");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ public static IEnumerable<MetadataReference> MicrosoftAspNetCoreRoutingAbstracti
public static IEnumerable<MetadataReference> MicrosoftAspNetMvc(string packageVersion) =>
Create("Microsoft.AspNet.Mvc", packageVersion);

public static IEnumerable<MetadataReference> MicrosoftAspNetMvcCore(string packageVersion) =>
Create("Microsoft.AspNet.Mvc.Core", packageVersion);

public static IEnumerable<MetadataReference> MicrosoftExtensionsConfigurationAbstractions(string packageVersion) =>
Create("Microsoft.Extensions.Configuration.Abstractions", packageVersion);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
using System.Web.Mvc;
using System;
using System.Web.Mvc;

public class Foo : Controller
{
public Foo() { } // Compliant, ctors should not be highlighted

~Foo() { } // Compliant, destructors should not be highlighted

public int MyProperty // Compliant, properties should not be highlighted
{
get { return 0; }
set { }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test with an event, an operator and a destructor.

public static Foo operator +(Foo a, Foo b) => null; // Compliant, operators should not be highlighted

public event EventHandler<EventArgs> MyEvent // Compliant, events should not be highlighted
{
add { }
remove { }
}

public void PublicFoo() { } // Noncompliant {{Make sure that exposing this HTTP endpoint is safe here.}}
// ^^^^^^^^^

Expand All @@ -11,6 +30,9 @@ internal void InternalFoo() { }

private void PrivateFoo() { }

[NonAction]
public void PublicNonAction() { } // Compliant, methods decorated with NonAction are not entrypoints

private class Bar : Controller
{
public void InnerFoo() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
Public Class Foo
Inherits Controller

Public sub New () ' Compliant, ctors should not be highlighted
End sub

Protected Overrides Sub Finalize() ' Compliant, desctructors should not be highlighted; in addition, this protected and will not be highlighted anyway
End Sub

Public Property MyProperty As Integer ' Compliant, properties should not be highlighted
Get
Return 0
End Get
Set(ByVal value As Integer)
End Set
End Property

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for C#.

Public Sub PublicFoo() ' Noncompliant {{Make sure that exposing this HTTP endpoint is safe here.}}
' ^^^^^^^^^
End Sub
Expand All @@ -16,6 +30,10 @@ Public Class Foo
Private Sub PrivateFoo()
End Sub

<NonAction>
Public Sub PublicNonAction() ' Compliant, methods decorated with NonAction are not entrypoints
End Sub

Private Class Bar
Inherits Controller

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

public class SomeController : ControllerBase
{
public SomeController() { } // Compliant, ctors should not be highlighted

public int MyProperty // Compliant, properties should not be highlighted
{
get { return 0; }
set { }
}

public void PublicFoo() { } // Noncompliant {{Make sure that exposing this HTTP endpoint is safe here.}}
// ^^^^^^^^^

Expand All @@ -11,6 +19,9 @@ internal void InternalFoo() { }

private void PrivateFoo() { }

[NonAction]
public void PublicNonAction() { } // Compliant, methods decorated with NonAction are not entrypoints

private class Bar : ControllerBase
{
public void InnerFoo() { }
Expand All @@ -20,6 +31,8 @@ public void InnerFoo() { }
[Controller]
public class MyController
{
public MyController() { } // Compliant, ctor should not be highlighted

public void PublicFoo() { } // Noncompliant
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
Public Class SomeController
Inherits ControllerBase

Public Sub New() ' Compliant, ctors should not be highlighted
End sub

Public Property MyProperty As Integer ' Compliant, properties should not be highlighted
Get
Return 0
End Get
Set(ByVal value As Integer)
End Set
End Property


Public Sub PublicFoo() ' Noncompliant {{Make sure that exposing this HTTP endpoint is safe here.}}
' ^^^^^^^^^
End Sub
Expand All @@ -16,6 +28,10 @@ Public Class SomeController
Private Sub PrivateFoo()
End Sub

<NonAction>
Public Sub PublicNonAction() ' Compliant, methods decorated with NonAction are not entrypoints
End Sub

Private Class Bar
Inherits ControllerBase

Expand All @@ -26,6 +42,9 @@ End Class

<Controller>
Public Class MyController
Public Sub New() ' Compliant, ctor should not be highlighted
End sub

Public Sub PublicFoo() ' Noncompliant
End Sub
End Class
Expand Down