内容目录
Inherit from DiagnosticAnalyzer.
Detecting async void
Register the listener:
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
// Register analysis type, only analyze methods
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
}
Analyze the code node:
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var node = context.Node;
if (node.IsKind(SyntaxKind.MethodDeclaration))
{
var syntax = node as MethodDeclarationSyntax;
if (syntax is null) return;
AnalyzeMethodDeclarationSyntax(context, syntax);
}
}
Determine if the node is an async void method:
private void AnalyzeMethodDeclarationSyntax(SyntaxNodeAnalysisContext context, MethodDeclarationSyntax syntax)
{
var returnType = syntax.ReturnType as PredefinedTypeSyntax;
if (returnType == null) return;
if (returnType.Keyword.ValueText != SyntaxFactory.Token(SyntaxKind.VoidKeyword).ValueText) return;
if (syntax.Modifiers.Any(x => x.ValueText == "async"))
{
context.ReportDiagnostic(Diagnostic.Create(
descriptor: Rule,
location: returnType.GetLocation(),
messageArgs: "async void"));
}
}
If the developer uses async void code, an error will be prompted.
Detecting asynchronous methods not using await
Register the listener:
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ExpressionStatement);
}
Analyze the node:
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var node = context.Node;
var ex = node as ExpressionStatementSyntax;
if (ex is null) return;
var syntax = ex.Expression as InvocationExpressionSyntax;
if (syntax is null) return;
AnalyzeMethodDeclarationSyntax(context, syntax);
}
Detect and analyze multiple situations:
private static string[] BlockingCalls = new string[] { "GetAwaiter", "Result", "Wait" };
private void AnalyzeMethodDeclarationSyntax(SyntaxNodeAnalysisContext context, InvocationExpressionSyntax syntax)
{
// Check if the invoked method is an async Task method
var invokeMethod = context.SemanticModel.GetSymbolInfo(syntax).Symbol as IMethodSymbol;
if (invokeMethod == null || !ReturnTask(invokeMethod))
{
return;
}
// Get the name of the invoked method
var methodSymbol = context
.SemanticModel
.GetSymbolInfo(syntax, context.CancellationToken)
.Symbol as IMethodSymbol;
// Find this method reference
var syntaxReference = methodSymbol
.DeclaringSyntaxReferences
.FirstOrDefault();
// Get the definition of this method in the syntax tree
var methodDeclaration = syntaxReference.GetSyntax(context.CancellationToken) as MethodDeclarationSyntax;
if (methodDeclaration == null) return;
// If it's not an asynchronous method
if (!methodDeclaration.Modifiers.Any(x => x.ValueText == "async")) return;
// Already awaited
var isAwaited = syntax.Ancestors().OfType<AwaitExpressionSyntax>().Any();
if (isAwaited)
{
return;
}
// Check if await, _ = xxx, or BlockingCalls were used
bool isInvocationWaited = false;
foreach (var parent in syntax.Ancestors())
{
var assignment = parent as AssignmentExpressionSyntax;
// Already used discard: _ = xxx
if (assignment != null)
{
return;
}
var parentMemberAccess = parent as MemberAccessExpressionSyntax;
if (parentMemberAccess?.Name != null)
{
if (BlockingCalls.Any(a => a.Equals(parentMemberAccess.Name.Identifier.ValueText, StringComparison.OrdinalIgnoreCase)))
{
isInvocationWaited = true;
break;
}
}
if (isInvocationWaited)
{
return;
}
}
context.ReportDiagnostic(Diagnostic.Create(
descriptor: Rule,
location: syntax.GetLocation()));
}
public static T FirstAncestorOrSelfUnderGivenNode<T>(SyntaxNode node, SyntaxNode parent) where T : SyntaxNode
{
var current = node;
while (current != null && current != parent)
{
var temp = current as T;
if (temp != null)
{
return temp;
}
current = current.Parent;
}
return null;
}
public static bool IsTask(ITypeSymbol type)
{
return type.ContainingNamespace?.ToDisplayString() == "System.Threading.Tasks" &&
(type.Name == "Task" || type.Name == "ValueTask");
}
public static bool ReturnTask(IMethodSymbol symbol)
{
return !symbol.ReturnsVoid && IsTask(symbol.ReturnType);
}
文章评论