diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 0085928a37d0..368e0ec91495 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -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; @@ -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) { + 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. diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 5fead860320c..3e1c7e3b4fca 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -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 = 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(), @@ -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); } } diff --git a/tests/ui/unknown-contract-harness/expected b/tests/ui/unknown-contract-harness/expected new file mode 100644 index 000000000000..dec02f9b0a50 --- /dev/null +++ b/tests/ui/unknown-contract-harness/expected @@ -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)]\ +| ^^^^^^^^^^^^^^ diff --git a/tests/ui/unknown-contract-harness/test.rs b/tests/ui/unknown-contract-harness/test.rs new file mode 100644 index 000000000000..70c323c144aa --- /dev/null +++ b/tests/ui/unknown-contract-harness/test.rs @@ -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); +}