-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add analyzer for detecting misplaced attributes on delegates #35779
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
Conversation
4aadfca
to
bdd1a34
Compare
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
bdd1a34
to
8330349
Compare
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Show resolved
Hide resolved
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Show resolved
Hide resolved
src/Framework/Analyzer/src/DelegateEndpoints/DiagnosticDescriptors.cs
Outdated
Show resolved
Hide resolved
Assert.Collection(diagnostics, | ||
diagnostic => { | ||
Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); | ||
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we be producing the diagnostic on the attribute (rather than on the delegate)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, I think it makes more sense to put it on the delegate since that is where the user will take the action to resolve the issue (absent us providing a codefix).
src/Framework/Analyzer/src/DelegateEndpoints/DiagnosticDescriptors.cs
Outdated
Show resolved
Hide resolved
private TestDiagnosticAnalyzerRunner Runner { get; } = new(new DelegateEndpointAnalyzer()); | ||
|
||
[Fact] | ||
public async Task MinimalAction_WithCorrectlyPlacedAttribute_Works() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a test with an invalid lambda or a invalid method reference? e.g.
app.MapGet(""/"", () => DoesNotExist());
One with a MethodReference:
app.MapGet(""/"", GetPersonId);
[Authorize]
public string GetPersonId() => string.Empty;
and with a reference to a different source:
// Source1
app.MapGet(""/"", () => PersonHandlers.GetPersonId);
Where PersonHandlers.GetPersonId
is in a separate source file / SyntaxTree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, good idea. Here's the expected behavior for these:
- Invalid reference: No warning from us but user gets a not defined warning.
- Method reference: No warning from us since this warning only applies to lambdas.
- Reference to a method in a different class: Warning from us about misplaced attribute.
Where PersonHandlers.GetPersonId is in a separate source file / SyntaxTree
For this part, I couldn't find any examples in our codebase of the GetDiagnosticsAsync
method processing sources from different source files so I put it in separate files. Is there a way to do that?
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Builder; | ||
var app = WebApplication.Create(); | ||
app.MapGet(""/"", /*MM*/() => { return Hello(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one's interesting - if the code looked like:
(int id) => if (id == 0) { return Results.NotFound(); } else { return Hello(); }
This would produce no diagnostics? Is that weird?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's a consequence of not doing the full search through the syntax tree that we were doing before (see #35779 (comment)).
Supporting this scenario invites the question of how "deep" in the syntax tree we want to look for returned invocations and support throwing a warning.
return false; | ||
} | ||
|
||
var fullyQualifiedName = attribute.AttributeClass.ContainingNamespace.ToDisplayString(symbolDisplayFormat); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we look up the assembly name or walk up the namespace? Allocating a formatted string on every attribute doesn't sound great for performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can walk up the namespace, we'll still have to evaluate against the string representation of the NamespaceSymbol
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be fine. It wouldn't allocate a new string every time no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh, actually, it uses the same SymbolDisplay
logic under the hood.
I ended up rewriting this to avoid interacting with the FQNS of the type at all. We can just traverse up the tree until we find an AspNetCore
namespace containing in a Microsoft
namespace and treat that as a valid scenario.
return true; | ||
} | ||
|
||
return IsInValidNamespace(@namespace.ContainingNamespace); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice.
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
…mbdaAttribute.cs Co-authored-by: Pranav K <prkrishn@hotmail.com>
src/Framework/Analyzer/src/DelegateEndpoints/DetectMisplacedLambdaAttribute.cs
Outdated
Show resolved
Hide resolved
/backport release/6.0 |
/backport to release/6.0 |
Started backporting to release/6.0: https://github.com/dotnet/aspnetcore/actions/runs/1190188412 |
This PR adds an analyzer that detects if attributes are incorrectly placed on a method invoked from an attribute in a lambda.
The above will produce a diagnostic warning the user about the misplaced
AuthorizeAttribute
with the following message:Fixes #35638