66
77use proc_macro:: TokenStream ;
88use proc_macro_error2:: abort_call_site;
9- use quote:: { format_ident, quote} ;
9+ use quote:: { format_ident, quote, quote_spanned } ;
1010use syn:: punctuated:: Punctuated ;
1111use syn:: spanned:: Spanned ;
1212use syn:: token:: AndAnd ;
1313use syn:: {
14- BinOp , Block , Expr , ExprBinary , Ident , Stmt , Token , parse_macro_input, parse_quote,
14+ BinOp , Block , Expr , ExprBinary , ExprWhile , Ident , Stmt , Token , parse_macro_input, parse_quote,
1515 visit_mut:: VisitMut ,
1616} ;
1717
@@ -232,9 +232,32 @@ fn transform_break_continue(block: &mut Block) {
232232 block. stmts . push ( return_stmt) ;
233233}
234234
235+ fn while_let_rewrite ( loopexpr : Stmt ) -> Stmt {
236+ if let Stmt :: Expr ( ref expr, _) = loopexpr {
237+ if let Expr :: While ( ExprWhile { cond, body, .. } ) = expr {
238+ if let Expr :: Let ( ref let_expr) = * * cond {
239+ let pat = & let_expr. pat ;
240+ let scrutinee = & let_expr. expr ;
241+
242+ // Transform to loop with match
243+ return parse_quote ! {
244+ loop {
245+ match #scrutinee {
246+ #pat => #body,
247+ _ => break ,
248+ }
249+ } ;
250+ } ;
251+ }
252+ }
253+ }
254+ return loopexpr. clone ( ) ;
255+ }
256+
235257pub fn loop_invariant ( attr : TokenStream , item : TokenStream ) -> TokenStream {
236258 // parse the stmt of the loop
237259 let mut loop_stmt: Stmt = syn:: parse ( item. clone ( ) ) . unwrap ( ) ;
260+ loop_stmt = while_let_rewrite ( loop_stmt) ;
238261
239262 // name of the loop invariant as closure of the form
240263 // __kani_loop_invariant_#startline_#startcol_#endline_#endcol
@@ -244,6 +267,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
244267
245268 // expr of the loop invariant
246269 let mut inv_expr: Expr = syn:: parse ( attr) . unwrap ( ) ;
270+ let original_span = inv_expr. span ( ) ;
247271
248272 // adding on_entry variables
249273 let mut onentry_var_prefix: String = "__kani_onentry_var" . to_owned ( ) ;
@@ -302,7 +326,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
302326 & mut Stmt :: Expr ( ref mut e, _) => match * e {
303327 Expr :: While ( ref mut ew) => {
304328 let new_cond: Expr = syn:: parse (
305- quote ! (
329+ quote_spanned ! ( original_span =>
306330 #register_ident( & ||->bool { #inv_expr} , 0 ) )
307331 . into ( ) ,
308332 )
@@ -316,7 +340,9 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
316340 }
317341 Expr :: Loop ( ref mut el) => {
318342 //let retexpr = get_return_statement(&el.body);
319- let invstmt: Stmt = syn:: parse ( quote ! ( if !( #register_ident( & ||->bool { #inv_expr} , 0 ) ) { assert!( false ) ; unreachable!( ) } ; ) . into ( ) ) . unwrap ( ) ;
343+ let invstmt: Stmt = syn:: parse ( quote_spanned ! ( original_span =>
344+ if !( #register_ident( & ||->bool { #inv_expr} , 0 ) ) { assert!( false ) ; unreachable!( ) } ; )
345+ . into ( ) ) . unwrap ( ) ;
320346 let mut new_stmts: Vec < Stmt > = Vec :: new ( ) ;
321347 new_stmts. push ( invstmt) ;
322348 new_stmts. extend ( el. body . stmts . clone ( ) ) ;
@@ -334,7 +360,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
334360 }
335361
336362 if has_prev {
337- quote ! (
363+ quote_spanned ! ( original_span =>
338364 {
339365 if ( #loop_guard) {
340366 #( #onentry_decl_stms) *
@@ -363,7 +389,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
363389 } )
364390 . into ( )
365391 } else {
366- quote ! (
392+ quote_spanned ! ( original_span =>
367393 {
368394 #( #onentry_decl_stms) *
369395 // Dummy function used to force the compiler to capture the environment.
0 commit comments