Skip to content

Commit

Permalink
Avoid VSTHRD010 diagnostics transitively across async methods
Browse files Browse the repository at this point in the history
Fixes #226
  • Loading branch information
AArnott committed Mar 17, 2018
1 parent 01bde1d commit 1ce7d7d
Showing 1 changed file with 16 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public override void Initialize(AnalysisContext context)
var typesRequiringMainThread = CommonInterest.ReadTypes(compilationStartContext, CommonInterest.FileNamePatternForTypesRequiringMainThread).ToImmutableArray();

var methodsDeclaringUIThreadRequirement = new HashSet<IMethodSymbol>();
var methodsAssertingUIThreadRequirement = new HashSet<IMethodSymbol>();
var callerToCalleeMap = new Dictionary<IMethodSymbol, HashSet<IMethodSymbol>>();

compilationStartContext.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockStartContext =>
Expand All @@ -125,6 +126,7 @@ public override void Initialize(AnalysisContext context)
MainThreadSwitchingMethods = mainThreadSwitchingMethods,
TypesRequiringMainThread = typesRequiringMainThread,
MethodsDeclaringUIThreadRequirement = methodsDeclaringUIThreadRequirement,
MethodsAssertingUIThreadRequirement = methodsAssertingUIThreadRequirement,
};
codeBlockStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
codeBlockStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeMemberAccess), SyntaxKind.SimpleMemberAccessExpression);
Expand All @@ -140,7 +142,7 @@ public override void Initialize(AnalysisContext context)
compilationStartContext.RegisterCompilationEndAction(compilationEndContext =>
{
var calleeToCallerMap = CreateCalleeToCallerMap(callerToCalleeMap);
var transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsDeclaringUIThreadRequirement, calleeToCallerMap);
var transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsAssertingUIThreadRequirement, calleeToCallerMap);
foreach (var implicitUserMethod in transitiveClosureOfMainThreadRequiringMethods.Except(methodsDeclaringUIThreadRequirement))
{
var declarationSyntax = implicitUserMethod.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(compilationEndContext.CancellationToken);
Expand Down Expand Up @@ -268,6 +270,8 @@ private class MethodAnalyzer

internal HashSet<IMethodSymbol> MethodsDeclaringUIThreadRequirement { get; set; }

internal HashSet<IMethodSymbol> MethodsAssertingUIThreadRequirement { get; set; }

internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocationSyntax = (InvocationExpressionSyntax)context.Node;
Expand All @@ -277,14 +281,24 @@ internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
var methodDeclaration = context.Node.FirstAncestorOrSelf<SyntaxNode>(n => CommonInterest.MethodSyntaxKinds.Contains(n.Kind()));
if (methodDeclaration != null)
{
if (this.MainThreadAssertingMethods.Contains(invokedMethod) || this.MainThreadSwitchingMethods.Contains(invokedMethod))
bool assertsMainThread = this.MainThreadAssertingMethods.Contains(invokedMethod);
bool switchesToMainThread = this.MainThreadSwitchingMethods.Contains(invokedMethod);
if (assertsMainThread || switchesToMainThread)
{
if (context.ContainingSymbol is IMethodSymbol callingMethod)
{
lock (this.MethodsDeclaringUIThreadRequirement)
{
this.MethodsDeclaringUIThreadRequirement.Add(callingMethod);
}

if (assertsMainThread)
{
lock (this.MethodsAssertingUIThreadRequirement)
{
this.MethodsAssertingUIThreadRequirement.Add(callingMethod);
}
}
}

this.methodDeclarationNodes = this.methodDeclarationNodes.SetItem(methodDeclaration, ThreadingContext.MainThread);
Expand Down

0 comments on commit 1ce7d7d

Please sign in to comment.