Skip to content

Commit e96a5c8

Browse files
committed
imp + test + changelog
1 parent 2bb04f2 commit e96a5c8

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

Documentation/Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
9+
### Fixed
10+
-ExcludeFromCodeCoverage attribute on local functions ignores lambda expression [#1302](https://github.com/coverlet-coverage/coverlet/issues/1302)
11+
712
## Release date 2022-02-06
813
### Packages
914
coverlet.msbuild 3.1.2

src/coverlet.core/Instrumentation/Instrumenter.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ internal class Instrumenter
4545
private List<string> _excludedSourceFiles;
4646
private List<string> _branchesInCompiledGeneratedClass;
4747
private List<(MethodDefinition, int)> _excludedMethods;
48+
private List<string> _excludedLambdaMethods;
4849
private List<string> _excludedCompilerGeneratedTypes;
4950
private readonly string[] _doesNotReturnAttributes;
5051
private ReachabilityHelper _reachabilityHelper;
@@ -500,12 +501,18 @@ private void InstrumentType(TypeDefinition type)
500501
continue;
501502
}
502503

504+
if (_excludedLambdaMethods != null && _excludedLambdaMethods.Contains(method.FullName))
505+
{
506+
continue;
507+
}
508+
503509
if (!customAttributes.Any(IsExcludeAttribute))
504510
{
505511
InstrumentMethod(method);
506512
}
507513
else
508514
{
515+
(_excludedLambdaMethods ??= new List<string>()).AddRange(CollectLambdaMethodsInsideLocalFunction(method));
509516
(_excludedMethods ??= new List<(MethodDefinition, int)>()).Add((method, ordinal));
510517
}
511518
}
@@ -842,6 +849,19 @@ internal bool IsSynthesizedNameOf(string name, string methodName, int methodOrdi
842849
(name.IndexOf($"<{methodName}>g__") != -1 && name.IndexOf($"|{methodOrdinal}_") != -1);
843850
}
844851

852+
private static IEnumerable<string> CollectLambdaMethodsInsideLocalFunction(MethodDefinition methodDefinition)
853+
{
854+
if (!methodDefinition.Name.Contains(">g__")) yield break;
855+
856+
foreach (Instruction instruction in methodDefinition.Body.Instructions.ToList())
857+
{
858+
if (instruction.OpCode == OpCodes.Ldftn && instruction.Operand is MethodReference mr && mr.Name.Contains(">b__"))
859+
{
860+
yield return mr.FullName;
861+
}
862+
}
863+
}
864+
845865
/// <summary>
846866
/// A custom importer created specifically to allow the instrumentation of System.Private.CoreLib by
847867
/// removing the external references to netstandard that are generated when instrumenting a typical

test/coverlet.core.tests/Coverage/CoverageTests.ExcludeFromCoverageAttribute.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,5 +277,32 @@ public void ExcludeFromCodeCoverageAutoGeneratedGet()
277277
File.Delete(path);
278278
}
279279
}
280+
281+
[Fact]
282+
public void ExcludeFromCodeCoverage_Issue1302()
283+
{
284+
string path = Path.GetTempFileName();
285+
try
286+
{
287+
FunctionExecutor.Run(async (string[] pathSerialize) =>
288+
{
289+
CoveragePrepareResult coveragePrepareResult = await TestInstrumentationHelper.Run<Issue1302>(instance =>
290+
{
291+
instance.Run();
292+
return Task.CompletedTask;
293+
}, persistPrepareResultToFile: pathSerialize[0]);
294+
295+
return 0;
296+
}, new string[] { path });
297+
298+
TestInstrumentationHelper.GetCoverageResult(path)
299+
.Document("Instrumentation.ExcludeFromCoverage.Issue1302.cs")
300+
.AssertNonInstrumentedLines(BuildConfiguration.Debug, 10, 13);
301+
}
302+
finally
303+
{
304+
File.Delete(path);
305+
}
306+
}
280307
}
281308
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
3+
namespace Coverlet.Core.Samples.Tests
4+
{
5+
public class Issue1302
6+
{
7+
public void Run()
8+
{
9+
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
10+
static Func<string, bool> LocalFunction()
11+
{
12+
return myString => myString.Length == 10;
13+
}
14+
15+
LocalFunction();
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)