private static void UpdateInvocationLocation( SyntaxNodeAnalysisContext context, ISymbol symbol, SimpleNameSyntax currentAccessNode, GetAccessInfoDelegate getAccessInfo) { var symbolInfo = _applicationBuilderSymbolInfos.GetOrCreateValue(symbol); var scopeEnclosingNode = currentAccessNode.FirstAncestorOrSelf <CSharpSyntaxNode>(IsScopeEnclosingNode); lock (symbolInfo) { if (!symbolInfo.Scopes.TryGetValue(scopeEnclosingNode, out var scopeInfo)) { symbolInfo.Scopes[scopeEnclosingNode] = scopeInfo = new ScopeInfo(); } if (scopeInfo.DiagnosticReported) { return; } ref var existingAccessInfo = ref getAccessInfo(scopeInfo); if (existingAccessInfo.location == null || currentAccessNode.GetLocation().SourceSpan.Start < existingAccessInfo.location.SourceSpan.Start) { existingAccessInfo = (currentAccessNode.GetLocation(), currentAccessNode.ToString()); VerifyInvocationOrder(context, scopeInfo); } }
private void IsAccessingThreadDotSleep(SimpleNameSyntax invokedFunction, SyntaxNodeAnalysisContext context, bool isAsync) { var invokedSymbol = context.SemanticModel.GetSymbolInfo(invokedFunction).Symbol; if (invokedSymbol == null) { return; } var isAccessedFunctionCorrect = invokedSymbol.Name == "Sleep"; var isAccessedTypeCorrect = invokedSymbol.ContainingType?.Name == "Thread"; if (isAccessedFunctionCorrect && isAccessedTypeCorrect) { var dic = ImmutableDictionary.CreateBuilder <string, string>(); dic.Add("isAsync", isAsync.ToString()); var invocationNode = invokedFunction.FirstAncestorOrSelf <InvocationExpressionSyntax>(); context.ReportDiagnostic(Diagnostic.Create(Rule, invocationNode.GetLocation(), dic.ToImmutable(), null)); } }
protected override async Task <Solution> GetChangedSolutionAsync(CancellationToken cancellationToken) { var document = this.document; var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Find the synchronously blocking call member, // and bookmark it so we can find it again after some mutations have taken place. var syncAccessBookmark = new SyntaxAnnotation(); SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan); if (syncMethodName == null) { var syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); syncMethodName = syncMemberAccess.Name; } // When we give the Document a modified SyntaxRoot, yet another is created. So we first assign it to the Document, // then we query for the SyntaxRoot from the Document. document = document.WithSyntaxRoot( root.ReplaceNode(syncMethodName, syncMethodName.WithAdditionalAnnotations(syncAccessBookmark))); root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); syncMethodName = (SimpleNameSyntax)root.GetAnnotatedNodes(syncAccessBookmark).Single(); // We'll need the semantic model later. But because we've annotated a node, that changes the SyntaxRoot // and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!). // So after acquiring the semantic model, update it with the new method body. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(); var originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf <MethodDeclarationSyntax>(); // Ensure that the method or anonymous delegate is using the async keyword. MethodDeclarationSyntax updatedMethod; if (originalAnonymousMethodContainerIfApplicable != null) { updatedMethod = originalMethodDeclaration.ReplaceNode( originalAnonymousMethodContainerIfApplicable, originalAnonymousMethodContainerIfApplicable.MakeMethodAsync(semanticModel, cancellationToken)); } else { (document, updatedMethod) = await originalMethodDeclaration.MakeMethodAsync(document, cancellationToken).ConfigureAwait(false); semanticModel = null; // out-dated } if (updatedMethod != originalMethodDeclaration) { // Re-discover our synchronously blocking member. syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single(); } var syncExpression = (ExpressionSyntax)syncMethodName.FirstAncestorOrSelf <InvocationExpressionSyntax>() ?? syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); ExpressionSyntax awaitExpression; if (this.AlternativeAsyncMethod != string.Empty) { // Replace the member being called and await the invocation expression. // While doing so, move leading trivia to the surrounding await expression. var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[AsyncMethodKeyName])); awaitExpression = SyntaxFactory.AwaitExpression( syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia()) .WithLeadingTrivia(syncExpression.GetLeadingTrivia()); if (!(syncExpression.Parent is ExpressionStatementSyntax)) { awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression) .WithAdditionalAnnotations(Simplifier.Annotation); } } else { // Remove the member being accessed that causes a synchronous block and simply await the object. var syncMemberAccess = syncMethodName.FirstAncestorOrSelf <MemberAccessExpressionSyntax>(); var syncMemberStrippedExpression = syncMemberAccess.Expression; // Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls. var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax; if (expressionMethodCall?.Name.Identifier.Text == nameof(Task.GetAwaiter)) { syncMemberStrippedExpression = expressionMethodCall.Expression; } awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression.WithoutLeadingTrivia()) .WithLeadingTrivia(syncMemberStrippedExpression.GetLeadingTrivia()); } updatedMethod = updatedMethod .ReplaceNode(syncExpression, awaitExpression); var newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod); var newDocument = document.WithSyntaxRoot(newRoot); return(newDocument.Project.Solution); }