diff --git a/Source/DafnyTestGeneration/CoverageReport/CoverageLabel.cs b/Source/DafnyCore/CoverageReport/CoverageLabel.cs similarity index 84% rename from Source/DafnyTestGeneration/CoverageReport/CoverageLabel.cs rename to Source/DafnyCore/CoverageReport/CoverageLabel.cs index 5e820a6c70f..641c1a9d95d 100644 --- a/Source/DafnyTestGeneration/CoverageReport/CoverageLabel.cs +++ b/Source/DafnyCore/CoverageReport/CoverageLabel.cs @@ -3,9 +3,10 @@ namespace Microsoft.Dafny; public enum CoverageLabel { + None, FullyCovered, NotCovered, - PartiallyCovered + PartiallyCovered, } public static class CoverageLabelExtension { @@ -14,6 +15,12 @@ public static class CoverageLabelExtension { /// Combine coverage labels. E.g. FullyCovered + NotCovered = PartiallyCovered /// public static CoverageLabel Combine(CoverageLabel one, CoverageLabel two) { + if (one == CoverageLabel.None) { + return two; + } + if (two == CoverageLabel.None) { + return one; + } if (one == CoverageLabel.PartiallyCovered || two == CoverageLabel.PartiallyCovered || one != two) { return CoverageLabel.PartiallyCovered; } diff --git a/Source/DafnyTestGeneration/CoverageReport/CoverageReport.cs b/Source/DafnyCore/CoverageReport/CoverageReport.cs similarity index 67% rename from Source/DafnyTestGeneration/CoverageReport/CoverageReport.cs rename to Source/DafnyCore/CoverageReport/CoverageReport.cs index 12dfa5be2f2..22439517b8e 100644 --- a/Source/DafnyTestGeneration/CoverageReport/CoverageReport.cs +++ b/Source/DafnyCore/CoverageReport/CoverageReport.cs @@ -11,7 +11,6 @@ public class CoverageReport { private static int nextUniqueId = 0; - // INVARIANT: CoverageSpans are sorted within each list by the position of the StartToken private readonly Dictionary> labelsByFile; public readonly string Name; // the name to assign to this coverage report public readonly string Units; // the units of coverage (plural). This will be written in the coverage report table. @@ -39,37 +38,12 @@ public CoverageReport(string name, string units, string suffix, Program program) /// /// Assign a coverage label to the code indicated by the range token. - /// If the span intersects with existing coverage information, it will be merged according to - /// CoverageLabelExtension.Combine. /// public void LabelCode(RangeToken span, CoverageLabel label) { Contract.Assert(labelsByFile.ContainsKey(span.ActualFilename)); - if (span.StartToken.CompareTo(span.EndToken) == 0) { - return; - } var labeledFile = labelsByFile[span.ActualFilename]; var coverageSpan = new CoverageSpan(span, label); - int index = labeledFile.BinarySearch(0, labeledFile.Count, coverageSpan, null); - if (index < 0) { - labeledFile.Insert(~index, coverageSpan); - return; - } - var overlappingSpan = labeledFile[index]; - labeledFile.RemoveAt(index); - var combinedLabel = CoverageLabelExtension.Combine(label, overlappingSpan.Label); - switch (overlappingSpan.Span.EndToken.CompareTo(span.EndToken)) { - case 0: - labeledFile.Insert(index, new CoverageSpan(span, combinedLabel)); - break; - case > 0: - labeledFile.Insert(index, new CoverageSpan(span, combinedLabel)); - labeledFile.Insert(index + 1, new CoverageSpan(new RangeToken(span.EndToken, overlappingSpan.Span.EndToken), overlappingSpan.Label)); - break; - default: - labeledFile.Insert(index, new CoverageSpan(overlappingSpan.Span, combinedLabel)); - LabelCode(new RangeToken(overlappingSpan.Span.EndToken, span.EndToken), label); - break; - } + labeledFile.Add(coverageSpan); } public IEnumerable CoverageSpansForFile(string fileName) { @@ -92,8 +66,12 @@ private void RegisterFiles(Node astNode) { if (astNode.StartToken.ActualFilename != null) { labelsByFile[astNode.StartToken.ActualFilename] = new(); } + foreach (var declaration in astNode.Children.OfType()) { RegisterFiles(declaration); } + foreach (var declaration in astNode.Children.OfType()) { + RegisterFiles(declaration); + } } } \ No newline at end of file diff --git a/Source/DafnyTestGeneration/CoverageReport/CoverageSpan.cs b/Source/DafnyCore/CoverageReport/CoverageSpan.cs similarity index 89% rename from Source/DafnyTestGeneration/CoverageReport/CoverageSpan.cs rename to Source/DafnyCore/CoverageReport/CoverageSpan.cs index 9f07fe48be2..a3ae066d427 100644 --- a/Source/DafnyTestGeneration/CoverageReport/CoverageSpan.cs +++ b/Source/DafnyCore/CoverageReport/CoverageSpan.cs @@ -13,7 +13,6 @@ public CoverageSpan(RangeToken span, CoverageLabel label) { Contract.Assert(span.Uri != null); Contract.Assert(span.StartToken != null); Contract.Assert(span.EndToken != null); - Contract.Assert(span.StartToken.CompareTo(span.EndToken) != 0); Span = span; Label = label; } diff --git a/Source/DafnyCore/Options/CommonOptionBag.cs b/Source/DafnyCore/Options/CommonOptionBag.cs index 4320351cf11..b4d5d26a99c 100644 --- a/Source/DafnyCore/Options/CommonOptionBag.cs +++ b/Source/DafnyCore/Options/CommonOptionBag.cs @@ -209,6 +209,10 @@ May slow down verification slightly. May produce spurious warnings.") { IsHidden = true }; + public static readonly Option VerificationCoverageReport = new("--coverage-report", + "Emit verification coverage report to a given directory, in the same format as a test coverage report.") { + ArgumentHelpName = "directory" + }; public static readonly Option IncludeRuntimeOption = new("--include-runtime", "Include the Dafny runtime as source in the target language."); @@ -470,6 +474,7 @@ void ParsePrintMode(Option option, Boogie.CommandLineParseState ps, IncludeRuntimeOption, WarnContradictoryAssumptions, WarnRedundantAssumptions, + VerificationCoverageReport, DefaultFunctionOpacity ); } diff --git a/Source/DafnyCore/Options/DafnyCommands.cs b/Source/DafnyCore/Options/DafnyCommands.cs index c812c991dd1..bf8b49b9326 100644 --- a/Source/DafnyCore/Options/DafnyCommands.cs +++ b/Source/DafnyCore/Options/DafnyCommands.cs @@ -41,7 +41,8 @@ static DafnyCommands() { BoogieOptionBag.VerificationErrorLimit, CommonOptionBag.DefaultFunctionOpacity, CommonOptionBag.WarnContradictoryAssumptions, - CommonOptionBag.WarnRedundantAssumptions + CommonOptionBag.WarnRedundantAssumptions, + CommonOptionBag.VerificationCoverageReport }.ToList(); public static IReadOnlyList