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

Emit an error when proof_for_contract function is not found #3609

Merged
merged 7 commits into from
Oct 19, 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
16 changes: 15 additions & 1 deletion kani-compiler/src/kani_middle/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! This module contains code for processing Rust attributes (like `kani::proof`).

use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};

use kani_metadata::{CbmcSolver, HarnessAttributes, HarnessKind, Stub};
use quote::ToTokens;
Expand Down Expand Up @@ -474,6 +474,20 @@ impl<'tcx> KaniAttributes<'tcx> {
|| self.map.contains_key(&KaniAttributeKind::ProofForContract)
}

/// Check that the function specified in the `proof_for_contract` attribute
/// is reachable and emit an error if it isn't
pub fn check_proof_for_contract(&self, reachable_functions: &HashSet<DefId>) {
if let Some((symbol, function, span)) = self.interpret_for_contract_attribute() {
if !reachable_functions.contains(&function) {
let err_msg = format!(
"The function specified in the `proof_for_contract` attribute, `{symbol}`, was not found.\
\nMake sure the function is reachable from the harness."
);
self.tcx.dcx().span_err(span, err_msg);
}
}
}

/// Extract harness attributes for a given `def_id`.
///
/// We only extract attributes for harnesses that are local to the current crate.
Expand Down
13 changes: 11 additions & 2 deletions kani-compiler/src/kani_middle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ impl TyVisitor for FindUnsafeCell<'_> {
pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) {
// Avoid printing the same error multiple times for different instantiations of the same item.
let mut def_ids = HashSet::new();
let reachable_functions: HashSet<InternalDefId> = items
.iter()
.filter_map(|i| match i {
MonoItem::Fn(instance) => Some(rustc_internal::internal(tcx, instance.def.def_id())),
_ => None,
})
.collect();
for item in items.iter().filter(|i| matches!(i, MonoItem::Fn(..) | MonoItem::Static(..))) {
let def_id = match item {
MonoItem::Fn(instance) => instance.def.def_id(),
Expand All @@ -110,9 +117,11 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem])
}
};
if !def_ids.contains(&def_id) {
let attributes = KaniAttributes::for_def_id(tcx, def_id);
// Check if any unstable attribute was reached.
KaniAttributes::for_def_id(tcx, def_id)
.check_unstable_features(&queries.args().unstable_features);
attributes.check_unstable_features(&queries.args().unstable_features);
// Check whether all `proof_for_contract` functions are reachable
attributes.check_proof_for_contract(&reachable_functions);
def_ids.insert(def_id);
}
}
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/unknown-contract-harness/expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: The function specified in the `proof_for_contract` attribute, `foo`, was not found.\
Make sure the function is reachable from the harness.
test.rs:\
|\
| #[kani::proof_for_contract(foo)]\
| ^^^^^^^^^^^^^^
14 changes: 14 additions & 0 deletions tests/ui/unknown-contract-harness/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
// kani-flags: -Zfunction-contracts

// This test checks Kani's error when function specified in `proof_for_contract`
// harness is not found (e.g. because it's not reachable from the harness)

#[kani::requires(true)]
fn foo() {}

#[kani::proof_for_contract(foo)]
fn check_foo() {
assert!(true);
}
Loading