coverlet-coverage / coverlet

Cross platform code coverage for .NET
MIT License
2.93k stars 385 forks source link

[BUG] ExcludeByFile filter does not exclude type constructor (.cctor) with partial classes #1596

Open Alexandr-Mihalciuc opened 5 months ago

Alexandr-Mihalciuc commented 5 months ago

Describe the bug I would like to exclude source generated code from my coverage report. I use following filter in my runsettingsfile:

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat code coverage">
        <Configuration>
          <Format>cobertura</Format>
          <ExcludeByFile>**/*.g.cs</ExcludeByFile>         
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

My solution uses LoggerMessage source generators [[https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator]]. When I define a class with static field initialization code and I also use LoggerMessageAttribute to generate logger methods, then static init code will be spread across two partial class definition files. One is from my own code and another one generated by the LoggerMessage. When I run coverage report I get coverage which includes the source code generated file:

<class name="<MyTypeName>" filename="<MyNamespace>\Microsoft.Extensions.Logging.Generators\Microsoft.Extensions.Logging.Generators.LoggerMessageGenerator\LoggerMessage.g.cs" line-rate="1" branch-rate="1" complexity="1">
  <methods>
    <method name=".cctor" signature="()" line-rate="1" branch-rate="1" complexity="1">

Notice that I have specifically excluded code coverage for **/*.g.cs files.

To Reproduce

  1. Create a partial class and initialize some static fields across two files:
    
    // file-name: DemoTheBug.cs
    internal partial class DemoTheBug
    {
    private static readonly TimeSpan Field1= TimeSpan.FromMinutes(5);
    }

//file-name: DemoTheBug.g.cs internal partial class DemoTheBug { private static readonly TimeSpan Field2= TimeSpan.FromMinutes(5); }


Use the `<ExcludeByFile>**/*.g.cs</ExcludeByFile>` filter and generate code coverage report.         

**Expected behavior**
DemoTheBug.g.cs file is not listed in code coverage report.

**Actual behavior**
DemoTheBug.g.cs file is listed in code coverage report

**Configuration (please complete the following information):**
Please provide more information on your .NET configuration:
    * Which coverlet package and version was used? 6.0.0
    * Which version of .NET is the code running on? .net 8
    * What OS and version, and what distro if applicable? Windows.
    * What is the architecture (x64, x86, ARM, ARM64)? x64
    * Do you know whether it is specific to that configuration? The issue is not specific to the config.

**Additional context**

The issues appears in `Coverlet.Core.Instrumentation.Instrumenter` when instrumenting cctor:

private void InstrumentMethod(MethodDefinition method) { string sourceFile = method.DebugInformation.SequencePoints.Select(s => _sourceRootTranslator.ResolveFilePath(s.Document.Url)).FirstOrDefault();

if (string.IsNullOrEmpty(sourceFile)) return;

if (!string.IsNullOrEmpty(sourceFile) && _excludedFilesHelper.Exclude(sourceFile)) { if (!(_excludedSourceFiles ??= new List()).Contains(sourceFile)) { _excludedSourceFiles.Add(sourceFile); } return; }


Notice that `_sourceRootTranslator.ResolveFilePath(s.Document.Url)).FirstOrDefault();` usually refers to my own source code file, and the `_excludedFilesHelper` is not going to exclude it, so we will instrument the code by eventually calling:

private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, SequencePoint sequencePoint) { if (!_result.Documents.TryGetValue(_sourceRootTranslator.ResolveFilePath(sequencePoint.Document.Url), out Document document)) { document = new Document { Path = _sourceRootTranslator.ResolveFilePath(sequencePoint.Document.Url) }; document.Index = _result.Documents.Count; _result.Documents.Add(document.Path, document); }


 Notice that ` _result.Documents.Add(document.Path, document);` can point to source code generated file and it will be added to the report, so the `ExcludeByFile` gets ignored.
Alexandr-Mihalciuc commented 5 months ago

I've tried to raise a PR, but I am getting "permissions denied"

github-actions[bot] commented 2 months ago

This issue is stale because it has been open for 3 months with no activity.