Skip to content

Commit 9cd3de1

Browse files
Unified handling for unstable command line flags across compiler and driver (rust-lang#2719)
Co-authored-by: Felipe R. Monteiro <[email protected]>
1 parent dd2003b commit 9cd3de1

File tree

8 files changed

+137
-34
lines changed

8 files changed

+137
-34
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ dependencies = [
549549
name = "kani_metadata"
550550
version = "0.36.0"
551551
dependencies = [
552+
"clap",
552553
"cprover_bindings",
553554
"serde",
554555
"strum 0.25.0",

kani-driver/src/args/common.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
//! Define arguments that should be common to all subcommands in Kani.
44
use crate::args::ValidateArgs;
5-
use clap::{error::Error, error::ErrorKind, ValueEnum};
5+
use clap::{error::Error, error::ErrorKind};
6+
pub use kani_metadata::{EnabledUnstableFeatures, UnstableFeature};
67

78
/// Common Kani arguments that we expect to be included in most subcommands.
89
#[derive(Debug, clap::Args)]
@@ -26,26 +27,8 @@ pub struct CommonArgs {
2627
pub dry_run: bool,
2728

2829
/// Enable an unstable feature.
29-
#[arg(short = 'Z', num_args(1), value_name = "UNSTABLE_FEATURE")]
30-
pub unstable_features: Vec<UnstableFeatures>,
31-
}
32-
33-
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, strum_macros::Display)]
34-
#[strum(serialize_all = "kebab-case")]
35-
pub enum UnstableFeatures {
36-
/// Allow replacing certain items with stubs (mocks).
37-
/// See [RFC-0002](https://model-checking.github.io/kani/rfc/rfcs/0002-function-stubbing.html)
38-
Stubbing,
39-
/// Generate a C-like file equivalent to input program used for debugging purpose.
40-
GenC,
41-
/// Allow Kani to link against C code.
42-
CFfi,
43-
/// Enable concrete playback flow.
44-
ConcretePlayback,
45-
/// Enable Kani's unstable async library.
46-
AsyncLib,
47-
/// Enable line coverage instrumentation/reports
48-
LineCoverage,
30+
#[clap(flatten)]
31+
pub unstable_features: EnabledUnstableFeatures,
4932
}
5033

5134
impl ValidateArgs for CommonArgs {

kani-driver/src/args/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl VerificationArgs {
349349
/// Is experimental stubbing enabled?
350350
pub fn is_stubbing_enabled(&self) -> bool {
351351
self.enable_stubbing
352-
|| self.common_args.unstable_features.contains(&UnstableFeatures::Stubbing)
352+
|| self.common_args.unstable_features.contains(UnstableFeature::Stubbing)
353353
}
354354
}
355355

@@ -628,7 +628,7 @@ impl ValidateArgs for VerificationArgs {
628628
}
629629

630630
if self.concrete_playback.is_some()
631-
&& !self.common_args.unstable_features.contains(&UnstableFeatures::ConcretePlayback)
631+
&& !self.common_args.unstable_features.contains(UnstableFeature::ConcretePlayback)
632632
{
633633
if self.common_args.enable_unstable {
634634
print_deprecated(&self.common_args, "--enable-unstable", "-Z concrete-playback");
@@ -642,7 +642,7 @@ impl ValidateArgs for VerificationArgs {
642642
}
643643

644644
if !self.c_lib.is_empty()
645-
&& !self.common_args.unstable_features.contains(&UnstableFeatures::CFfi)
645+
&& !self.common_args.unstable_features.contains(UnstableFeature::CFfi)
646646
{
647647
if self.common_args.enable_unstable {
648648
print_deprecated(&self.common_args, "`--enable-unstable`", "-Z c-ffi");
@@ -656,7 +656,7 @@ impl ValidateArgs for VerificationArgs {
656656
}
657657

658658
if self.coverage
659-
&& !self.common_args.unstable_features.contains(&UnstableFeatures::LineCoverage)
659+
&& !self.common_args.unstable_features.contains(UnstableFeature::LineCoverage)
660660
{
661661
return Err(Error::raw(
662662
ErrorKind::MissingRequiredArgument,

kani-driver/src/args/playback_args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Implements the subcommand handling of the playback subcommand
44
55
use crate::args::cargo::CargoTestArgs;
6-
use crate::args::common::UnstableFeatures;
6+
use crate::args::common::UnstableFeature;
77
use crate::args::{CommonArgs, ValidateArgs};
88
use clap::error::ErrorKind;
99
use clap::{Error, Parser, ValueEnum};
@@ -87,7 +87,7 @@ impl ValidateArgs for KaniPlaybackArgs {
8787
impl ValidateArgs for PlaybackArgs {
8888
fn validate(&self) -> Result<(), Error> {
8989
self.common_opts.validate()?;
90-
if !self.common_opts.unstable_features.contains(&UnstableFeatures::ConcretePlayback) {
90+
if !self.common_opts.unstable_features.contains(UnstableFeature::ConcretePlayback) {
9191
return Err(Error::raw(
9292
ErrorKind::MissingRequiredArgument,
9393
"The `playback` subcommand is unstable and requires `-Z concrete-playback` \

kani-driver/src/call_single_file.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,7 @@ impl KaniSession {
100100
flags.push("--coverage-checks".into());
101101
}
102102

103-
flags.extend(
104-
self.args
105-
.common_args
106-
.unstable_features
107-
.iter()
108-
.map(|feature| format!("--unstable={feature}")),
109-
);
103+
flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string));
110104

111105
// This argument will select the Kani flavour of the compiler. It will be removed before
112106
// rustc driver is invoked.

kani_metadata/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ serde = {version = "1", features = ["derive"]}
1515
cbmc = { path = "../cprover_bindings", package = "cprover_bindings" }
1616
strum = "0.25.0"
1717
strum_macros = "0.25.2"
18+
clap = { version = "4.1.3", features = ["derive"] }

kani_metadata/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4+
extern crate clap;
5+
46
use std::{collections::HashSet, path::PathBuf};
57

68
use serde::{Deserialize, Serialize};
@@ -13,8 +15,11 @@ pub use vtable::*;
1315
pub mod artifact;
1416
mod cbmc_solver;
1517
mod harness;
18+
pub mod unstable;
1619
mod vtable;
1720

21+
pub use unstable::{EnabledUnstableFeatures, UnstableFeature};
22+
1823
/// The structure of `.kani-metadata.json` files, which are emitted for each crate
1924
#[derive(Debug, Clone, Serialize, Deserialize)]
2025
pub struct KaniMetadata {

kani_metadata/src/unstable.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//! Unified handling of unstable-feature flags across Kani.
4+
//!
5+
//! The central types are [`UnstableFeature`], which describes a single feature
6+
//! and is intended as a small, cheap enum that is [`Copy`].
7+
//! [`EnabledUnstableFeatures`] then describes which [`UnstableFeature`]s are
8+
//! enabled.
9+
//!
10+
//! To check if a feature is enabled use [`EnabledUnstableFeatures::contains`].
11+
//!
12+
//! ### Parsing
13+
//!
14+
//! [`EnabledUnstableFeatures`] is intended to be used with the [`clap`]
15+
//! "derive" API. You can directly drop it into a command line arguments struct
16+
//! like so:
17+
//!
18+
//! ```
19+
//! # use kani_metadata::unstable::*;
20+
//! use clap::Parser;
21+
//!
22+
//! #[derive(Parser)]
23+
//! struct MyCmdArgs {
24+
//! // ...
25+
//! #[clap(flatten)]
26+
//! unstable: EnabledUnstableFeatures,
27+
//! }
28+
//! ```
29+
//!
30+
//! Which will add the long form `--unstable feature-name` and short form `-Z
31+
//! feature-name` options to your argument parser.
32+
//!
33+
//! **Note:** [`clap`] internally uses a unique name (string) to refer to each
34+
//! argument or group, which is usually derived from the field name.
35+
//! [`EnabledUnstableFeatures`] uses the internal name
36+
//! `"enabled_unstable_features"` which may therefore not be used (as a field
37+
//! name) in the embedding argument struct, e.g. `MyCmdArgs`.
38+
//!
39+
//! ### Reusing
40+
//!
41+
//! You can turn an [`UnstableFeature`] back into it's command line
42+
//! representation. This should be done with
43+
//! [`EnabledUnstableFeatures::as_arguments`], which returns an iterator that,
44+
//! when passed to e.g. [`std::process::Command::args`] will enable those
45+
//! features in the subsequent call.
46+
//!
47+
//! You can also serialize a single feature with
48+
//! [`UnstableFeature::as_argument`].
49+
//!
50+
//! Both of these methods return values that are ready for direct usage on the
51+
//! command line, e.g one or more `-Z feature-name`. If you need only the
52+
//! serialized name of the feature use [`UnstableFeature::as_ref`].
53+
54+
/// A single unstable feature. This is where you should register your own if you
55+
/// are adding new ones, e.g. as part of the RFC process.
56+
///
57+
/// For usage see the [module level documentation][self].
58+
#[derive(
59+
Copy,
60+
Clone,
61+
Debug,
62+
PartialEq,
63+
Eq,
64+
clap::ValueEnum,
65+
strum_macros::Display,
66+
strum_macros::AsRefStr
67+
)]
68+
#[strum(serialize_all = "kebab-case")]
69+
pub enum UnstableFeature {
70+
/// Allow replacing certain items with stubs (mocks).
71+
/// See [RFC-0002](https://model-checking.github.io/kani/rfc/rfcs/0002-function-stubbing.html)
72+
Stubbing,
73+
/// Generate a C-like file equivalent to input program used for debugging purpose.
74+
GenC,
75+
/// Allow Kani to link against C code.
76+
CFfi,
77+
/// Enable concrete playback flow.
78+
ConcretePlayback,
79+
/// Enable Kani's unstable async library.
80+
AsyncLib,
81+
/// Enable line coverage instrumentation/reports.
82+
LineCoverage,
83+
}
84+
85+
impl UnstableFeature {
86+
/// Serialize this feature into a format in which it can be passed on the
87+
/// command line. Note that this already includes the `-Z` prefix, if you
88+
/// require only the serialized feature name use [`Self::as_ref`].
89+
pub fn as_argument(&self) -> [&str; 2] {
90+
["-Z", self.as_ref()]
91+
}
92+
}
93+
94+
/// An opaque collection of unstable features that is enabled on this session.
95+
/// Used to unify handling of unstable command line arguments across the
96+
/// compiler and the driver.
97+
///
98+
/// For usage see the [module level documentation][self].
99+
#[derive(clap::Args, Debug)]
100+
pub struct EnabledUnstableFeatures {
101+
#[clap(short = 'Z', long = "unstable", num_args(1), value_name = "UNSTABLE_FEATURE")]
102+
enabled_unstable_features: Vec<UnstableFeature>,
103+
}
104+
105+
impl EnabledUnstableFeatures {
106+
/// The preferred way to serialize these unstable features back into a
107+
/// format that can be used as command line arguments fo an invocation of
108+
/// e.g. the compiler.
109+
///
110+
/// See also the [module level documentation][self].
111+
pub fn as_arguments(&self) -> impl Iterator<Item = &str> {
112+
self.enabled_unstable_features.iter().flat_map(|f| f.as_argument())
113+
}
114+
115+
/// Is this feature enabled?
116+
pub fn contains(&self, feature: UnstableFeature) -> bool {
117+
self.enabled_unstable_features.contains(&feature)
118+
}
119+
}

0 commit comments

Comments
 (0)