@@ -16,17 +16,31 @@ use crate::cbmc_output_parser::{
1616use crate :: cbmc_property_renderer:: { format_result, kani_cbmc_output_filter} ;
1717use crate :: session:: KaniSession ;
1818
19- #[ derive( Debug , PartialEq , Eq ) ]
19+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
2020pub enum VerificationStatus {
2121 Success ,
2222 Failure ,
2323}
2424
25+ /// Represents failed properties in three different categories.
26+ /// This simplifies the process to determine and format verification results.
27+ #[ derive( Clone , Copy , Debug ) ]
28+ pub enum FailedProperties {
29+ // No failures
30+ None ,
31+ // One or more panic-related failures
32+ PanicsOnly ,
33+ // One or more failures that aren't panic-related
34+ Other ,
35+ }
36+
2537/// Our (kani-driver) notions of CBMC results.
2638#[ derive( Debug ) ]
2739pub struct VerificationResult {
2840 /// Whether verification should be considered to have succeeded, or have failed.
2941 pub status : VerificationStatus ,
42+ /// The compact representation for failed properties
43+ pub failed_properties : FailedProperties ,
3044 /// The parsed output, message by message, of CBMC. However, the `Result` message has been
3145 /// removed and is available in `results` instead.
3246 pub messages : Option < Vec < ParserItem > > ,
@@ -76,7 +90,7 @@ impl KaniSession {
7690 )
7791 } ) ?;
7892
79- VerificationResult :: from ( output, start_time)
93+ VerificationResult :: from ( output, harness . attributes . should_panic , start_time)
8094 } ;
8195
8296 self . gen_and_add_concrete_playback ( harness, & mut verification_results) ?;
@@ -234,13 +248,20 @@ impl VerificationResult {
234248 /// (CBMC will regularly report "failure" but that's just our cover checks.)
235249 /// 2. Positively checking for the presence of results.
236250 /// (Do not mistake lack of results for success: report it as failure.)
237- fn from ( output : VerificationOutput , start_time : Instant ) -> VerificationResult {
251+ fn from (
252+ output : VerificationOutput ,
253+ should_panic : bool ,
254+ start_time : Instant ,
255+ ) -> VerificationResult {
238256 let runtime = start_time. elapsed ( ) ;
239257 let ( items, results) = extract_results ( output. processed_items ) ;
240258
241259 if let Some ( results) = results {
260+ let ( status, failed_properties) =
261+ verification_outcome_from_properties ( & results, should_panic) ;
242262 VerificationResult {
243- status : determine_status_from_properties ( & results) ,
263+ status,
264+ failed_properties,
244265 messages : Some ( items) ,
245266 results : Ok ( results) ,
246267 runtime,
@@ -250,6 +271,7 @@ impl VerificationResult {
250271 // We never got results from CBMC - something went wrong (e.g. crash) so it's failure
251272 VerificationResult {
252273 status : VerificationStatus :: Failure ,
274+ failed_properties : FailedProperties :: Other ,
253275 messages : Some ( items) ,
254276 results : Err ( output. process_status ) ,
255277 runtime,
@@ -261,6 +283,7 @@ impl VerificationResult {
261283 pub fn mock_success ( ) -> VerificationResult {
262284 VerificationResult {
263285 status : VerificationStatus :: Success ,
286+ failed_properties : FailedProperties :: None ,
264287 messages : None ,
265288 results : Ok ( vec ! [ ] ) ,
266289 runtime : Duration :: from_secs ( 0 ) ,
@@ -271,6 +294,7 @@ impl VerificationResult {
271294 fn mock_failure ( ) -> VerificationResult {
272295 VerificationResult {
273296 status : VerificationStatus :: Failure ,
297+ failed_properties : FailedProperties :: Other ,
274298 messages : None ,
275299 // on failure, exit codes in theory might be used,
276300 // but `mock_failure` should never be used in a context where they will,
@@ -281,11 +305,14 @@ impl VerificationResult {
281305 }
282306 }
283307
284- pub fn render ( & self , output_format : & OutputFormat ) -> String {
308+ pub fn render ( & self , output_format : & OutputFormat , should_panic : bool ) -> String {
285309 match & self . results {
286310 Ok ( results) => {
311+ let status = self . status ;
312+ let failed_properties = self . failed_properties ;
287313 let show_checks = matches ! ( output_format, OutputFormat :: Regular ) ;
288- let mut result = format_result ( results, show_checks) ;
314+ let mut result =
315+ format_result ( results, status, should_panic, failed_properties, show_checks) ;
289316 writeln ! ( result, "Verification Time: {}s" , self . runtime. as_secs_f32( ) ) . unwrap ( ) ;
290317 result
291318 }
@@ -310,13 +337,42 @@ impl VerificationResult {
310337}
311338
312339/// We decide if verification succeeded based on properties, not (typically) on exit code
313- fn determine_status_from_properties ( properties : & [ Property ] ) -> VerificationStatus {
314- let number_failed_properties =
315- properties. iter ( ) . filter ( |prop| prop. status == CheckStatus :: Failure ) . count ( ) ;
316- if number_failed_properties == 0 {
317- VerificationStatus :: Success
340+ fn verification_outcome_from_properties (
341+ properties : & [ Property ] ,
342+ should_panic : bool ,
343+ ) -> ( VerificationStatus , FailedProperties ) {
344+ let failed_properties = determine_failed_properties ( properties) ;
345+ let status = if should_panic {
346+ match failed_properties {
347+ FailedProperties :: None | FailedProperties :: Other => VerificationStatus :: Failure ,
348+ FailedProperties :: PanicsOnly => VerificationStatus :: Success ,
349+ }
318350 } else {
319- VerificationStatus :: Failure
351+ match failed_properties {
352+ FailedProperties :: None => VerificationStatus :: Success ,
353+ FailedProperties :: PanicsOnly | FailedProperties :: Other => VerificationStatus :: Failure ,
354+ }
355+ } ;
356+ ( status, failed_properties)
357+ }
358+
359+ /// Determines the `FailedProperties` variant that corresponds to an array of properties
360+ fn determine_failed_properties ( properties : & [ Property ] ) -> FailedProperties {
361+ let failed_properties: Vec < & Property > =
362+ properties. iter ( ) . filter ( |prop| prop. status == CheckStatus :: Failure ) . collect ( ) ;
363+ // Return `FAILURE` if there isn't at least one failed property
364+ if failed_properties. is_empty ( ) {
365+ FailedProperties :: None
366+ } else {
367+ // Check if all failed properties correspond to the `assertion` class.
368+ // Note: Panics caused by `panic!` and `assert!` fall into this class.
369+ let all_failed_checks_are_panics =
370+ failed_properties. iter ( ) . all ( |prop| prop. property_class ( ) == "assertion" ) ;
371+ if all_failed_checks_are_panics {
372+ FailedProperties :: PanicsOnly
373+ } else {
374+ FailedProperties :: Other
375+ }
320376 }
321377}
322378
0 commit comments