Skip to content

Commit 738351e

Browse files
authored
List Subcommand Improvements (#3729)
Improvements to the list subcommand. - Print the location of the JSON file for `--format=json` (#3633) - Use `CommonArgs` instead of `VerificationArgs` (#3632) - Add a Markdown option. The pretty table is nice for smaller packages, but as we progress with the standard library verification effort, it's too massive to be easily readable. In this case, it's better to print a markdown table and then just read it in a markdown editor. (Once this PR is merged, the [kani list workflow step](model-checking/verify-rust-std#146) will use the markdown option). Resolves #3632, #3633 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent 51958f0 commit 738351e

32 files changed

+478
-199
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ dependencies = [
826826
"strum_macros",
827827
"tempfile",
828828
"time",
829+
"to_markdown_table",
829830
"tokio",
830831
"toml",
831832
"tracing",
@@ -1753,6 +1754,15 @@ dependencies = [
17531754
"time-core",
17541755
]
17551756

1757+
[[package]]
1758+
name = "to_markdown_table"
1759+
version = "0.1.5"
1760+
source = "registry+https://github.com/rust-lang/crates.io-index"
1761+
checksum = "8450ade61b78735ed7811cc14639462723d87a6cd748a41e7bfde554ac5033dd"
1762+
dependencies = [
1763+
"thiserror",
1764+
]
1765+
17561766
[[package]]
17571767
name = "tokio"
17581768
version = "1.41.1"

kani-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ cargo_metadata = "0.18.0"
1717
anyhow = "1"
1818
console = "0.15.1"
1919
once_cell = "1.19.0"
20+
to_markdown_table = "0.1.0"
2021
serde = { version = "1", features = ["derive"] }
2122
serde_json = { version = "1", features = ["preserve_order"] }
2223
clap = { version = "4.4.11", features = ["derive"] }

kani-driver/src/args/list_args.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@
44
55
use std::path::PathBuf;
66

7-
use crate::args::ValidateArgs;
7+
use crate::args::{CommonArgs, ValidateArgs};
88
use clap::{Error, Parser, ValueEnum, error::ErrorKind};
99
use kani_metadata::UnstableFeature;
1010

11-
use super::VerificationArgs;
12-
1311
/// List information relevant to verification
1412
#[derive(Debug, Parser)]
1513
pub struct CargoListArgs {
1614
#[command(flatten)]
17-
pub verify_opts: VerificationArgs,
15+
pub common_args: CommonArgs,
1816

1917
/// Output format
2018
#[clap(long, default_value = "pretty")]
@@ -32,7 +30,7 @@ pub struct StandaloneListArgs {
3230
pub crate_name: Option<String>,
3331

3432
#[command(flatten)]
35-
pub verify_opts: VerificationArgs,
33+
pub common_args: CommonArgs,
3634

3735
/// Output format
3836
#[clap(long, default_value = "pretty")]
@@ -44,20 +42,22 @@ pub struct StandaloneListArgs {
4442
pub std: bool,
4543
}
4644

47-
/// Message formats available for the subcommand.
45+
/// Output formats available for the subcommand.
4846
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, strum_macros::Display)]
4947
#[strum(serialize_all = "kebab-case")]
5048
pub enum Format {
51-
/// Print diagnostic messages in a user friendly format.
49+
/// Print output in human-readable format.
5250
Pretty,
53-
/// Print diagnostic messages in JSON format.
51+
/// Write output to a Markdown file.
52+
Markdown,
53+
/// Write output to a JSON file.
5454
Json,
5555
}
5656

5757
impl ValidateArgs for CargoListArgs {
5858
fn validate(&self) -> Result<(), Error> {
59-
self.verify_opts.validate()?;
60-
if !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::List) {
59+
self.common_args.validate()?;
60+
if !self.common_args.unstable_features.contains(UnstableFeature::List) {
6161
return Err(Error::raw(
6262
ErrorKind::MissingRequiredArgument,
6363
"The `list` subcommand is unstable and requires -Z list",
@@ -70,8 +70,8 @@ impl ValidateArgs for CargoListArgs {
7070

7171
impl ValidateArgs for StandaloneListArgs {
7272
fn validate(&self) -> Result<(), Error> {
73-
self.verify_opts.validate()?;
74-
if !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::List) {
73+
self.common_args.validate()?;
74+
if !self.common_args.unstable_features.contains(UnstableFeature::List) {
7575
return Err(Error::raw(
7676
ErrorKind::MissingRequiredArgument,
7777
"The `list` subcommand is unstable and requires -Z list",

kani-driver/src/args/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ pub enum StandaloneSubcommand {
143143
Playback(Box<playback_args::KaniPlaybackArgs>),
144144
/// Verify the rust standard library.
145145
VerifyStd(Box<std_args::VerifyStdArgs>),
146-
/// Execute the list subcommand
146+
/// List contracts and harnesses.
147147
List(Box<list_args::StandaloneListArgs>),
148148
}
149149

@@ -171,7 +171,7 @@ pub enum CargoKaniSubcommand {
171171
/// Execute concrete playback testcases of a local package.
172172
Playback(Box<playback_args::CargoPlaybackArgs>),
173173

174-
/// List metadata relevant to verification, e.g., harnesses.
174+
/// List contracts and harnesses.
175175
List(Box<list_args::CargoListArgs>),
176176
}
177177

kani-driver/src/list/collect_metadata.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ use std::collections::{BTreeMap, BTreeSet};
66

77
use crate::{
88
InvocationType,
9-
args::list_args::{CargoListArgs, Format, StandaloneListArgs},
10-
list::Totals,
11-
list::output::{json, pretty},
9+
args::{
10+
VerificationArgs,
11+
list_args::{CargoListArgs, StandaloneListArgs},
12+
},
13+
list::ListMetadata,
14+
list::output::output_list_results,
1215
project::{Project, cargo_project, standalone_project, std_project},
1316
session::KaniSession,
1417
version::print_kani_version,
@@ -17,7 +20,7 @@ use anyhow::Result;
1720
use kani_metadata::{ContractedFunction, HarnessKind, KaniMetadata};
1821

1922
/// Process the KaniMetadata output from kani-compiler and output the list subcommand results
20-
fn process_metadata(metadata: Vec<KaniMetadata>, format: Format) -> Result<()> {
23+
fn process_metadata(metadata: Vec<KaniMetadata>) -> ListMetadata {
2124
// We use ordered maps and sets so that the output is in lexicographic order (and consistent across invocations).
2225

2326
// Map each file to a vector of its harnesses.
@@ -26,14 +29,14 @@ fn process_metadata(metadata: Vec<KaniMetadata>, format: Format) -> Result<()> {
2629

2730
let mut contracted_functions: BTreeSet<ContractedFunction> = BTreeSet::new();
2831

29-
let mut total_standard_harnesses = 0;
30-
let mut total_contract_harnesses = 0;
32+
let mut standard_harnesses_count = 0;
33+
let mut contract_harnesses_count = 0;
3134

3235
for kani_meta in metadata {
3336
for harness_meta in kani_meta.proof_harnesses {
3437
match harness_meta.attributes.kind {
3538
HarnessKind::Proof => {
36-
total_standard_harnesses += 1;
39+
standard_harnesses_count += 1;
3740
if let Some(harnesses) = standard_harnesses.get_mut(&harness_meta.original_file)
3841
{
3942
harnesses.insert(harness_meta.pretty_name);
@@ -45,7 +48,7 @@ fn process_metadata(metadata: Vec<KaniMetadata>, format: Format) -> Result<()> {
4548
}
4649
}
4750
HarnessKind::ProofForContract { .. } => {
48-
total_contract_harnesses += 1;
51+
contract_harnesses_count += 1;
4952
if let Some(harnesses) = contract_harnesses.get_mut(&harness_meta.original_file)
5053
{
5154
harnesses.insert(harness_meta.pretty_name);
@@ -63,31 +66,34 @@ fn process_metadata(metadata: Vec<KaniMetadata>, format: Format) -> Result<()> {
6366
contracted_functions.extend(kani_meta.contracted_functions.into_iter());
6467
}
6568

66-
let totals = Totals {
67-
standard_harnesses: total_standard_harnesses,
68-
contract_harnesses: total_contract_harnesses,
69-
contracted_functions: contracted_functions.len(),
70-
};
71-
72-
match format {
73-
Format::Pretty => pretty(standard_harnesses, contracted_functions, totals),
74-
Format::Json => json(standard_harnesses, contract_harnesses, contracted_functions, totals),
69+
ListMetadata {
70+
standard_harnesses,
71+
standard_harnesses_count,
72+
contract_harnesses,
73+
contract_harnesses_count,
74+
contracted_functions,
7575
}
7676
}
7777

78-
pub fn list_cargo(args: CargoListArgs) -> Result<()> {
79-
let session = KaniSession::new(args.verify_opts)?;
80-
if !session.args.common_args.quiet {
78+
pub fn list_cargo(args: CargoListArgs, mut verify_opts: VerificationArgs) -> Result<()> {
79+
let quiet = args.common_args.quiet;
80+
verify_opts.common_args = args.common_args;
81+
let session = KaniSession::new(verify_opts)?;
82+
if !quiet {
8183
print_kani_version(InvocationType::CargoKani(vec![]));
8284
}
8385

8486
let project = cargo_project(&session, false)?;
85-
process_metadata(project.metadata, args.format)
87+
let list_metadata = process_metadata(project.metadata);
88+
89+
output_list_results(list_metadata, args.format, quiet)
8690
}
8791

88-
pub fn list_standalone(args: StandaloneListArgs) -> Result<()> {
89-
let session = KaniSession::new(args.verify_opts)?;
90-
if !session.args.common_args.quiet {
92+
pub fn list_standalone(args: StandaloneListArgs, mut verify_opts: VerificationArgs) -> Result<()> {
93+
let quiet = args.common_args.quiet;
94+
verify_opts.common_args = args.common_args;
95+
let session = KaniSession::new(verify_opts)?;
96+
if !quiet {
9197
print_kani_version(InvocationType::Standalone);
9298
}
9399

@@ -97,5 +103,7 @@ pub fn list_standalone(args: StandaloneListArgs) -> Result<()> {
97103
standalone_project(&args.input, args.crate_name, &session)?
98104
};
99105

100-
process_metadata(project.metadata, args.format)
106+
let list_metadata = process_metadata(project.metadata);
107+
108+
output_list_results(list_metadata, args.format, quiet)
101109
}

kani-driver/src/list/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
// Implements the list subcommand logic
44

5+
use kani_metadata::ContractedFunction;
6+
use std::collections::{BTreeMap, BTreeSet};
7+
58
pub mod collect_metadata;
69
mod output;
710

8-
/// Stores the total count of standard harnesses, contract harnesses,
9-
/// and functions under contract across all `KaniMetadata` objects.
10-
struct Totals {
11-
standard_harnesses: usize,
12-
contract_harnesses: usize,
13-
contracted_functions: usize,
11+
struct ListMetadata {
12+
// Files mapped to their #[kani::proof] harnesses
13+
standard_harnesses: BTreeMap<String, BTreeSet<String>>,
14+
// Total number of #[kani::proof] harnesses
15+
standard_harnesses_count: usize,
16+
// Files mapped to their #[kani::proof_for_contract] harnesses
17+
contract_harnesses: BTreeMap<String, BTreeSet<String>>,
18+
// Total number of #[kani:proof_for_contract] harnesses
19+
contract_harnesses_count: usize,
20+
// Set of all functions under contract
21+
contracted_functions: BTreeSet<ContractedFunction>,
1422
}

0 commit comments

Comments
 (0)