6
6
7
7
use proc_macro:: TokenStream ;
8
8
use proc_macro_error2:: abort_call_site;
9
- use quote:: { format_ident, quote} ;
9
+ use quote:: { format_ident, quote, quote_spanned } ;
10
10
use syn:: punctuated:: Punctuated ;
11
11
use syn:: spanned:: Spanned ;
12
12
use syn:: token:: AndAnd ;
13
13
use 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,
15
15
visit_mut:: VisitMut ,
16
16
} ;
17
17
@@ -232,9 +232,32 @@ fn transform_break_continue(block: &mut Block) {
232
232
block. stmts . push ( return_stmt) ;
233
233
}
234
234
235
+ fn while_let_rewrite ( loopexpr : Stmt ) -> Stmt {
236
+ if let Stmt :: Expr ( ref expr, _) = loopexpr
237
+ && let Expr :: While ( ExprWhile { cond, body, .. } ) = expr
238
+ && let Expr :: Let ( ref let_expr) = * * cond
239
+ {
240
+ let pat = & let_expr. pat ;
241
+ let scrutinee = & let_expr. expr ;
242
+
243
+ // Transform to loop with match
244
+ return parse_quote ! {
245
+ loop {
246
+ match #scrutinee {
247
+ #pat => #body,
248
+ _ => break ,
249
+ }
250
+ } ;
251
+ } ;
252
+ }
253
+
254
+ loopexpr. clone ( )
255
+ }
256
+
235
257
pub fn loop_invariant ( attr : TokenStream , item : TokenStream ) -> TokenStream {
236
258
// parse the stmt of the loop
237
259
let mut loop_stmt: Stmt = syn:: parse ( item. clone ( ) ) . unwrap ( ) ;
260
+ loop_stmt = while_let_rewrite ( loop_stmt) ;
238
261
239
262
// name of the loop invariant as closure of the form
240
263
// __kani_loop_invariant_#startline_#startcol_#endline_#endcol
@@ -244,6 +267,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
244
267
245
268
// expr of the loop invariant
246
269
let mut inv_expr: Expr = syn:: parse ( attr) . unwrap ( ) ;
270
+ let original_span = inv_expr. span ( ) ;
247
271
248
272
// adding on_entry variables
249
273
let mut onentry_var_prefix: String = "__kani_onentry_var" . to_owned ( ) ;
@@ -302,7 +326,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
302
326
& mut Stmt :: Expr ( ref mut e, _) => match * e {
303
327
Expr :: While ( ref mut ew) => {
304
328
let new_cond: Expr = syn:: parse (
305
- quote ! (
329
+ quote_spanned ! ( original_span =>
306
330
#register_ident( & ||->bool { #inv_expr} , 0 ) )
307
331
. into ( ) ,
308
332
)
@@ -316,7 +340,9 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
316
340
}
317
341
Expr :: Loop ( ref mut el) => {
318
342
//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 ( ) ;
320
346
let mut new_stmts: Vec < Stmt > = Vec :: new ( ) ;
321
347
new_stmts. push ( invstmt) ;
322
348
new_stmts. extend ( el. body . stmts . clone ( ) ) ;
@@ -334,7 +360,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
334
360
}
335
361
336
362
if has_prev {
337
- quote ! (
363
+ quote_spanned ! ( original_span =>
338
364
{
339
365
if ( #loop_guard) {
340
366
#( #onentry_decl_stms) *
@@ -363,7 +389,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream {
363
389
} )
364
390
. into ( )
365
391
} else {
366
- quote ! (
392
+ quote_spanned ! ( original_span =>
367
393
{
368
394
#( #onentry_decl_stms) *
369
395
// Dummy function used to force the compiler to capture the environment.
0 commit comments