Skip to content

Commit

Permalink
Descend into local asynchronous methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
manfred-brands committed Sep 7, 2023
1 parent 81c26ba commit cfe5e9d
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,39 @@ private void SetFields()
RoslynAssert.Suppressed(suppressor, testCode);
}

[TestCase("async Task", "await ", "", "")]
[TestCase("async Task", "await ", "this.", "")]
[TestCase("async Task", "await ", "", ".ConfigureAwait(false)")]
[TestCase("async Task", "await ", "this.", ".ConfigureAwait(false)")]
[TestCase("void", "", "", ".Wait()")]
[TestCase("void", "", "this.", ".Wait()")]
public void FieldAssignedInAsyncCalledMethod(string returnType, string awaitKeyWord, string accessor, string suffix)
{
var testCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@$"
private string ↓field;
[SetUp]
public {returnType} Initialize()
{{
{awaitKeyWord}{accessor}SetFields(){suffix};
}}
[Test]
public void Test()
{{
Assert.That({accessor}field, Is.Not.Null);
}}
private Task SetFields()
{{
{accessor}field = string.Empty;
return Task.CompletedTask;
}}
");

RoslynAssert.Suppressed(suppressor, testCode);
}

[TestCase("SetUp", "")]
[TestCase("OneTimeSetUp", "this.")]
public void FieldAssignedInCalledMethods(string attribute, string prefix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,40 @@ public void TearDownMethod()
RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void FieldConditionallyAssignedInCalledLocalMethod()
{
var testCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@"
private object? ↓field;
private bool initializeField;
public TestClass(bool initializeField) => this.initializeField = initializeField;
[SetUp]
public async Task SetUp()
{{
if (initializeField)
await InitializeField().ConfigureAwait(false);
}}
[TearDown]
public void TearDownMethod()
{{
field = null;
}}
private Task InitializeField()
{{
field = new DummyDisposable();
return Task.CompletedTask;
}}
{DummyDisposable}
");

RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeWhenPropertyBackingFieldIsDisposed(
[Values("field", "Property")] string fieldOrProperty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ private static bool IsAssignedIn(
ExpressionSyntax? expressionStatement,
string fieldOrPropertyName)
{
if (expressionStatement is AwaitExpressionSyntax awaitExpression)
{
expressionStatement = awaitExpression.Expression;
if (expressionStatement is InvocationExpressionSyntax awaitInvocationExpression &&
awaitInvocationExpression.Expression is MemberAccessExpressionSyntax awaitMemberAccessExpression &&
awaitMemberAccessExpression.Name.Identifier.Text == "ConfigureAwait")
{
expressionStatement = awaitMemberAccessExpression.Expression;
}
}

if (expressionStatement is AssignmentExpressionSyntax assignmentExpression)
{
if (assignmentExpression.Left is TupleExpressionSyntax tupleExpression)
Expand All @@ -224,6 +235,13 @@ private static bool IsAssignedIn(
}
else if (expressionStatement is InvocationExpressionSyntax invocationExpression)
{
if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression &&
memberAccessExpression.Expression is InvocationExpressionSyntax awaitedInvocationExpression &&
memberAccessExpression.Name.Identifier.Text == "Wait")
{
invocationExpression = awaitedInvocationExpression;
}

string? identifier = GetIdentifier(invocationExpression.Expression);

if (!string.IsNullOrEmpty(identifier) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ private static void AssignedIn(Parameters parameters, HashSet<string> assignment

private static void AssignedIn(Parameters parameters, HashSet<string> assignments, ExpressionSyntax expression)
{
if (expression is AwaitExpressionSyntax awaitExpression)
{
expression = awaitExpression.Expression;
if (expression is InvocationExpressionSyntax awaitInvocationExpression &&
awaitInvocationExpression.Expression is MemberAccessExpressionSyntax awaitMemberAccessExpression &&
awaitMemberAccessExpression.Name.Identifier.Text == "ConfigureAwait")
{
expression = awaitMemberAccessExpression.Expression;
}
}

if (expression is AssignmentExpressionSyntax assignmentExpression)
{
// We only deal with simple assignments, not tuple or deconstruct
Expand All @@ -241,6 +252,13 @@ private static void AssignedIn(Parameters parameters, HashSet<string> assignment
}
else if (expression is InvocationExpressionSyntax invocationExpression)
{
if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccessExpression &&
memberAccessExpression.Expression is InvocationExpressionSyntax awaitedInvocationExpression &&
memberAccessExpression.Name.Identifier.Text == "Wait")
{
invocationExpression = awaitedInvocationExpression;
}

string? method = GetIdentifier(invocationExpression.Expression);
if (method is not null)
{
Expand Down

0 comments on commit cfe5e9d

Please sign in to comment.