in src/Microsoft.VisualStudio.SDK.Analyzers/VSSDK007ThreadHelperJTFRunAsync.cs [60:143]
private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context)
{
var invocationExpr = (InvocationExpressionSyntax)context.Node;
// We don't care about awaited calls.
if (IsAwaited(invocationExpr))
{
return;
}
// Is a member named `RunAsync` being invoked? (Confirm it's the right one later.)
if (invocationExpr.Expression is not MemberAccessExpressionSyntax runAsyncExpr ||
runAsyncExpr.Name.Identifier.ToString() != Types.JoinableTaskFactory.RunAsync)
{
return;
}
// Do we have a `JoinableTaskFactory.RunAsync` expression?
if (runAsyncExpr.Expression is not MemberAccessExpressionSyntax jtfExpr ||
jtfExpr.Name.Identifier.ToString() != Types.JoinableTaskFactory.TypeName)
{
return;
}
// Do we have a `ThreadHelper.JoinableTaskFactory.RunAsync` expression?
if (jtfExpr.Expression is not IdentifierNameSyntax threadHelperExpr ||
threadHelperExpr.ToString() != Types.ThreadHelper.TypeName)
{
return;
}
// Verify `ThreadHelper` is the correct type in the expected namespace.
ISymbol? threadHelperSymbol = context
.SemanticModel
.GetSymbolInfo(threadHelperExpr, context.CancellationToken)
.Symbol;
if (threadHelperSymbol == null || !threadHelperSymbol.BelongsToNamespace(Types.ThreadHelper.Namespace))
{
return;
}
// Get the symbol for the `RunAsync` method and verify it belongs to the correct JoinableTaskFactory
// class in the expected namespace.
var isRunAsyncMethodOnJTF =
context
.SemanticModel
.GetSymbolInfo(invocationExpr, context.CancellationToken)
.Symbol is IMethodSymbol runAsyncSymbol &&
runAsyncSymbol.Name == Types.JoinableTaskFactory.RunAsync &&
runAsyncSymbol.ContainingType.Name == Types.JoinableTaskFactory.TypeName &&
runAsyncSymbol.ContainingType.BelongsToNamespace(Types.JoinableTaskFactory.Namespace);
if (!isRunAsyncMethodOnJTF)
{
return;
}
// No diagnostic if the RunAsync invocation is immediately synchronously joined with Join(),
// e.g. ThreadHelper.JoinableTaskFactory.RunAsync(...).Join();
if (IsSynchronouslyJoined(context, invocationExpr))
{
return;
}
// Is the JoinableTask returned from RunAsync assigned to a variable? If so, search the enclosing block
// and check if that variable is awaited/joined.
if (IsJoinableTaskAssigned(invocationExpr, out SyntaxNode? assignedTo, out SyntaxToken? assignedToToken) &&
assignedTo != null &&
assignedToToken != null)
{
SyntaxNode? enclosingBlock = GetEnclosingBlock(assignedTo);
if (enclosingBlock != null)
{
if (IsVariableAwaitedOrJoined(context, assignedToToken.Value.ValueText, enclosingBlock))
{
return;
}
}
}
// Report the diagnostic
context.ReportDiagnostic(Diagnostic.Create(Descriptor, runAsyncExpr.Name.GetLocation()));
}