Skip to content

Commit 717fb51

Browse files
authored
Merge pull request #97 from Concurrency-Lab/ph-s020-refactor-to-taskanalysis
PH_S020: Refactor to TaskAnalysis
2 parents b16a1e5 + d0e37f3 commit 717fb51

3 files changed

Lines changed: 84 additions & 13 deletions

File tree

src/ParallelHelper.Test/Util/TaskAnalysisTest.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,5 +861,65 @@ public void DoIt() {
861861
var analysis = new TaskAnalysis(semanticModel, default);
862862
Assert.IsFalse(analysis.IsFromResultInvocation(invocation));
863863
}
864+
865+
866+
867+
[TestMethod]
868+
public void IsCompletedTaskAccessReturnsTrueForAccessingCompletedTask() {
869+
const string source = @"
870+
using System.Threading.Tasks;
871+
872+
public class Test {
873+
public void DoIt() {
874+
var task = Task.CompletedTask;
875+
}
876+
}";
877+
var semanticModel = CompilationFactory.GetSemanticModel(source);
878+
var memberAccess = semanticModel.SyntaxTree.GetRoot()
879+
.DescendantNodes()
880+
.OfType<MemberAccessExpressionSyntax>()
881+
.Single();
882+
var analysis = new TaskAnalysis(semanticModel, default);
883+
Assert.IsTrue(analysis.IsCompletedTaskAccess(memberAccess));
884+
}
885+
886+
[TestMethod]
887+
public void IsCompletedTaskAccessReturnsFalseForAccessingUnknownTaskMember() {
888+
const string source = @"
889+
using System.Threading.Tasks;
890+
891+
public class Test {
892+
public void DoIt() {
893+
var task = Task.SomeNotExistantProperty;
894+
}
895+
}";
896+
var semanticModel = CompilationFactory.GetSemanticModel(source);
897+
var memberAccess = semanticModel.SyntaxTree.GetRoot()
898+
.DescendantNodes()
899+
.OfType<MemberAccessExpressionSyntax>()
900+
.Single();
901+
var analysis = new TaskAnalysis(semanticModel, default);
902+
Assert.IsFalse(analysis.IsCompletedTaskAccess(memberAccess));
903+
}
904+
905+
[TestMethod]
906+
public void IsCompletedTaskAccessReturnsFalseForAccessingDifferentMember() {
907+
const string source = @"
908+
using System;
909+
using System.Threading.Tasks;
910+
911+
public class Test {
912+
public void DoIt() {
913+
var task = Task.Factory;
914+
}
915+
}";
916+
var semanticModel = CompilationFactory.GetSemanticModel(source);
917+
var memberAccess = semanticModel.SyntaxTree.GetRoot()
918+
.DescendantNodes()
919+
.OfType<MemberAccessExpressionSyntax>()
920+
.Single();
921+
var analysis = new TaskAnalysis(semanticModel, default);
922+
Assert.IsFalse(analysis.IsCompletedTaskAccess(memberAccess));
923+
}
864924
}
865925
}

src/ParallelHelper/Analyzer/Smells/AwaitSynchronousTaskCompletionAnalyzer.cs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
44
using Microsoft.CodeAnalysis.Diagnostics;
5-
using ParallelHelper.Extensions;
65
using ParallelHelper.Util;
76
using System.Collections.Immutable;
87

@@ -36,10 +35,6 @@ public class AwaitSynchronousTaskCompletionAnalyzer : DiagnosticAnalyzer {
3635
isEnabledByDefault: true, description: Description, helpLinkUri: HelpLinkFactory.CreateUri(DiagnosticId)
3736
);
3837

39-
private const string TaskType = "System.Threading.Tasks.Task";
40-
private const string FromResultMethod = "FromResult";
41-
private const string CompletedTaskProperty = "CompletedTask";
42-
4338
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
4439

4540
public override void Initialize(AnalysisContext context) {
@@ -53,7 +48,11 @@ private static void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context) {
5348
}
5449

5550
private class Analyzer : InternalAnalyzerBase<AwaitExpressionSyntax> {
56-
public Analyzer(SyntaxNodeAnalysisContext context) : base(new SyntaxNodeAnalysisContextWrapper(context)) { }
51+
private readonly TaskAnalysis _taskAnalysis;
52+
53+
public Analyzer(SyntaxNodeAnalysisContext context) : base(new SyntaxNodeAnalysisContextWrapper(context)) {
54+
_taskAnalysis = new TaskAnalysis(context.SemanticModel, context.CancellationToken);
55+
}
5756

5857
public override void Analyze() {
5958
if(!AwaitsSynchronouslyCompletedTask()) {
@@ -69,16 +68,12 @@ private bool AwaitsSynchronouslyCompletedTask() {
6968

7069
private bool IsTaskFromResult(ExpressionSyntax expression) {
7170
return expression is InvocationExpressionSyntax invocation
72-
&& SemanticModel.GetSymbolInfo(invocation, CancellationToken).Symbol is IMethodSymbol method
73-
&& method.Name.Equals(FromResultMethod)
74-
&& SemanticModel.IsEqualType(method.ContainingType, TaskType);
71+
&& _taskAnalysis.IsFromResultInvocation(invocation);
7572
}
7673

7774
private bool IsCompletedTask(ExpressionSyntax expression) {
7875
return expression is MemberAccessExpressionSyntax memberAccess
79-
&& SemanticModel.GetSymbolInfo(memberAccess, CancellationToken).Symbol is IPropertySymbol property
80-
&& property.Name.Equals(CompletedTaskProperty)
81-
&& SemanticModel.IsEqualType(property.ContainingType, TaskType);
76+
&& _taskAnalysis.IsCompletedTaskAccess(memberAccess);
8277
}
8378
}
8479
}

src/ParallelHelper/Util/TaskAnalysis.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class TaskAnalysis {
3636
);
3737

3838
private const string FromResultMethod = "FromResult";
39+
private const string CompletedTaskProperty = "CompletedTask";
3940

4041
private readonly SemanticModel _semanticModel;
4142
private readonly CancellationToken _cancellationToken;
@@ -130,7 +131,16 @@ public bool IsFromResultInvocation(InvocationExpressionSyntax invocation) {
130131
}
131132

132133
/// <summary>
133-
/// CHecks if the given node is a method or function (e.g. lambda) returns a task.
134+
/// Checks if the given member access is accessing <see cref="System.Threading.Tasks.Task.CompletedTask"/>.
135+
/// </summary>
136+
/// <param name="memberAccess">The member access to check.</param>
137+
/// <returns><c>true</c> if it's the member access of CompletedTask, <c>false</c> otherwise.</returns>
138+
public bool IsCompletedTaskAccess(MemberAccessExpressionSyntax memberAccess) {
139+
return IsTaskMemberAccess(memberAccess, CompletedTaskProperty);
140+
}
141+
142+
/// <summary>
143+
/// Checks if the given node is a method or function (e.g. lambda) returns a task.
134144
/// </summary>
135145
/// <param name="node">The node to check.</param>
136146
/// <returns><c>true</c> if the underlying method or function returns a task, <c>false</c> otherwise.</returns>
@@ -155,6 +165,12 @@ private bool IsTaskMethodInvocation(InvocationExpressionSyntax invocation, strin
155165
&& IsTaskMember(method, taskMember);
156166
}
157167

168+
private bool IsTaskMemberAccess(MemberAccessExpressionSyntax invocation, string taskMember) {
169+
var symbol = _semanticModel.GetSymbolInfo(invocation, _cancellationToken).Symbol;
170+
return symbol != null
171+
&& IsTaskMember(symbol, taskMember);
172+
}
173+
158174
private bool IsTaskMember(ISymbol member, string taskMember) {
159175
return member.Name == taskMember && IsTaskType(member.ContainingType);
160176
}

0 commit comments

Comments
 (0)