diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs index a225d69fb..4d69aba2c 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD010MainThreadUsageAnalyzer.cs @@ -12,6 +12,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; + using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Text; /// @@ -84,6 +85,8 @@ public class VSTHRD010MainThreadUsageAnalyzer : DiagnosticAnalyzer DescriptorSync, DescriptorAsync); + private readonly LanguageUtils languageUtils = CSharpUtils.Instance; + private enum ThreadingContext { /// @@ -142,9 +145,8 @@ public override void Initialize(AnalysisContext context) codeBlockStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeIsPattern), SyntaxKind.IsPatternExpression); }); - compilationStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AddToCallerCalleeMap(c, callerToCalleeMap)), SyntaxKind.InvocationExpression); - compilationStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AddToCallerCalleeMap(c, callerToCalleeMap)), SyntaxKind.SimpleMemberAccessExpression); - compilationStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AddToCallerCalleeMap(c, callerToCalleeMap)), SyntaxKind.IdentifierName); + compilationStartContext.RegisterOperationAction(Utils.DebuggableWrapper(c => this.AddToCallerCalleeMap(c, callerToCalleeMap)), OperationKind.Invocation); + compilationStartContext.RegisterOperationAction(Utils.DebuggableWrapper(c => this.AddToCallerCalleeMap(c, callerToCalleeMap)), OperationKind.PropertyReference); compilationStartContext.RegisterCompilationEndAction(compilationEndContext => { @@ -201,9 +203,30 @@ void MarkMethod(IMethodSymbol method) return result; } - private static void AddToCallerCalleeMap(SyntaxNodeAnalysisContext context, Dictionary> callerToCalleeMap) + private static Dictionary> CreateCalleeToCallerMap(Dictionary> callerToCalleeMap) + { + var result = new Dictionary>(); + + foreach (KeyValuePair> item in callerToCalleeMap) + { + IMethodSymbol? caller = item.Key; + foreach (CallInfo callee in item.Value) + { + if (!result.TryGetValue(callee.MethodSymbol, out List? callers)) + { + result[callee.MethodSymbol] = callers = new List(); + } + + callers.Add(new CallInfo(methodSymbol: caller, callee.InvocationSyntax)); + } + } + + return result; + } + + private void AddToCallerCalleeMap(OperationAnalysisContext context, Dictionary> callerToCalleeMap) { - if (CSharpUtils.IsWithinNameOf(context.Node)) + if (CSharpUtils.IsWithinNameOf(context.Operation.Syntax)) { return; } @@ -212,7 +235,7 @@ private static void AddToCallerCalleeMap(SyntaxNodeAnalysisContext context, Dict { if (propertySymbol is object) { - return CSharpUtils.IsOnLeftHandOfAssignment(context.Node) + return CSharpUtils.IsOnLeftHandOfAssignment(context.Operation.Syntax) ? propertySymbol.SetMethod : propertySymbol.GetMethod; } @@ -221,18 +244,15 @@ private static void AddToCallerCalleeMap(SyntaxNodeAnalysisContext context, Dict } ISymbol? targetMethod = null; - SyntaxNode locationToBlame = context.Node; - switch (context.Node) + SyntaxNode locationToBlame = context.Operation.Syntax; + switch (context.Operation) { - case InvocationExpressionSyntax invocationExpressionSyntax: - targetMethod = context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax.Expression, context.CancellationToken).Symbol; - locationToBlame = invocationExpressionSyntax.Expression; + case IInvocationOperation invocationOperation: + targetMethod = invocationOperation.TargetMethod; + locationToBlame = this.languageUtils.IsolateMethodName(invocationOperation); break; - case MemberAccessExpressionSyntax memberAccessExpressionSyntax: - targetMethod = GetPropertyAccessor(context.SemanticModel.GetSymbolInfo(memberAccessExpressionSyntax.Name, context.CancellationToken).Symbol as IPropertySymbol); - break; - case IdentifierNameSyntax identifierNameSyntax: - targetMethod = GetPropertyAccessor(context.SemanticModel.GetSymbolInfo(identifierNameSyntax, context.CancellationToken).Symbol as IPropertySymbol); + case IPropertyReferenceOperation propertyReference: + targetMethod = GetPropertyAccessor(propertyReference.Property); break; } @@ -250,27 +270,6 @@ private static void AddToCallerCalleeMap(SyntaxNodeAnalysisContext context, Dict } } - private static Dictionary> CreateCalleeToCallerMap(Dictionary> callerToCalleeMap) - { - var result = new Dictionary>(); - - foreach (KeyValuePair> item in callerToCalleeMap) - { - IMethodSymbol? caller = item.Key; - foreach (CallInfo callee in item.Value) - { - if (!result.TryGetValue(callee.MethodSymbol, out List? callers)) - { - result[callee.MethodSymbol] = callers = new List(); - } - - callers.Add(new CallInfo(methodSymbol: caller, callee.InvocationSyntax)); - } - } - - return result; - } - private readonly struct CallInfo { public CallInfo(IMethodSymbol methodSymbol, SyntaxNode invocationSyntax)