@@ -16,17 +16,31 @@ use crate::cbmc_output_parser::{
16
16
use crate :: cbmc_property_renderer:: { format_result, kani_cbmc_output_filter} ;
17
17
use crate :: session:: KaniSession ;
18
18
19
- #[ derive( Debug , PartialEq , Eq ) ]
19
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
20
20
pub enum VerificationStatus {
21
21
Success ,
22
22
Failure ,
23
23
}
24
24
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
+
25
37
/// Our (kani-driver) notions of CBMC results.
26
38
#[ derive( Debug ) ]
27
39
pub struct VerificationResult {
28
40
/// Whether verification should be considered to have succeeded, or have failed.
29
41
pub status : VerificationStatus ,
42
+ /// The compact representation for failed properties
43
+ pub failed_properties : FailedProperties ,
30
44
/// The parsed output, message by message, of CBMC. However, the `Result` message has been
31
45
/// removed and is available in `results` instead.
32
46
pub messages : Option < Vec < ParserItem > > ,
@@ -76,7 +90,7 @@ impl KaniSession {
76
90
)
77
91
} ) ?;
78
92
79
- VerificationResult :: from ( output, start_time)
93
+ VerificationResult :: from ( output, harness . attributes . should_panic , start_time)
80
94
} ;
81
95
82
96
self . gen_and_add_concrete_playback ( harness, & mut verification_results) ?;
@@ -234,13 +248,20 @@ impl VerificationResult {
234
248
/// (CBMC will regularly report "failure" but that's just our cover checks.)
235
249
/// 2. Positively checking for the presence of results.
236
250
/// (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 {
238
256
let runtime = start_time. elapsed ( ) ;
239
257
let ( items, results) = extract_results ( output. processed_items ) ;
240
258
241
259
if let Some ( results) = results {
260
+ let ( status, failed_properties) =
261
+ verification_outcome_from_properties ( & results, should_panic) ;
242
262
VerificationResult {
243
- status : determine_status_from_properties ( & results) ,
263
+ status,
264
+ failed_properties,
244
265
messages : Some ( items) ,
245
266
results : Ok ( results) ,
246
267
runtime,
@@ -250,6 +271,7 @@ impl VerificationResult {
250
271
// We never got results from CBMC - something went wrong (e.g. crash) so it's failure
251
272
VerificationResult {
252
273
status : VerificationStatus :: Failure ,
274
+ failed_properties : FailedProperties :: Other ,
253
275
messages : Some ( items) ,
254
276
results : Err ( output. process_status ) ,
255
277
runtime,
@@ -261,6 +283,7 @@ impl VerificationResult {
261
283
pub fn mock_success ( ) -> VerificationResult {
262
284
VerificationResult {
263
285
status : VerificationStatus :: Success ,
286
+ failed_properties : FailedProperties :: None ,
264
287
messages : None ,
265
288
results : Ok ( vec ! [ ] ) ,
266
289
runtime : Duration :: from_secs ( 0 ) ,
@@ -271,6 +294,7 @@ impl VerificationResult {
271
294
fn mock_failure ( ) -> VerificationResult {
272
295
VerificationResult {
273
296
status : VerificationStatus :: Failure ,
297
+ failed_properties : FailedProperties :: Other ,
274
298
messages : None ,
275
299
// on failure, exit codes in theory might be used,
276
300
// but `mock_failure` should never be used in a context where they will,
@@ -281,11 +305,14 @@ impl VerificationResult {
281
305
}
282
306
}
283
307
284
- pub fn render ( & self , output_format : & OutputFormat ) -> String {
308
+ pub fn render ( & self , output_format : & OutputFormat , should_panic : bool ) -> String {
285
309
match & self . results {
286
310
Ok ( results) => {
311
+ let status = self . status ;
312
+ let failed_properties = self . failed_properties ;
287
313
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) ;
289
316
writeln ! ( result, "Verification Time: {}s" , self . runtime. as_secs_f32( ) ) . unwrap ( ) ;
290
317
result
291
318
}
@@ -310,13 +337,42 @@ impl VerificationResult {
310
337
}
311
338
312
339
/// 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
+ }
318
350
} 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
+ }
320
376
}
321
377
}
322
378
0 commit comments