diff --git a/Source/DafnyCore/CoverageReport/CoverageLabel.cs b/Source/DafnyCore/CoverageReport/CoverageLabel.cs index 641c1a9d95d..a8a53d63933 100644 --- a/Source/DafnyCore/CoverageReport/CoverageLabel.cs +++ b/Source/DafnyCore/CoverageReport/CoverageLabel.cs @@ -4,6 +4,7 @@ namespace Microsoft.Dafny; public enum CoverageLabel { None, + NotApplicable, FullyCovered, NotCovered, PartiallyCovered, @@ -15,6 +16,9 @@ public static class CoverageLabelExtension { /// Combine coverage labels. E.g. FullyCovered + NotCovered = PartiallyCovered /// public static CoverageLabel Combine(CoverageLabel one, CoverageLabel two) { + if (one == CoverageLabel.NotApplicable || two == CoverageLabel.NotApplicable) { + return CoverageLabel.NotApplicable; + } if (one == CoverageLabel.None) { return two; } diff --git a/Source/DafnyCore/CoverageReport/CoverageReport.cs b/Source/DafnyCore/CoverageReport/CoverageReport.cs index 22439517b8e..3593227fc5b 100644 --- a/Source/DafnyCore/CoverageReport/CoverageReport.cs +++ b/Source/DafnyCore/CoverageReport/CoverageReport.cs @@ -59,11 +59,13 @@ public void RegisterFiles(Program program) { } public void RegisterFile(Uri uri) { - labelsByFile[uri.LocalPath] = new List(); + if (!labelsByFile.ContainsKey(uri.LocalPath)) { + labelsByFile[uri.LocalPath] = new List(); + } } private void RegisterFiles(Node astNode) { - if (astNode.StartToken.ActualFilename != null) { + if (astNode.StartToken.ActualFilename != null && !labelsByFile.ContainsKey(astNode.StartToken.ActualFilename)) { labelsByFile[astNode.StartToken.ActualFilename] = new(); } diff --git a/Source/DafnyCore/Options/CommonOptionBag.cs b/Source/DafnyCore/Options/CommonOptionBag.cs index b4d5d26a99c..b8a108c0e9c 100644 --- a/Source/DafnyCore/Options/CommonOptionBag.cs +++ b/Source/DafnyCore/Options/CommonOptionBag.cs @@ -213,6 +213,10 @@ May slow down verification slightly. "Emit verification coverage report to a given directory, in the same format as a test coverage report.") { ArgumentHelpName = "directory" }; + public static readonly Option NoTimeStampForCoverageReport = new("--no-timestamp-for-coverage-report", + "Write coverage report directly to the specified folder instead of creating a timestamped subdirectory.") { + IsHidden = true + }; public static readonly Option IncludeRuntimeOption = new("--include-runtime", "Include the Dafny runtime as source in the target language."); @@ -475,6 +479,7 @@ void ParsePrintMode(Option option, Boogie.CommandLineParseState ps, WarnContradictoryAssumptions, WarnRedundantAssumptions, VerificationCoverageReport, + NoTimeStampForCoverageReport, DefaultFunctionOpacity ); } diff --git a/Source/DafnyCore/Options/DafnyCommands.cs b/Source/DafnyCore/Options/DafnyCommands.cs index bf8b49b9326..e338b6e3edd 100644 --- a/Source/DafnyCore/Options/DafnyCommands.cs +++ b/Source/DafnyCore/Options/DafnyCommands.cs @@ -42,6 +42,7 @@ static DafnyCommands() { CommonOptionBag.DefaultFunctionOpacity, CommonOptionBag.WarnContradictoryAssumptions, CommonOptionBag.WarnRedundantAssumptions, + CommonOptionBag.NoTimeStampForCoverageReport, CommonOptionBag.VerificationCoverageReport }.ToList(); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index ffe2a56d7e9..c3c9403cb2a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -612,6 +612,9 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, builder.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, stmt.Expr, "assume statement", true)); stmtContext = StmtType.NONE; } + if (options.TestGenOptions.Mode != TestGenerationOptions.Modes.None) { + builder.AddCaptureState(stmt); + } } else if (stmt is ExpectStmt) { AddComment(builder, stmt, "expect statement"); var s = (ExpectStmt)stmt; @@ -639,6 +642,9 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false); builder.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, s.Expr, "assume statement", true, etran.TrAttributes(stmt.Attributes, null))); stmtContext = StmtType.NONE; // done with translating assume stmt. + if (options.TestGenOptions.Mode != TestGenerationOptions.Modes.None) { + builder.AddCaptureState(s); + } } this.fuelContext = FuelSetting.PopFuelContext(); } diff --git a/Source/DafnyDriver/Commands/CoverageReportCommand.cs b/Source/DafnyDriver/Commands/CoverageReportCommand.cs index 63cddabb610..846e71440b4 100644 --- a/Source/DafnyDriver/Commands/CoverageReportCommand.cs +++ b/Source/DafnyDriver/Commands/CoverageReportCommand.cs @@ -4,7 +4,6 @@ #nullable disable using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Invocation; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -13,6 +12,11 @@ namespace Microsoft.Dafny; static class CoverageReportCommand { + public static IEnumerable