Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable doctests to avoid coding mistakes in our API documentation #3557

Merged
merged 3 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions library/kani/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//! simple division function `my_div`:
//!
//! ```
//! fn my_div(dividend: u32, divisor: u32) -> u32 {
//! fn my_div(dividend: usize, divisor: usize) -> usize {
//! dividend / divisor
//! }
//! ```
Expand All @@ -35,8 +35,12 @@
//! allows us to declare constraints on what constitutes valid inputs to our
//! function. In this case we would want to disallow a divisor that is `0`.
//!
//! ```ignore
//! ```
//! # use kani::requires;
//! #[requires(divisor != 0)]
//! # fn my_div(dividend: usize, divisor: usize) -> usize {
//! # dividend / divisor
//! # }
//! ```
//!
//! This is called a precondition, because it is enforced before (pre-) the
Expand All @@ -51,7 +55,11 @@
//! approximation of the result of division for instance could be this:
//!
//! ```
//! #[ensures(|result : &u32| *result <= dividend)]
//! # use kani::ensures;
//! #[ensures(|result : &usize| *result <= dividend)]
//! # fn my_div(dividend: usize, divisor: usize) -> usize {
//! # dividend / divisor
//! # }
//! ```
//!
//! This is called a postcondition and it also has access to the arguments and
Expand All @@ -66,9 +74,11 @@
//! order does not matter. In our example putting them together looks like this:
//!
//! ```
//! #[kani::requires(divisor != 0)]
//! #[kani::ensures(|result : &u32| *result <= dividend)]
//! fn my_div(dividend: u32, divisor: u32) -> u32 {
//! use kani::{requires, ensures};
//!
//! #[requires(divisor != 0)]
//! #[ensures(|result : &usize| *result <= dividend)]
//! fn my_div(dividend: usize, divisor: usize) -> usize {
//! dividend / divisor
//! }
//! ```
Expand All @@ -84,9 +94,18 @@
//! function we want to check.
//!
//! ```
//! # use kani::{requires, ensures};
//! #
//! # #[requires(divisor != 0)]
//! # #[ensures(|result : &usize| *result <= dividend)]
//! # fn my_div(dividend: usize, divisor: usize) -> usize {
//! # dividend / divisor
//! # }
//! #
//! #[kani::proof_for_contract(my_div)]
//! fn my_div_harness() {
//! my_div(kani::any(), kani::any()) }
//! my_div(kani::any(), kani::any());
//! }
//! ```
//!
//! The harness is checked like any other by running `cargo kani` and can be
Expand All @@ -104,10 +123,18 @@
//! the contract will be used automatically.
//!
//! ```
//! # use kani::{requires, ensures};
//! #
//! # #[requires(divisor != 0)]
//! # #[ensures(|result : &usize| *result <= dividend)]
//! # fn my_div(dividend: usize, divisor: usize) -> usize {
//! # dividend / divisor
//! # }
//! #
//! #[kani::proof]
//! #[kani::stub_verified(my_div)]
//! fn use_div() {
//! let v = vec![...];
//! let v = kani::vec::any_vec::<char, 5>();
//! let some_idx = my_div(v.len() - 1, 3);
//! v[some_idx];
//! }
Expand Down
33 changes: 31 additions & 2 deletions library/kani/src/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
/// ```
/// You can specify its safety invariant as:
/// ```rust
/// # #[derive(kani::Arbitrary)]
/// # pub struct MyDate {
/// # day: u8,
/// # month: u8,
/// # year: i64,
/// # }
/// # fn days_in_month(_: i64, _: u8) -> u8 { 31 }
///
/// impl kani::Invariant for MyDate {
/// fn is_safe(&self) -> bool {
/// self.month > 0
Expand All @@ -49,12 +57,33 @@
/// }
/// ```
/// And use it to check that your APIs are safe:
/// ```rust
/// ```no_run
/// # use kani::Invariant;
/// #
/// # #[derive(kani::Arbitrary)]
/// # pub struct MyDate {
/// # day: u8,
/// # month: u8,
/// # year: i64,
/// # }
/// #
/// # fn days_in_month(_: i64, _: u8) -> u8 { todo!() }
/// # fn increase_date(_: &mut MyDate, _: u8) { todo!() }
/// #
/// # impl Invariant for MyDate {
/// # fn is_safe(&self) -> bool {
/// # self.month > 0
/// # && self.month <= 12
/// # && self.day > 0
/// # && self.day <= days_in_month(self.year, self.month)
/// # }
/// # }
/// #
/// #[kani::proof]
/// fn check_increase_date() {
/// let mut date: MyDate = kani::any();
/// // Increase date by one day
/// increase_date(date, 1);
/// increase_date(&mut date, 1);
/// assert!(date.is_safe());
/// }
/// ```
Expand Down
2 changes: 2 additions & 0 deletions library/kani/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#![feature(ptr_metadata)]
#![feature(f16)]
#![feature(f128)]
// Need to add this since we are deprecating `kani::check`. Remove this when we remove that API.
#![allow(deprecated)]

// Allow us to use `kani::` to access crate features.
extern crate self as kani;
Expand Down
2 changes: 1 addition & 1 deletion library/kani/src/shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//!
//! # Example
//!
//! ```
//! ```no_run
//! use kani::shadow::ShadowMem;
//! use std::alloc::{alloc, Layout};
//!
Expand Down
3 changes: 2 additions & 1 deletion library/kani/src/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use crate::{any, assume};
///
/// # Example:
///
/// ```rust
/// ```no_run
/// # fn foo(_: &[i32]) {}
/// let arr = [1, 2, 3];
/// let slice = kani::slice::any_slice_of_array(&arr);
/// foo(slice); // where foo is a function that takes a slice and verifies a property about it
Expand Down
98 changes: 68 additions & 30 deletions library/kani_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ macro_rules! kani_lib {
pub use kani_core::*;
kani_core::kani_intrinsics!(core);
kani_core::generate_arbitrary!(core);
kani_core::check_intrinsic!();

pub mod mem {
kani_core::kani_mem!(core);
Expand All @@ -58,6 +59,10 @@ macro_rules! kani_lib {
kani_core::kani_intrinsics!(std);
kani_core::generate_arbitrary!(std);

kani_core::check_intrinsic! {
msg="This API will be made private in future releases.", vis=pub
}

pub mod mem {
kani_core::kani_mem!(std);
}
Expand Down Expand Up @@ -85,7 +90,7 @@ macro_rules! kani_intrinsics {
///
/// The code snippet below should never panic.
///
/// ```rust
/// ```no_run
/// let i : i32 = kani::any();
/// kani::assume(i > 10);
/// if i < 0 {
Expand All @@ -95,7 +100,7 @@ macro_rules! kani_intrinsics {
///
/// The following code may panic though:
///
/// ```rust
/// ```no_run
/// let i : i32 = kani::any();
/// assert!(i < 0, "This may panic and verification should fail.");
/// kani::assume(i > 10);
Expand All @@ -118,7 +123,7 @@ macro_rules! kani_intrinsics {
///
/// # Example:
///
/// ```rust
/// ```no_run
/// let x: bool = kani::any();
/// let y = !x;
/// kani::assert(x || y, "ORing a boolean variable with its negation must be true")
Expand All @@ -138,35 +143,15 @@ macro_rules! kani_intrinsics {
assert!(cond, "{}", msg);
}

/// Creates an assertion of the specified condition and message, but does not assume it afterwards.
///
/// # Example:
///
/// ```rust
/// let x: bool = kani::any();
/// let y = !x;
/// kani::check(x || y, "ORing a boolean variable with its negation must be true")
/// ```
#[cfg(not(feature = "concrete_playback"))]
#[inline(never)]
#[rustc_diagnostic_item = "KaniCheck"]
pub const fn check(cond: bool, msg: &'static str) {
let _ = cond;
let _ = msg;
}

#[cfg(feature = "concrete_playback")]
#[inline(never)]
#[rustc_diagnostic_item = "KaniCheck"]
pub const fn check(cond: bool, msg: &'static str) {
assert!(cond, "{}", msg);
}

/// Creates a cover property with the specified condition and message.
///
/// # Example:
///
/// ```rust
/// ```no_run
/// # use crate::kani;
/// #
/// # let array: [u8; 10] = kani::any();
/// # let slice = kani::slice::any_slice_of_array(&array);
/// kani::cover(slice.len() == 0, "The slice may have a length of 0");
/// ```
///
Expand All @@ -193,7 +178,11 @@ macro_rules! kani_intrinsics {
/// In the snippet below, we are verifying the behavior of the function `fn_under_verification`
/// under all possible `NonZeroU8` input values, i.e., all possible `u8` values except zero.
///
/// ```rust
/// ```no_run
/// # use std::num::NonZeroU8;
/// # use crate::kani;
/// #
/// # fn fn_under_verification(_: NonZeroU8) {}
/// let inputA = kani::any::<core::num::NonZeroU8>();
/// fn_under_verification(inputA);
/// ```
Expand Down Expand Up @@ -231,7 +220,11 @@ macro_rules! kani_intrinsics {
/// In the snippet below, we are verifying the behavior of the function `fn_under_verification`
/// under all possible `u8` input values between 0 and 12.
///
/// ```rust
/// ```no_run
/// # use std::num::NonZeroU8;
/// # use crate::kani;
/// #
/// # fn fn_under_verification(_: u8) {}
/// let inputA: u8 = kani::any_where(|x| *x < 12);
/// fn_under_verification(inputA);
/// ```
Expand Down Expand Up @@ -460,3 +453,48 @@ macro_rules! kani_intrinsics {
}
};
}

#[macro_export]
macro_rules! check_intrinsic {
($(msg=$msg:literal, vis=$vis:vis)?) => {
/// Creates a non-fatal property with the specified condition and message.
///
/// This check will not impact the program control flow even when it fails.
///
/// # Example:
///
/// ```no_run
/// let x: bool = kani::any();
/// let y = !x;
/// kani::check(x || y, "ORing a boolean variable with its negation must be true");
/// kani::check(x == y, "A boolean variable is always different than its negation");
/// kani::cover!(true, "This should still be reachable");
/// ```
///
/// # Deprecated
///
/// This function was meant to be internal only, and it was added to Kani's public interface
/// by mistake. Thus, it will be made private in future releases.
/// Instead, we recommend users to either use `assert` or `cover` to create properties they
/// would like to verify.
///
/// See <https://github.com/model-checking/kani/issues/3561> for more details.
#[cfg(not(feature = "concrete_playback"))]
#[inline(never)]
#[rustc_diagnostic_item = "KaniCheck"]
// TODO: Remove the `#![allow(deprecated)]` inside kani's crate once this is made private.
$(#[deprecated(since="0.55.0", note=$msg)])?
$($vis)? const fn check(cond: bool, msg: &'static str) {
let _ = cond;
let _ = msg;
}

#[cfg(feature = "concrete_playback")]
#[inline(never)]
#[rustc_diagnostic_item = "KaniCheck"]
$(#[deprecated(since="0.55.0", note=$msg)])?
$($vis)? const fn check(cond: bool, msg: &'static str) {
assert!(cond, "{}", msg);
}
};
}
4 changes: 2 additions & 2 deletions scripts/kani-regression.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ cargo test -p cprover_bindings
cargo test -p kani-compiler
cargo test -p kani-driver
cargo test -p kani_metadata
# skip doc tests and enable assertions to fail
cargo test -p kani --lib --features concrete_playback
# Use concrete playback to enable assertions failure
cargo test -p kani --features concrete_playback
# Test the actual macros, skipping doc tests and enabling extra traits for "syn"
# so we can debug print AST
RUSTFLAGS=--cfg=kani_sysroot cargo test -p kani_macros --features syn/extra-traits --lib
Expand Down
1 change: 1 addition & 0 deletions tests/ui/check_deprecated/deprecated_warning.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
warning: use of deprecated function `kani::check`: This API will be made private in future releases.
10 changes: 10 additions & 0 deletions tests/ui/check_deprecated/deprecated_warning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
// kani-flags: --only-codegen

/// Ensure that Kani prints a deprecation warning if users invoke `kani::check`.
#[kani::proof]
fn internal_api() {
kani::check(kani::any(), "oops");
}
Loading