@@ -35,6 +35,8 @@ use std::os::unix::process::ExitStatusExt;
3535use std:: path:: PathBuf ;
3636use std:: process:: { Child , ChildStdout } ;
3737
38+ const RESULT_ITEM_PREFIX : & str = " {\n \" result\" :" ;
39+
3840/// A parser item is a top-level unit of output from the CBMC json format.
3941/// See the parser for more information on how they are processed.
4042#[ derive( Debug , Deserialize ) ]
@@ -57,6 +59,17 @@ pub enum ParserItem {
5759 } ,
5860}
5961
62+ /// Struct that is equivalent to `ParserItem::Result`.
63+ ///
64+ /// Note: this struct is only used to provide better error messages when there
65+ /// are issues deserializing a `ParserItem::Result`. See `Parser::parse_item`
66+ /// for more details.
67+ #[ allow( unused) ]
68+ #[ derive( Debug , Deserialize ) ]
69+ struct ResultStruct {
70+ result : Vec < Property > ,
71+ }
72+
6073/// Struct that represents a single property in the set of CBMC results.
6174///
6275/// Note: `reach` is not part of the parsed data, but it's useful to annotate
@@ -285,7 +298,7 @@ pub struct TraceValue {
285298 pub name : String ,
286299 pub binary : Option < String > ,
287300 pub data : Option < TraceData > ,
288- pub width : Option < u8 > ,
301+ pub width : Option < u32 > ,
289302}
290303
291304/// Enum that represents a trace data item.
@@ -420,6 +433,25 @@ impl<'a, 'b> Parser<'a, 'b> {
420433 if let Ok ( item) = result_item {
421434 return item;
422435 }
436+ // If we failed to parse a `ParserItem::Result` earlier, we will get
437+ // this error message when we attempt to parse it using the complete
438+ // string:
439+ // ```
440+ // thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value:
441+ // Error("data did not match any variant of untagged enum ParserItem", line: 0, column: 0)'
442+ // ```
443+ // This error message doesn't provide information about what went wrong
444+ // while parsing due to `ParserItem` being an untagged enum. A more
445+ // informative error message will be produced if we attempt to
446+ // deserialize it into a struct. The attempt will still fail, but it
447+ // shouldn't be hard to debug with that information. The same strategy
448+ // can be used for other `ParserItem` variants, but they're normally
449+ // easier to debug.
450+ if string_without_delimiter. starts_with ( RESULT_ITEM_PREFIX ) {
451+ let result_item: Result < ResultStruct , _ > =
452+ serde_json:: from_str ( string_without_delimiter) ;
453+ result_item. unwrap ( ) ;
454+ }
423455 let complete_string = & self . input_so_far [ 0 ..self . input_so_far . len ( ) ] ;
424456 let result_item: Result < ParserItem , _ > = serde_json:: from_str ( complete_string) ;
425457 result_item. unwrap ( )
@@ -681,4 +713,45 @@ mod tests {
681713 serde_json:: from_str ( prop_id_string) ;
682714 let _prop_id = prop_id_result. unwrap ( ) ;
683715 }
716+
717+ #[ test]
718+ fn check_trace_value_deserialization_works ( ) {
719+ let data = format ! (
720+ r#"{{
721+ "binary": "{:0>1000}",
722+ "data": "0",
723+ "name": "integer",
724+ "type": "unsigned __CPROVER_bitvector[960]",
725+ "width": 960
726+ }}"# ,
727+ 0
728+ ) ;
729+ let trace_value: Result < TraceValue , _ > = serde_json:: from_str ( & data) ;
730+ assert ! ( trace_value. is_ok( ) ) ;
731+ }
732+
733+ /// Checks that a valid CBMC "result" item can be deserialized into a
734+ /// `ParserItem` or `ResultStruct`.
735+ #[ test]
736+ fn check_result_deserialization_works ( ) {
737+ let data = r#"{
738+ "result": [
739+ {
740+ "description": "assertion failed: 1 > 2",
741+ "property": "long_function_name.assertion.1",
742+ "sourceLocation": {
743+ "column": "16",
744+ "file": "/home/ubuntu/file.rs",
745+ "function": "long_function_name",
746+ "line": "815"
747+ },
748+ "status": "SUCCESS"
749+ }
750+ ]
751+ }"# ;
752+ let parser_item: Result < ParserItem , _ > = serde_json:: from_str ( & data) ;
753+ let result_struct: Result < ResultStruct , _ > = serde_json:: from_str ( & data) ;
754+ assert ! ( parser_item. is_ok( ) ) ;
755+ assert ! ( result_struct. is_ok( ) ) ;
756+ }
684757}
0 commit comments