From e96a5c88a76f5ef168ea600abdca4f98e5304280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Sat, 26 Feb 2022 03:31:57 +0100 Subject: [PATCH] imp + test + changelog --- Documentation/Changelog.md | 5 ++++ .../Instrumentation/Instrumenter.cs | 20 ++++++++++++++ ...erageTests.ExcludeFromCoverageAttribute.cs | 27 +++++++++++++++++++ ...mentation.ExcludeFromCoverage.Issue1302.cs | 18 +++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 test/coverlet.core.tests/Samples/Instrumentation.ExcludeFromCoverage.Issue1302.cs diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 5c551ceec..06daa9310 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 +-ExcludeFromCodeCoverage attribute on local functions ignores lambda expression [#1302](https://github.com/coverlet-coverage/coverlet/issues/1302) + ## Release date 2022-02-06 ### Packages coverlet.msbuild 3.1.2 diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 51b18c07c..42a1a1835 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -45,6 +45,7 @@ internal class Instrumenter private List _excludedSourceFiles; private List _branchesInCompiledGeneratedClass; private List<(MethodDefinition, int)> _excludedMethods; + private List _excludedLambdaMethods; private List _excludedCompilerGeneratedTypes; private readonly string[] _doesNotReturnAttributes; private ReachabilityHelper _reachabilityHelper; @@ -500,12 +501,18 @@ private void InstrumentType(TypeDefinition type) continue; } + if (_excludedLambdaMethods != null && _excludedLambdaMethods.Contains(method.FullName)) + { + continue; + } + if (!customAttributes.Any(IsExcludeAttribute)) { InstrumentMethod(method); } else { + (_excludedLambdaMethods ??= new List()).AddRange(CollectLambdaMethodsInsideLocalFunction(method)); (_excludedMethods ??= new List<(MethodDefinition, int)>()).Add((method, ordinal)); } } @@ -842,6 +849,19 @@ internal bool IsSynthesizedNameOf(string name, string methodName, int methodOrdi (name.IndexOf($"<{methodName}>g__") != -1 && name.IndexOf($"|{methodOrdinal}_") != -1); } + private static IEnumerable CollectLambdaMethodsInsideLocalFunction(MethodDefinition methodDefinition) + { + if (!methodDefinition.Name.Contains(">g__")) yield break; + + foreach (Instruction instruction in methodDefinition.Body.Instructions.ToList()) + { + if (instruction.OpCode == OpCodes.Ldftn && instruction.Operand is MethodReference mr && mr.Name.Contains(">b__")) + { + yield return mr.FullName; + } + } + } + /// /// A custom importer created specifically to allow the instrumentation of System.Private.CoreLib by /// removing the external references to netstandard that are generated when instrumenting a typical diff --git a/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs b/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs index e989c3838..bead5b913 100644 --- a/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs +++ b/test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs @@ -277,5 +277,32 @@ public void ExcludeFromCodeCoverageAutoGeneratedGet() File.Delete(path); } } + + [Fact] + public void ExcludeFromCodeCoverage_Issue1302() + { + string path = Path.GetTempFileName(); + try + { + FunctionExecutor.Run(async (string[] pathSerialize) => + { + CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run(instance => + { + instance.Run(); + return Task.CompletedTask; + }, persistPrepareResultToFile: pathSerialize[0]); + + return 0; + }, new string[] { path }); + + TestInstrumentationHelper.GetCoverageResult(path) + .Document("Instrumentation.ExcludeFromCoverage.Issue1302.cs") + .AssertNonInstrumentedLines(BuildConfiguration.Debug, 10, 13); + } + finally + { + File.Delete(path); + } + } } } diff --git a/test/coverlet.core.tests/Samples/Instrumentation.ExcludeFromCoverage.Issue1302.cs b/test/coverlet.core.tests/Samples/Instrumentation.ExcludeFromCoverage.Issue1302.cs new file mode 100644 index 000000000..8bf8dd2d4 --- /dev/null +++ b/test/coverlet.core.tests/Samples/Instrumentation.ExcludeFromCoverage.Issue1302.cs @@ -0,0 +1,18 @@ +using System; + +namespace Coverlet.Core.Samples.Tests +{ + public class Issue1302 + { + public void Run() + { + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + static Func LocalFunction() + { + return myString => myString.Length == 10; + } + + LocalFunction(); + } + } +}