From b13d87cdf925291d0ed0f5a7e805ac244ffa0679 Mon Sep 17 00:00:00 2001 From: MartyIX <203266+MartyIX@users.noreply.github.com> Date: Sun, 7 Jan 2024 01:50:45 +0100 Subject: [PATCH] xUnit1030/xUnit1031 should not trigger inside local functions (#178) --- .../DoNotUseBlockingTaskOperationsTests.cs | 51 +++++++++++++++---- .../X1000/DoNotUseConfigureAwaitTests.cs | 43 ++++++++++++++++ .../X1000/DoNotUseBlockingTaskOperations.cs | 8 +-- .../X1000/DoNotUseConfigureAwait.cs | 4 +- 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseBlockingTaskOperationsTests.cs b/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseBlockingTaskOperationsTests.cs index db1d5278..d51487c3 100644 --- a/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseBlockingTaskOperationsTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseBlockingTaskOperationsTests.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; @@ -35,10 +36,13 @@ public class TestClass { public void TestMethod() { default(IValueTaskSource).[|GetResult(0)|]; Action _ = vts => vts.GetResult(0); + void LocalFunction() { + default(IValueTaskSource).GetResult(0); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } @@ -57,10 +61,13 @@ public class TestClass { public void TestMethod() { default(IValueTaskSource).[|GetResult(0)|]; Func, int> _ = vts => vts.GetResult(0); + void LocalFunction() { + default(IValueTaskSource).GetResult(0); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } @@ -81,10 +88,13 @@ public class TestClass { public void TestMethod() { Task.Delay(1).[|Wait()|]; Action _ = t => t.Wait(); + void LocalFunction() { + Task.Delay(1).Wait(); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] @@ -211,10 +221,13 @@ public class TestClass {{ public void TestMethod() {{ Task.[|{waitMethod}(Task.Delay(1))|]; Action _ = t => Task.{waitMethod}(t); + void LocalFunction() {{ + Task.{waitMethod}(Task.Delay(1)); + }} }} }}"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Theory] @@ -351,10 +364,13 @@ public class TestClass { public void TestMethod() { Task.CompletedTask.GetAwaiter().[|GetResult()|]; Action _ = t => t.GetAwaiter().GetResult(); + void LocalFunction() { + Task.CompletedTask.GetAwaiter().GetResult(); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] @@ -482,10 +498,13 @@ public class TestClass { public void TestMethod() { var _ = Task.FromResult(42).[|Result|]; Func, int> _2 = t => t.Result; + void LocalFunction() { + var _3 = Task.FromResult(42).Result; + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] @@ -612,10 +631,13 @@ public class TestClass { public void TestMethod() { var _ = Task.FromResult(42).GetAwaiter().[|GetResult()|]; Func, int> _2 = t => t.GetAwaiter().GetResult(); + void LocalFunction() { + var _3 = Task.FromResult(42).GetAwaiter().GetResult(); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] @@ -743,10 +765,13 @@ public class TestClass { public void TestMethod() { default(ValueTask).GetAwaiter().[|GetResult()|]; Action _ = vt => vt.GetAwaiter().GetResult(); + void LocalFunction() { + default(ValueTask).GetAwaiter().GetResult(); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } @@ -765,10 +790,13 @@ public class TestClass { public void TestMethod() { var _ = new ValueTask(42).[|Result|]; Func, int> _2 = vt => vt.Result; + void LocalFunction() { + var _3 = new ValueTask(42).Result; + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } [Fact] @@ -784,10 +812,13 @@ public class TestClass { public void TestMethod() { var _ = new ValueTask(42).GetAwaiter().[|GetResult()|]; Func, int> _2 = vt => vt.GetAwaiter().GetResult(); + void LocalFunction() { + var _3 = new ValueTask(42).GetAwaiter().GetResult(); + } } }"; - await Verify.VerifyAnalyzer(source); + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); } } } diff --git a/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseConfigureAwaitTests.cs b/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseConfigureAwaitTests.cs index 162ff34a..bf10732a 100644 --- a/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseConfigureAwaitTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/X1000/DoNotUseConfigureAwaitTests.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis.CSharp; using Xunit; using Verify = CSharpVerifier; @@ -85,6 +86,27 @@ public async Task TestMethod() {{ await Verify.VerifyAnalyzer(source); } + [Theory] + [MemberData(nameof(InvalidValues))] + public async void InvalidValue_InsideLocalFunction_DoesNotTrigger(string argumentValue) + { + var source = @$" +using System.Threading.Tasks; +using Xunit; + +public class TestClass {{ + [Fact] + public async Task TestMethod() {{ + var booleanVar = true; + async Task AssertEventStateAsync() {{ + await Task.Delay(1).ConfigureAwait({argumentValue}); + }} + }} +}}"; + + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); + } + [Theory] [MemberData(nameof(InvalidValues))] public async void InvalidValue_TaskWithAwait_Triggers(string argumentValue) @@ -281,6 +303,27 @@ public async Task TestMethod() {{ await Verify.VerifyAnalyzer(source); } + [Theory] + [MemberData(nameof(InvalidValues))] + public async void InvalidValue_InsideLocalFunction_DoesNotTrigger(string argumentValue) + { + var source = @$" +using System.Threading.Tasks; +using Xunit; + +public class TestClass {{ + [Fact] + public async Task TestMethod() {{ + var enumVar = ConfigureAwaitOptions.ContinueOnCapturedContext; + async Task AssertEventStateAsync() {{ + await Task.Delay(1).ConfigureAwait({argumentValue}); + }} + }} +}}"; + + await Verify.VerifyAnalyzer(LanguageVersion.CSharp7, source); + } + [Theory] [MemberData(nameof(InvalidValues))] public async void InvalidValue_TaskWithAwait_Triggers(string enumValue) diff --git a/src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs b/src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs index 63bfce3b..26740862 100644 --- a/src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs +++ b/src/xunit.analyzers/X1000/DoNotUseBlockingTaskOperations.cs @@ -86,9 +86,9 @@ public override void AnalyzeCompilation( if (!foundSymbol) return; - // Ignore anything inside a lambda expression + // Ignore anything inside a lambda expression or a local function for (var current = context.Operation; current is not null; current = current.Parent) - if (current is IAnonymousFunctionOperation) + if (current is IAnonymousFunctionOperation || current is ILocalFunctionOperation) return; var symbolsForSearch = default(IEnumerable); @@ -158,9 +158,9 @@ invocation.Arguments[0].Value is IArrayCreationOperation arrayCreationOperation if (!foundSymbol) return; - // Ignore anything inside a lambda expression + // Ignore anything inside a lambda expression or a local function for (var current = context.Operation; current is not null; current = current.Parent) - if (current is IAnonymousFunctionOperation) + if (current is IAnonymousFunctionOperation || current is ILocalFunctionOperation) return; if (foundSymbolName == nameof(Task.Result) && diff --git a/src/xunit.analyzers/X1000/DoNotUseConfigureAwait.cs b/src/xunit.analyzers/X1000/DoNotUseConfigureAwait.cs index 817a187c..88d52e2c 100644 --- a/src/xunit.analyzers/X1000/DoNotUseConfigureAwait.cs +++ b/src/xunit.analyzers/X1000/DoNotUseConfigureAwait.cs @@ -63,9 +63,9 @@ public override void AnalyzeCompilation( if (!invocation.IsInTestMethod(xunitContext)) return; - // Ignore anything inside a lambda expression + // Ignore anything inside a lambda expression or a local function for (var current = context.Operation; current is not null; current = current.Parent) - if (current is IAnonymousFunctionOperation) + if (current is IAnonymousFunctionOperation || current is ILocalFunctionOperation) return; // invocation should be two nodes: "(some other code).ConfigureAwait" and the arguments (like "(false)")