-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Analyzer + Fixer: Forward cancellation token to method invocations (#…
…3641) * Analyzer + Fixer: Forward cancellation token to async methods * Address PR suggestions: - Do not limit to only await. Detect all invocations. - Fix the resource messages. - Fix some warnings. * Address case where CancellationToken is an aliased using * Address PR suggestions * Fix breaking change introduced by most recent rebased changes. * Address PR suggestions * Address named argument problem in C#, add two cases with diagnostics but no fix. * Offer diagnostic only on the invocation expression name. * Bail out early in fixer before registering code fix * address analyzer attributes in wrong locations * Fix newest edge cases except lambda that does not take token * Address last false positives, add trivia support * Fix comment formatting warning * Additional case for ct params * Added more edge cases, ensured to do the most basic check at the beginning * Add method extension unit test * Address suggestions and more edge cases * Address latest suggestions, fixed one VB case where extension method parameters were not detected correctly * Address latest suggestions * Address last suggestions
- Loading branch information
1 parent
88a0117
commit b9b4015
Showing
23 changed files
with
5,070 additions
and
28 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
...crosoft.NetCore.Analyzers/Runtime/CSharpForwardCancellationTokenToInvocations.Analyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) Microsoft. 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.NetCore.Analyzers.Runtime; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis.Operations; | ||
using System.Linq; | ||
|
||
namespace Microsoft.NetCore.CSharp.Analyzers.Runtime | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public sealed class CSharpForwardCancellationTokenToInvocationsAnalyzer : ForwardCancellationTokenToInvocationsAnalyzer | ||
{ | ||
protected override SyntaxNode? GetInvocationMethodNameNode(SyntaxNode invocationNode) | ||
{ | ||
if (invocationNode is InvocationExpressionSyntax invocationExpression) | ||
{ | ||
if (invocationExpression.Expression is MemberBindingExpressionSyntax memberBindingExpression) | ||
{ | ||
// When using nullability features, specifically attempting to dereference possible null references, | ||
// the dot becomes part of the member invocation expression, so we need to return just the name, | ||
// so that the diagnostic gets properly returned in the method name only. | ||
return memberBindingExpression.Name; | ||
} | ||
return invocationExpression.Expression; | ||
} | ||
return null; | ||
} | ||
protected override bool ArgumentsImplicitOrNamed(INamedTypeSymbol cancellationTokenType, ImmutableArray<IArgumentOperation> arguments) | ||
{ | ||
return arguments.Any(a => | ||
(a.IsImplicit && !a.Parameter.Type.Equals(cancellationTokenType)) || | ||
(a.Syntax is ArgumentSyntax argumentNode && argumentNode.NameColon != null)); | ||
} | ||
|
||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
.../Microsoft.NetCore.Analyzers/Runtime/CSharpForwardCancellationTokenToInvocations.Fixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Operations; | ||
using Microsoft.NetCore.Analyzers.Runtime; | ||
|
||
namespace Microsoft.NetCore.CSharp.Analyzers.Runtime | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp)] | ||
public sealed class CSharpForwardCancellationTokenToInvocationsFixer : ForwardCancellationTokenToInvocationsFixer | ||
{ | ||
protected override bool TryGetInvocation( | ||
SemanticModel model, | ||
SyntaxNode node, | ||
CancellationToken ct, | ||
[NotNullWhen(true)] out IInvocationOperation? invocation) | ||
{ | ||
// If the method was invoked using nullability for the case of attempting to dereference a possibly null reference, | ||
// then the node.Parent.Parent is the actual invocation (and it will contain the dot as well) | ||
|
||
var operation = node.Parent.IsKind(SyntaxKind.MemberBindingExpression) | ||
? model.GetOperation(node.Parent.Parent, ct) | ||
: model.GetOperation(node.Parent, ct); | ||
|
||
invocation = operation as IInvocationOperation; | ||
|
||
return invocation != null; | ||
} | ||
|
||
protected override bool IsArgumentNamed(IArgumentOperation argumentOperation) | ||
{ | ||
return argumentOperation.Syntax is ArgumentSyntax argumentNode && argumentNode.NameColon != null; | ||
} | ||
|
||
protected override SyntaxNode GetConditionalOperationInvocationExpression(SyntaxNode invocationNode) | ||
{ | ||
return ((InvocationExpressionSyntax)invocationNode).Expression; | ||
} | ||
|
||
protected override bool TryGetExpressionAndArguments( | ||
SyntaxNode invocationNode, | ||
[NotNullWhen(returnValue: true)] out SyntaxNode? expression, | ||
[NotNullWhen(returnValue: true)] out List<SyntaxNode>? arguments) | ||
{ | ||
if (invocationNode is InvocationExpressionSyntax invocationExpression) | ||
{ | ||
expression = invocationExpression.Expression; | ||
arguments = invocationExpression.ArgumentList.Arguments.Cast<SyntaxNode>().ToList(); | ||
return true; | ||
} | ||
|
||
expression = null; | ||
arguments = null; | ||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.