diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 5c551ceec..c04c509ae 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed +-Await foreach has wrong branch coverage when method is generic [#1210](https://github.com/coverlet-coverage/coverlet/issues/1210) + ## Release date 2022-02-06 ### Packages coverlet.msbuild 3.1.2 diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs index a718c82bb..f5f8458b0 100644 --- a/src/coverlet.core/Symbols/CecilSymbolHelper.cs +++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs @@ -496,8 +496,11 @@ static bool CheckForAsyncEnumerator(List instructions, Instruction (instructions[currentIndex - 2].OpCode == OpCodes.Ldarg || instructions[currentIndex - 2].OpCode == OpCodes.Ldarg_0) && instructions[currentIndex - 1].OpCode == OpCodes.Ldfld && - instructions[currentIndex - 1].Operand is FieldDefinition field && - IsCompilerGenerated(field) && field.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator")) + ( + (instructions[currentIndex - 1].Operand is FieldDefinition field && IsCompilerGenerated(field) && field.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator")) || + (instructions[currentIndex - 1].Operand is FieldReference fieldRef && IsCompilerGenerated(fieldRef.Resolve()) && fieldRef.FieldType.FullName.StartsWith("System.Collections.Generic.IAsyncEnumerator")) + ) + ) { return true; } @@ -538,8 +541,10 @@ static bool CheckIfExceptionThrown(List instructions, Instruction i for (int i = currentIndex - 1; i >= minFieldIndex; --i) { if (instructions[i].OpCode == OpCodes.Ldfld && - instructions[i].Operand is FieldDefinition field && - IsCompilerGenerated(field) && field.FieldType.FullName == "System.Object") + ( + (instructions[i].Operand is FieldDefinition field && IsCompilerGenerated(field) && field.FieldType.FullName == "System.Object") || + (instructions[i].Operand is FieldReference fieldRef && IsCompilerGenerated(fieldRef.Resolve()) && fieldRef.FieldType.FullName == "System.Object") + )) { // We expect the call to GetResult() to be no more than four // instructions before the loading of the field's value. diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs index 8e3a2bc91..77450d19b 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.AsyncForeach.cs @@ -24,6 +24,7 @@ public void AsyncForeach() int res = ((ValueTask)instance.SumWithATwist(AsyncEnumerable.Range(1, 5))).GetAwaiter().GetResult(); res += ((ValueTask)instance.Sum(AsyncEnumerable.Range(1, 3))).GetAwaiter().GetResult(); res += ((ValueTask)instance.SumEmpty()).GetAwaiter().GetResult(); + ((ValueTask)instance.GenericAsyncForeach(AsyncEnumerable.Range(1, 3))).GetAwaiter().GetResult(); return Task.CompletedTask; }, persistPrepareResultToFile: pathSerialize[0]); @@ -43,7 +44,9 @@ public void AsyncForeach() // Sum(IAsyncEnumerable) (34, 1), (35, 1), (37, 9), (38, 3), (39, 3), (40, 3), (42, 1), (43, 1), // SumEmpty() - (47, 1), (48, 1), (50, 3), (51, 0), (52, 0), (53, 0), (55, 1), (56, 1) + (47, 1), (48, 1), (50, 3), (51, 0), (52, 0), (53, 0), (55, 1), (56, 1), + // GenericAsyncForeach + (59,1), (60, 9), (61, 3), (62, 3), (63, 3), (64, 1) ) .AssertBranchesCovered(BuildConfiguration.Debug, // SumWithATwist(IAsyncEnumerable) @@ -53,9 +56,10 @@ public void AsyncForeach() // SumEmpty() // If we never entered the loop, that's a branch not taken, which is // what we want to see. - (50, 0, 1), (50, 1, 0) + (50, 0, 1), (50, 1, 0), + (60, 0, 1), (60, 1, 3) ) - .ExpectedTotalNumberOfBranches(BuildConfiguration.Debug, 4); + .ExpectedTotalNumberOfBranches(BuildConfiguration.Debug, 5); } finally { diff --git a/test/coverlet.core.tests/Samples/Instrumentation.AsyncForeach.cs b/test/coverlet.core.tests/Samples/Instrumentation.AsyncForeach.cs index 961f9df31..d8765cfd2 100644 --- a/test/coverlet.core.tests/Samples/Instrumentation.AsyncForeach.cs +++ b/test/coverlet.core.tests/Samples/Instrumentation.AsyncForeach.cs @@ -54,5 +54,13 @@ async public ValueTask SumEmpty() return sum; } + + public async ValueTask GenericAsyncForeach(IAsyncEnumerable ints) + { + await foreach (int obj in ints) + { + await Task.Delay(1); + } + } } }