diff --git a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs index e2ac0e523..0fb1ef097 100644 --- a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs +++ b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs @@ -2,6 +2,6 @@ namespace Coverlet.Core.Attributes { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)] public sealed class ExcludeFromCoverageAttribute : Attribute { } } \ No newline at end of file diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 5319da9a2..f870ef042 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -165,6 +165,9 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) return false; } + public static bool IsLocalMethod(string method) + => new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method); + public static string[] GetExcludedFiles(string[] rules) { const string RELATIVE_KEY = nameof(RELATIVE_KEY); diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index dfc853204..fdbd56c97 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -60,11 +60,19 @@ private void InstrumentModule() { resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; + using (var module = ModuleDefinition.ReadModule(stream, parameters)) { - foreach (var type in module.GetTypes()) + var types = module.GetTypes(); + foreach (var type in types) { - InstrumentType(type); + TypeDefinition actualType = type; + if (type.FullName.Contains("/")) + actualType = types.FirstOrDefault(t => t.FullName == type.FullName.Split('/')[0]); + + if (!actualType.CustomAttributes.Any(IsExcludeAttribute) + && !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters)) + InstrumentType(type); } module.Write(stream); @@ -74,13 +82,14 @@ private void InstrumentModule() private void InstrumentType(TypeDefinition type) { - if (type.CustomAttributes.Any(IsExcludeAttribute) - || InstrumentationHelper.IsTypeExcluded(_module, type.FullName, _filters)) - return; - - foreach (var method in type.Methods) + var methods = type.GetMethods(); + foreach (var method in methods) { - if (!method.CustomAttributes.Any(IsExcludeAttribute)) + MethodDefinition actualMethod = method; + if (InstrumentationHelper.IsLocalMethod(method.Name)) + actualMethod = methods.FirstOrDefault(m => m.Name == method.Name.Split('>')[0].Substring(1)) ?? method; + + if (!actualMethod.CustomAttributes.Any(IsExcludeAttribute)) InstrumentMethod(method); } } @@ -116,7 +125,7 @@ private void InstrumentIL(MethodDefinition method) var instruction = processor.Body.Instructions[index]; var sequencePoint = method.DebugInformation.GetSequencePoint(instruction); var targetedBranchPoints = branchPoints.Where(p => p.EndOffset == instruction.Offset); - + if (sequencePoint != null && !sequencePoint.IsHidden) { var target = AddInstrumentationCode(method, processor, instruction, sequencePoint); @@ -139,7 +148,7 @@ private void InstrumentIL(MethodDefinition method) */ if (_branchTarget.StartLine == -1 || _branchTarget.Document == null) continue; - + var target = AddInstrumentationCode(method, processor, instruction, _branchTarget); foreach (var _instruction in processor.Body.Instructions) ReplaceInstructionTarget(_instruction, instruction, target);