Skip to content

Commit

Permalink
xunit/xunit#2780: Do not trigger xUnit1031 inside of ContinueWith
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Sep 27, 2023
1 parent 2c5f100 commit 560e267
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,23 @@ public void TestMethod() {

await Verify.VerifyAnalyzer(source);
}

[Fact]
public async void SuccessCase_GetAwaiterGetResult_InContinueWithLambda()
{
var source = @"
using System.Threading.Tasks;
using Xunit;
public class TestClass {
[Fact]
public void TestMethod() {
Task.CompletedTask.ContinueWith(x => x.GetAwaiter().GetResult());
}
}";

await Verify.VerifyAnalyzer(source);
}
}

public class Task_Generic
Expand All @@ -135,6 +152,23 @@ public void TestMethod() {
await Verify.VerifyAnalyzer(source);
}

[Fact]
public async void SuccessCase_Result_InContinueWithLambda()
{
var source = @"
using System.Threading.Tasks;
using Xunit;
public class TestClass {
[Fact]
public void TestMethod() {
var _ = Task.FromResult(42).ContinueWith(x => x.Result);
}
}";

await Verify.VerifyAnalyzer(source);
}

[Fact]
public async void FailureCase_GetAwaiterGetResult()
{
Expand All @@ -151,6 +185,23 @@ public void TestMethod() {

await Verify.VerifyAnalyzer(source);
}

[Fact]
public async void SuccessCase_GetAwaiterGetResult_InContinueWithLambda()
{
var source = @"
using System.Threading.Tasks;
using Xunit;
public class TestClass {
[Fact]
public void TestMethod() {
var _ = Task.FromResult(42).ContinueWith(x => x.GetAwaiter().GetResult());
}
}";

await Verify.VerifyAnalyzer(source);
}
}

public class ValueTask_NonGeneric
Expand Down
30 changes: 30 additions & 0 deletions src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class DoNotUseBlockingTaskOperations : XunitDiagnosticAnalyzer
// These are on both Task<T> and ValueTask<T>
nameof(Task<int>.Result),
};
static readonly string[] continueWith = new[]
{
nameof(Task<int>.ContinueWith),
};

public DoNotUseBlockingTaskOperations() :
base(Descriptors.X1031_DoNotUseBlockingTaskOperations)
Expand Down Expand Up @@ -66,6 +70,9 @@ public override void AnalyzeCompilation(
if (!foundSymbol)
return;

if (WrappedInContinueWith(invocation, taskType, xunitContext))
return;

// Should have two child nodes: "(some other code).(target method)" and the arguments
var invocationChildren = invocation.Syntax.ChildNodes().ToList();
if (invocationChildren.Count != 2)
Expand Down Expand Up @@ -95,6 +102,9 @@ public override void AnalyzeCompilation(
if (!foundSymbol)
return;

if (WrappedInContinueWith(reference, taskType, xunitContext))
return;

// Should have two child nodes: "(some other code)" and "(property name)"
var propertyChildren = reference.Syntax.ChildNodes().ToList();
if (propertyChildren.Count != 2)
Expand Down Expand Up @@ -131,4 +141,24 @@ static bool FindSymbol(
// Only trigger when you're inside a test method
return operation.IsInTestMethod(xunitContext);
}

static bool WrappedInContinueWith(
IOperation? operation,
INamedTypeSymbol? taskType,
XunitContext xunitContext)
{
if (taskType is null)
return false;

for (; operation != null; operation = operation.Parent)
{
if (operation is not IInvocationOperation invocation)
continue;

if (FindSymbol(invocation.TargetMethod, invocation, taskType, continueWith, xunitContext))
return true;
}

return false;
}
}

0 comments on commit 560e267

Please sign in to comment.