1#![deny(unused_must_use)]
2#![feature(extract_if)]
3#![feature(box_patterns)]
4#![feature(proc_macro_span)]
5#![feature(if_let_guard)]
6#![feature(assert_matches)]
7#![allow(clippy::iter_with_drain)]
9#![warn(clippy::disallowed_types)]
10
11#[macro_use]
12mod common;
13mod extern_spec_rewriter;
14mod type_cond_specs;
15mod parse_closure_macro;
16mod parse_quote_spanned;
17mod predicate;
18mod rewriter;
19mod span_overrider;
20mod spec_attribute_kind;
21pub mod specifications;
22mod type_model;
23mod user_provided_type_params;
24mod print_counterexample;
25
26use proc_macro2::{Span, TokenStream, TokenTree};
27use quote::{quote, quote_spanned, ToTokens};
28use rewriter::AstRewriter;
29use std::convert::TryInto;
30use syn::{spanned::Spanned, visit::Visit};
31
32use crate::{
33 common::{merge_generics, RewritableReceiver, SelfTypeRewriter},
34 predicate::{is_predicate_macro, ParsedPredicate},
35 specifications::preparser::{parse_prusti, parse_type_cond_spec, NestedSpec},
36};
37pub use extern_spec_rewriter::ExternSpecKind;
38use parse_closure_macro::ClosureWithSpec;
39pub use spec_attribute_kind::SpecAttributeKind;
40use specifications::{common::SpecificationId, untyped};
41
42pub const SPECS_VERSION: &str = env!("CARGO_PKG_VERSION");
43
44macro_rules! handle_result {
45 ($parse_result: expr) => {
46 match $parse_result {
47 Ok(data) => data,
48 Err(err) => return err.to_compile_error(),
49 }
50 };
51}
52
53macro_rules! result_to_tokens {
54 ($body:block) => {{
55 let body = || $body;
56 handle_result!(body())
57 }};
58}
59
60fn extract_prusti_attributes(
61 item: &mut untyped::AnyFnItem,
62) -> Vec<(SpecAttributeKind, TokenStream)> {
63 let mut prusti_attributes = Vec::new();
64 let mut regular_attributes = Vec::new();
65 for attr in item.attrs_mut().drain(0..) {
66 if attr.path.segments.len() == 1
67 || (attr.path.segments.len() == 2 && attr.path.segments[0].ident == "prusti_contracts")
68 {
69 let idx = attr.path.segments.len() - 1;
70 if let Ok(attr_kind) = attr.path.segments[idx].ident.to_string().try_into() {
71 let tokens = match attr_kind {
72 SpecAttributeKind::Requires
73 | SpecAttributeKind::Ensures
74 | SpecAttributeKind::AfterExpiry
75 | SpecAttributeKind::AssertOnExpiry
76 | SpecAttributeKind::RefineSpec => {
77 let mut iter = attr.tokens.into_iter();
81 let TokenTree::Group(group) = iter.next().unwrap() else {
82 unreachable!()
83 };
84 assert!(iter.next().is_none(), "Unexpected shape of an attribute.");
85 group.stream()
86 }
87 SpecAttributeKind::Pure
89 | SpecAttributeKind::Terminates
90 | SpecAttributeKind::Trusted
91 | SpecAttributeKind::Predicate
92 | SpecAttributeKind::Verified => {
93 assert!(attr.tokens.is_empty(), "Unexpected shape of an attribute.");
94 attr.tokens
95 }
96 SpecAttributeKind::Invariant => unreachable!("type invariant on function"),
97 SpecAttributeKind::Model => unreachable!("model on function"),
98 SpecAttributeKind::PrintCounterexample => {
99 unreachable!("print_counterexample on function")
100 }
101 };
102 prusti_attributes.push((attr_kind, tokens));
103 } else {
104 regular_attributes.push(attr);
105 }
106 } else {
107 regular_attributes.push(attr);
108 }
109 }
110 *item.attrs_mut() = regular_attributes;
111 prusti_attributes
112}
113
114pub fn rewrite_prusti_attributes(
119 outer_attr_kind: SpecAttributeKind,
120 outer_attr_tokens: TokenStream,
121 item_tokens: TokenStream,
122) -> TokenStream {
123 let mut item: untyped::AnyFnItem = handle_result!(syn::parse2(item_tokens));
124
125 let mut prusti_attributes = vec![(outer_attr_kind, outer_attr_tokens)];
127
128 prusti_attributes.extend(extract_prusti_attributes(&mut item));
130
131 if prusti_attributes
133 .iter()
134 .any(|(ak, _)| ak == &SpecAttributeKind::Predicate)
135 {
136 return syn::Error::new(
137 item.span(),
138 "`predicate!` is incompatible with other Prusti attributes",
139 )
140 .to_compile_error();
141 }
142
143 let (generated_spec_items, generated_attributes) =
144 handle_result!(generate_spec_and_assertions(prusti_attributes, &item));
145
146 quote_spanned! {item.span()=>
147 #(#generated_spec_items)*
148 #(#generated_attributes)*
149 #[prusti::specs_version = #SPECS_VERSION]
150 #item
151 }
152}
153
154type GeneratedResult = syn::Result<(Vec<syn::Item>, Vec<syn::Attribute>)>;
155
156fn generate_spec_and_assertions(
158 mut prusti_attributes: Vec<(SpecAttributeKind, TokenStream)>,
159 item: &untyped::AnyFnItem,
160) -> GeneratedResult {
161 let mut generated_items = vec![];
162 let mut generated_attributes = vec![];
163
164 for (attr_kind, attr_tokens) in prusti_attributes.drain(..) {
165 let rewriting_result = match attr_kind {
166 SpecAttributeKind::Requires => generate_for_requires(attr_tokens, item),
167 SpecAttributeKind::Ensures => generate_for_ensures(attr_tokens, item),
168 SpecAttributeKind::AfterExpiry => generate_for_after_expiry(attr_tokens, item),
169 SpecAttributeKind::AssertOnExpiry => generate_for_assert_on_expiry(attr_tokens, item),
170 SpecAttributeKind::Pure => generate_for_pure(attr_tokens, item),
171 SpecAttributeKind::Verified => generate_for_verified(attr_tokens, item),
172 SpecAttributeKind::Terminates => generate_for_terminates(attr_tokens, item),
173 SpecAttributeKind::Trusted => generate_for_trusted(attr_tokens, item),
174 SpecAttributeKind::Predicate => unreachable!(),
178 SpecAttributeKind::Invariant => unreachable!(),
179 SpecAttributeKind::RefineSpec => type_cond_specs::generate(attr_tokens, item),
180 SpecAttributeKind::Model => unreachable!(),
181 SpecAttributeKind::PrintCounterexample => unreachable!(),
182 };
183 let (new_items, new_attributes) = rewriting_result?;
184 generated_items.extend(new_items);
185 generated_attributes.extend(new_attributes);
186 }
187
188 Ok((generated_items, generated_attributes))
189}
190
191fn generate_for_requires(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
193 let mut rewriter = rewriter::AstRewriter::new();
194 let spec_id = rewriter.generate_spec_id();
195 let spec_id_str = spec_id.to_string();
196 let spec_item =
197 rewriter.process_assertion(rewriter::SpecItemType::Precondition, spec_id, attr, item)?;
198 Ok((
199 vec![spec_item],
200 vec![parse_quote_spanned! {item.span()=>
201 #[prusti::pre_spec_id_ref = #spec_id_str]
202 }],
203 ))
204}
205
206fn generate_for_ensures(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
208 let mut rewriter = rewriter::AstRewriter::new();
209 let spec_id = rewriter.generate_spec_id();
210 let spec_id_str = spec_id.to_string();
211 let spec_item =
212 rewriter.process_assertion(rewriter::SpecItemType::Postcondition, spec_id, attr, item)?;
213 Ok((
214 vec![spec_item],
215 vec![parse_quote_spanned! {item.span()=>
216 #[prusti::post_spec_id_ref = #spec_id_str]
217 }],
218 ))
219}
220
221fn generate_for_after_expiry(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
223 let mut rewriter = rewriter::AstRewriter::new();
224 let spec_id = rewriter.generate_spec_id();
225 let spec_id_str = spec_id.to_string();
226 let spec_item = rewriter.process_pledge(spec_id, attr, item)?;
227 Ok((
228 vec![spec_item],
229 vec![parse_quote_spanned! {item.span()=>
230 #[prusti::pledge_spec_id_ref = #spec_id_str]
231 }],
232 ))
233}
234
235fn generate_for_assert_on_expiry(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
237 let mut rewriter = rewriter::AstRewriter::new();
238 let spec_id_lhs = rewriter.generate_spec_id();
239 let spec_id_lhs_str = spec_id_lhs.to_string();
240 let spec_id_rhs = rewriter.generate_spec_id();
241 let spec_id_rhs_str = spec_id_rhs.to_string();
242 let (spec_item_lhs, spec_item_rhs) =
243 rewriter.process_assert_pledge(spec_id_lhs, spec_id_rhs, attr, item)?;
244 Ok((
245 vec![spec_item_lhs, spec_item_rhs],
246 vec![
247 parse_quote_spanned! {item.span()=>
248 #[prusti::assert_pledge_spec_id_ref_lhs = #spec_id_lhs_str]
249 },
250 parse_quote_spanned! {item.span()=>
251 #[prusti::assert_pledge_spec_id_ref_rhs = #spec_id_rhs_str]
252 },
253 ],
254 ))
255}
256
257fn generate_for_terminates(mut attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
259 if attr.is_empty() {
260 attr = quote! { Int::new(1) };
261 } else {
262 let mut attr_iter = attr.clone().into_iter();
263 let first = attr_iter.next();
264 if let Some(TokenTree::Ident(ident)) = first {
265 if attr_iter.next().is_none() && ident == "trusted" {
266 attr = quote! { prusti_terminates_trusted() }
267 }
268 }
269 }
270
271 let mut rewriter = rewriter::AstRewriter::new();
272 let spec_id = rewriter.generate_spec_id();
273 let spec_id_str = spec_id.to_string();
274 let spec_item =
275 rewriter.process_assertion(rewriter::SpecItemType::Termination, spec_id, attr, item)?;
276
277 Ok((
278 vec![spec_item],
279 vec![parse_quote_spanned! {item.span()=>
280 #[prusti::terminates_spec_id_ref = #spec_id_str]
281 }],
282 ))
283}
284
285fn generate_for_pure(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
287 if !attr.is_empty() {
288 return Err(syn::Error::new(
289 attr.span(),
290 "the `#[pure]` attribute does not take parameters",
291 ));
292 }
293
294 Ok((
295 vec![],
296 vec![parse_quote_spanned! {item.span()=>
297 #[prusti::pure]
298 }],
299 ))
300}
301
302fn generate_for_verified(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
304 if !attr.is_empty() {
305 return Err(syn::Error::new(
306 attr.span(),
307 "the `#[verified]` attribute does not take parameters",
308 ));
309 }
310
311 Ok((
312 vec![],
313 vec![parse_quote_spanned! {item.span()=>
314 #[prusti::verified]
315 }],
316 ))
317}
318
319fn generate_for_pure_refinements(item: &untyped::AnyFnItem) -> GeneratedResult {
321 let mut rewriter = rewriter::AstRewriter::new();
322 let spec_id = rewriter.generate_spec_id();
323 let spec_id_str = spec_id.to_string();
324 let spec_item = rewriter.process_pure_refinement(spec_id, item)?;
325
326 Ok((
327 vec![spec_item],
328 vec![parse_quote_spanned! {item.span()=>
329 #[prusti::pure_spec_id_ref = #spec_id_str]
330 }],
331 ))
332}
333
334fn generate_for_trusted(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
336 if !attr.is_empty() {
337 return Err(syn::Error::new(
338 attr.span(),
339 "the `#[trusted]` attribute does not take parameters",
340 ));
341 }
342
343 Ok((
344 vec![],
345 vec![parse_quote_spanned! {item.span()=>
346 #[prusti::trusted]
347 }],
348 ))
349}
350
351fn generate_for_trusted_for_types(attr: TokenStream, item: &syn::DeriveInput) -> GeneratedResult {
353 if !attr.is_empty() {
354 return Err(syn::Error::new(
355 attr.span(),
356 "the `#[trusted]` attribute does not take parameters",
357 ));
358 }
359 let mut rewriter = rewriter::AstRewriter::new();
361 let spec_id = rewriter.generate_spec_id();
362 let spec_id_str = spec_id.to_string();
363
364 let item_span = item.span();
365 let item_ident = item.ident.clone();
366 let item_name = syn::Ident::new(
367 &format!("prusti_trusted_item_{item_ident}_{spec_id}"),
368 item_span,
369 );
370
371 let spec_item: syn::ItemFn = parse_quote_spanned! {item_span=>
372 #[allow(unused_variables, dead_code, non_snake_case)]
373 #[prusti::spec_only]
374 #[prusti::trusted_type]
375 #[prusti::spec_id = #spec_id_str]
376 fn #item_name(self) {}
377 };
378
379 let generics = &item.generics;
380 let generics_idents = generics
381 .params
382 .iter()
383 .map(|generic_param| match generic_param {
384 syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam {
385 attrs: Vec::new(),
386 bounds: syn::punctuated::Punctuated::new(),
387 colon_token: None,
388 default: None,
389 eq_token: None,
390 ident: param.ident.clone(),
391 }),
392 syn::GenericParam::Lifetime(param) => syn::GenericParam::Lifetime(syn::LifetimeDef {
393 attrs: Vec::new(),
394 bounds: syn::punctuated::Punctuated::new(),
395 colon_token: None,
396 lifetime: param.lifetime.clone(),
397 }),
398 syn::GenericParam::Const(param) => syn::GenericParam::Const(syn::ConstParam {
399 attrs: Vec::new(),
400 colon_token: param.colon_token,
401 const_token: param.const_token,
402 default: None,
403 eq_token: None,
404 ident: param.ident.clone(),
405 ty: param.ty.clone(),
406 }),
407 })
408 .collect::<syn::punctuated::Punctuated<_, syn::Token![,]>>();
409 let item_impl: syn::ItemImpl = parse_quote_spanned! {item_span=>
411 impl #generics #item_ident <#generics_idents> {
412 #spec_item
413 }
414 };
415
416 Ok((vec![syn::Item::Impl(item_impl)], vec![]))
417}
418
419pub fn body_variant(tokens: TokenStream) -> TokenStream {
420 generate_expression_closure(&AstRewriter::process_loop_variant, tokens)
421}
422
423pub fn body_invariant(tokens: TokenStream) -> TokenStream {
424 generate_expression_closure(&AstRewriter::process_loop_invariant, tokens)
425}
426
427pub fn prusti_assertion(tokens: TokenStream) -> TokenStream {
428 generate_expression_closure(&AstRewriter::process_prusti_assertion, tokens)
429}
430
431pub fn prusti_assume(tokens: TokenStream) -> TokenStream {
432 generate_expression_closure(&AstRewriter::process_prusti_assumption, tokens)
433}
434
435pub fn prusti_refutation(tokens: TokenStream) -> TokenStream {
436 generate_expression_closure(&AstRewriter::process_prusti_refutation, tokens)
437}
438
439fn generate_expression_closure(
442 fun: &dyn Fn(&mut AstRewriter, SpecificationId, TokenStream) -> syn::Result<TokenStream>,
443 tokens: TokenStream,
444) -> TokenStream {
445 let mut rewriter = rewriter::AstRewriter::new();
446 let spec_id = rewriter.generate_spec_id();
447 let closure = handle_result!(fun(&mut rewriter, spec_id, tokens));
448 let callsite_span = Span::call_site();
449 quote_spanned! {callsite_span=>
450 #[allow(unused_must_use, unused_variables, unused_braces, unused_parens)]
451 #[prusti::specs_version = #SPECS_VERSION]
452 if false {
453 #closure
454 }
455 }
456}
457
458pub fn closure(tokens: TokenStream) -> TokenStream {
459 let cl_spec: ClosureWithSpec = handle_result!(syn::parse(tokens.into()));
460 let callsite_span = Span::call_site();
461
462 let mut rewriter = rewriter::AstRewriter::new();
463
464 let mut preconds: Vec<(SpecificationId, syn::Expr)> = vec![];
465 let mut postconds: Vec<(SpecificationId, syn::Expr)> = vec![];
466
467 let mut cl_annotations = TokenStream::new();
468
469 for r in cl_spec.pres {
470 let spec_id = rewriter.generate_spec_id();
471 let precond =
472 handle_result!(rewriter.process_closure_assertion(spec_id, r.to_token_stream(),));
473 preconds.push((spec_id, precond));
474 let spec_id_str = spec_id.to_string();
475 cl_annotations.extend(quote_spanned! {callsite_span=>
476 #[prusti::pre_spec_id_ref = #spec_id_str]
477 });
478 }
479
480 for e in cl_spec.posts {
481 let spec_id = rewriter.generate_spec_id();
482 let postcond =
483 handle_result!(rewriter.process_closure_assertion(spec_id, e.to_token_stream(),));
484 postconds.push((spec_id, postcond));
485 let spec_id_str = spec_id.to_string();
486 cl_annotations.extend(quote_spanned! {callsite_span=>
487 #[prusti::post_spec_id_ref = #spec_id_str]
488 });
489 }
490
491 let syn::ExprClosure {
492 attrs,
493 asyncness,
494 movability,
495 capture,
496 or1_token,
497 inputs,
498 or2_token,
499 output,
500 body,
501 } = cl_spec.cl;
502
503 let output_type: syn::Type = match output {
504 syn::ReturnType::Default => {
505 return syn::Error::new(output.span(), "closure must specify return type")
506 .to_compile_error();
507 }
508 syn::ReturnType::Type(_, ref ty) => (**ty).clone(),
509 };
510
511 let (spec_toks_pre, spec_toks_post) =
512 handle_result!(rewriter.process_closure(inputs.clone(), output_type, preconds, postconds,));
513
514 let mut attrs_ts = TokenStream::new();
515 for a in attrs {
516 attrs_ts.extend(a.into_token_stream());
517 }
518
519 quote_spanned! {callsite_span=>
520 {
521 #[allow(unused_variables, unused_braces, unused_parens)]
522 #[prusti::closure]
523 #[prusti::specs_version = #SPECS_VERSION]
524 #cl_annotations #attrs_ts
525 let _prusti_closure =
526 #asyncness #movability #capture
527 #or1_token #inputs #or2_token #output
528 {
529 #[allow(unused_must_use, unused_braces, unused_parens)]
530 if false {
531 #spec_toks_pre
532 }
533 let result = #body ;
534 #[allow(unused_must_use, unused_braces, unused_parens)]
535 if false {
536 #spec_toks_post
537 }
538 result
539 };
540 _prusti_closure
541 }
542 }
543}
544
545pub fn refine_trait_spec(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
546 let mut impl_block: syn::ItemImpl = handle_result!(syn::parse2(tokens));
547 let impl_generics = &impl_block.generics;
548
549 let trait_path: syn::TypePath = match &impl_block.trait_ {
550 Some((_, trait_path, _)) => parse_quote_spanned!(trait_path.span()=>#trait_path),
551 None => handle_result!(Err(syn::Error::new(
552 impl_block.span(),
553 "Can refine trait specifications only on trait implementation blocks"
554 ))),
555 };
556
557 let self_type: &syn::Type = &impl_block.self_ty;
558
559 let mut new_items = Vec::new();
560 let mut generated_spec_items = Vec::new();
561 for item in impl_block.items {
562 match item {
563 syn::ImplItem::Method(method) => {
564 let mut method_item = untyped::AnyFnItem::ImplMethod(method);
565 let prusti_attributes: Vec<_> = extract_prusti_attributes(&mut method_item);
566
567 let illegal_attribute_span = prusti_attributes
568 .iter()
569 .filter(|(kind, _)| kind == &SpecAttributeKind::RefineSpec)
570 .map(|(_, tokens)| tokens.span())
571 .next();
572 if let Some(span) = illegal_attribute_span {
573 let err = Err(syn::Error::new(
574 span,
575 "Type-conditional spec refinements in trait spec refinements not supported",
576 ));
577 handle_result!(err);
578 }
579
580 let (spec_items, generated_attributes) = handle_result!(
581 generate_spec_and_assertions(prusti_attributes, &method_item)
582 );
583
584 spec_items
585 .into_iter()
586 .map(|spec_item| match spec_item {
587 syn::Item::Fn(spec_item_fn) => spec_item_fn,
588 x => unimplemented!("Unexpected variant: {:?}", x),
589 })
590 .for_each(|spec_item_fn| generated_spec_items.push(spec_item_fn));
591
592 let new_item = parse_quote_spanned! {method_item.span()=>
593 #(#generated_attributes)*
594 #method_item
595 };
596 new_items.push(new_item);
597 }
598 syn::ImplItem::Macro(makro) if is_predicate_macro(&makro) => {
599 let parsed_predicate =
600 handle_result!(predicate::parse_predicate_in_impl(makro.mac.tokens.clone()));
601
602 let ParsedPredicate::Impl(predicate) = parsed_predicate else {
603 unreachable!()
604 };
605
606 let syn::Item::Fn(spec_function) = predicate.spec_function else {
608 unreachable!()
609 };
610 generated_spec_items.push(spec_function);
611
612 new_items.push(syn::ImplItem::Method(predicate.patched_function));
614 }
615 _ => new_items.push(item),
616 }
617 }
618
619 for generated_spec_item in generated_spec_items.iter_mut() {
621 merge_generics(&mut generated_spec_item.sig.generics, impl_generics);
622 generated_spec_item.rewrite_self_type(self_type, Some(&trait_path));
623 generated_spec_item.rewrite_receiver(self_type);
624 }
625
626 impl_block.items = new_items;
627 quote_spanned! {impl_block.span()=>
628 #(#generated_spec_items)*
629 #[prusti::specs_version = #SPECS_VERSION]
630 #impl_block
631 }
632}
633
634pub fn trusted(attr: TokenStream, tokens: TokenStream) -> TokenStream {
635 if !attr.is_empty() {
636 return syn::Error::new(
637 attr.span(),
638 "the `#[trusted]` attribute does not take parameters",
639 )
640 .to_compile_error();
641 }
642
643 if syn::parse2::<syn::DeriveInput>(tokens.clone()).is_ok() {
646 let mut rewriter = rewriter::AstRewriter::new();
648 let spec_id = rewriter.generate_spec_id();
649 let spec_id_str = spec_id.to_string();
650
651 let item: syn::DeriveInput = handle_result!(syn::parse2(tokens));
652 let item_span = item.span();
653
654 #[allow(clippy::redundant_clone)]
656 let item_ident = item.ident.clone();
657
658 let item_name = syn::Ident::new(
659 &format!("prusti_trusted_item_{item_ident}_{spec_id}"),
660 item_span,
661 );
662
663 let spec_item: syn::ItemFn = parse_quote_spanned! {item_span=>
664 #[allow(unused_variables, dead_code, non_snake_case)]
665 #[prusti::spec_only]
666 #[prusti::trusted_type]
667 #[prusti::spec_id = #spec_id_str]
668 fn #item_name(self) {}
669 };
670
671 let generics = &item.generics;
672 let generics_idents = generics
673 .params
674 .iter()
675 .map(|generic_param| match generic_param {
676 syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam {
677 attrs: Vec::new(),
678 bounds: syn::punctuated::Punctuated::new(),
679 colon_token: None,
680 default: None,
681 eq_token: None,
682 ident: param.ident.clone(),
683 }),
684 syn::GenericParam::Lifetime(param) => {
685 syn::GenericParam::Lifetime(syn::LifetimeDef {
686 attrs: Vec::new(),
687 bounds: syn::punctuated::Punctuated::new(),
688 colon_token: None,
689 lifetime: param.lifetime.clone(),
690 })
691 }
692 syn::GenericParam::Const(param) => syn::GenericParam::Const(syn::ConstParam {
693 attrs: Vec::new(),
694 colon_token: param.colon_token,
695 const_token: param.const_token,
696 default: None,
697 eq_token: None,
698 ident: param.ident.clone(),
699 ty: param.ty.clone(),
700 }),
701 })
702 .collect::<syn::punctuated::Punctuated<_, syn::Token![,]>>();
703 let item_impl: syn::ItemImpl = parse_quote_spanned! {item_span=>
705 impl #generics #item_ident <#generics_idents> {
706 #spec_item
707 }
708 };
709 quote_spanned! { item_span =>
710 #[prusti::specs_version = #SPECS_VERSION]
711 #item
712 #item_impl
713 }
714 } else {
715 rewrite_prusti_attributes(SpecAttributeKind::Trusted, attr, tokens)
716 }
717}
718
719pub fn invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream {
720 let mut rewriter = rewriter::AstRewriter::new();
721 let spec_id = rewriter.generate_spec_id();
722 let spec_id_str = spec_id.to_string();
723
724 let item: syn::DeriveInput = handle_result!(syn::parse2(tokens));
725 let item_span = item.span();
726
727 #[allow(clippy::redundant_clone)]
729 let item_ident = item.ident.clone();
730
731 let item_name = syn::Ident::new(
732 &format!("prusti_invariant_item_{item_ident}_{spec_id}"),
733 item_span,
734 );
735
736 let attr = handle_result!(parse_prusti(attr));
737
738 let spec_item: syn::ItemFn = parse_quote_spanned! {item_span=>
741 #[allow(unused_must_use, unused_parens, unused_variables, dead_code, non_snake_case)]
742 #[prusti::spec_only]
743 #[prusti::type_invariant_spec]
744 #[prusti::spec_id = #spec_id_str]
745 fn #item_name(self) -> bool {
746 !!(#attr)
747 }
748 };
749
750 #[allow(clippy::redundant_clone)]
752 let generics = item.generics.clone();
753
754 let generics_idents = generics
755 .params
756 .iter()
757 .filter_map(|generic_param| match generic_param {
758 syn::GenericParam::Type(type_param) => Some(type_param.ident.clone()),
759 _ => None,
760 })
761 .collect::<syn::punctuated::Punctuated<_, syn::Token![,]>>();
762 let item_impl: syn::ItemImpl = parse_quote_spanned! {item_span=>
764 impl #generics #item_ident < #generics_idents > {
765 #spec_item
766 }
767 };
768 quote_spanned! { item_span =>
769 #[prusti::specs_version = #SPECS_VERSION]
770 #item
771 #item_impl
772 }
773}
774
775pub fn extern_spec(attr: TokenStream, tokens: TokenStream) -> TokenStream {
776 result_to_tokens!({
777 let item: syn::Item = syn::parse2(tokens)?;
778 let mod_path: syn::Path = Some(attr)
779 .filter(|attr| !attr.is_empty())
780 .map(syn::parse2)
781 .transpose()?
782 .unwrap_or_else(|| syn::Path {
783 leading_colon: None,
784 segments: syn::punctuated::Punctuated::new(),
785 });
786 match item {
787 syn::Item::Impl(item_impl) => {
788 if !mod_path.segments.is_empty() {
789 return Err(syn::Error::new(
790 mod_path.span(),
791 "extern_spec does not take a path argument for impls--you can qualify the involved types directly",
792 ));
793 }
794 extern_spec_rewriter::impls::rewrite_extern_spec(&item_impl)
795 }
796 syn::Item::Trait(item_trait) => {
797 extern_spec_rewriter::traits::rewrite_extern_spec(&item_trait, mod_path)
798 }
799 syn::Item::Mod(item_mod) => {
800 extern_spec_rewriter::mods::rewrite_mod(&item_mod, mod_path)
801 }
802 syn::Item::ForeignMod(item_foreign_mod) => {
803 extern_spec_rewriter::foreign_mods::rewrite_extern_spec(
804 &item_foreign_mod,
805 &mod_path,
806 )
807 }
808 syn::Item::Verbatim(stub_tokens) => {
810 extern_spec_rewriter::functions::rewrite_stub(&stub_tokens, &mod_path, false)
811 }
812 _ => Err(syn::Error::new(
813 Span::call_site(), "Extern specs cannot be attached to this item",
815 )),
816 }
817 })
818}
819
820pub fn predicate(tokens: TokenStream) -> TokenStream {
821 let parsed = handle_result!(predicate::parse_predicate(tokens));
822 parsed.into_token_stream()
823}
824
825pub fn rewrite_prusti_attributes_for_types(
826 outer_attr_kind: SpecAttributeKind,
827 outer_attr_tokens: TokenStream,
828 item_tokens: TokenStream,
829) -> TokenStream {
830 let mut item: syn::DeriveInput = handle_result!(syn::parse2(item_tokens));
831
832 let mut prusti_attributes = vec![(outer_attr_kind, outer_attr_tokens)];
834
835 prusti_attributes.extend(extract_prusti_attributes_for_types(&mut item));
837
838 if prusti_attributes.len() > 1
839 && prusti_attributes
840 .iter()
841 .any(|(ak, _)| ak == &SpecAttributeKind::Trusted)
842 {
843 return syn::Error::new(
844 item.span(),
845 "`trusted!` is incompatible with other Prusti attributes",
846 )
847 .to_compile_error();
848 }
849
850 prusti_attributes.sort_by(|(ak1, _), (ak2, _)| ak1.cmp(ak2));
852
853 let (generated_spec_items, generated_attributes) = handle_result!(
854 generate_spec_and_assertions_for_types(prusti_attributes, &mut item)
855 );
856
857 quote_spanned! {item.span()=>
858 #(#generated_attributes)*
859 #item
860 #(#generated_spec_items)*
861 }
862}
863
864fn extract_prusti_attributes_for_types(
865 item: &mut syn::DeriveInput,
866) -> Vec<(SpecAttributeKind, TokenStream)> {
867 let mut prusti_attributes = Vec::new();
868 let mut regular_attributes = Vec::new();
869 for attr in item.attrs.drain(0..) {
870 if attr.path.segments.len() == 1 {
871 if let Ok(attr_kind) = attr.path.segments[0].ident.to_string().try_into() {
872 let tokens = match attr_kind {
873 SpecAttributeKind::Requires => unreachable!("requires on type"),
874 SpecAttributeKind::Ensures => unreachable!("ensures on type"),
875 SpecAttributeKind::AfterExpiry => unreachable!("after_expiry on type"),
876 SpecAttributeKind::AssertOnExpiry => unreachable!("assert_on_expiry on type"),
877 SpecAttributeKind::RefineSpec => unreachable!("refine_spec on type"),
878 SpecAttributeKind::Pure => unreachable!("pure on type"),
879 SpecAttributeKind::Verified => unreachable!("verified on type"),
880 SpecAttributeKind::Invariant => unreachable!("invariant on type"),
881 SpecAttributeKind::Predicate => unreachable!("predicate on type"),
882 SpecAttributeKind::Terminates => unreachable!("terminates on type"),
883 SpecAttributeKind::Trusted | SpecAttributeKind::Model => {
884 assert!(attr.tokens.is_empty(), "Unexpected shape of an attribute.");
885 attr.tokens
886 }
887 SpecAttributeKind::PrintCounterexample => {
888 let mut iter = attr.tokens.into_iter();
892 let TokenTree::Group(group) = iter.next().unwrap() else {
893 unreachable!()
894 };
895 group.stream()
896 }
897 };
898 prusti_attributes.push((attr_kind, tokens));
899 } else {
900 regular_attributes.push(attr);
901 }
902 } else {
903 regular_attributes.push(attr);
904 }
905 }
906 item.attrs = regular_attributes;
907 prusti_attributes
908}
909
910fn generate_spec_and_assertions_for_types(
912 mut prusti_attributes: Vec<(SpecAttributeKind, TokenStream)>,
913 item: &mut syn::DeriveInput,
914) -> GeneratedResult {
915 let mut generated_items = vec![];
916 let mut generated_attributes = vec![];
917
918 for (attr_kind, attr_tokens) in prusti_attributes.drain(..) {
919 let rewriting_result = match attr_kind {
920 SpecAttributeKind::Requires => unreachable!(),
921 SpecAttributeKind::Ensures => unreachable!(),
922 SpecAttributeKind::AfterExpiry => unreachable!(),
923 SpecAttributeKind::AssertOnExpiry => unreachable!(),
924 SpecAttributeKind::Pure => unreachable!(),
925 SpecAttributeKind::Verified => unreachable!(),
926 SpecAttributeKind::Predicate => unreachable!(),
927 SpecAttributeKind::Invariant => unreachable!(),
928 SpecAttributeKind::RefineSpec => unreachable!(),
929 SpecAttributeKind::Terminates => unreachable!(),
930 SpecAttributeKind::Trusted => generate_for_trusted_for_types(attr_tokens, item),
931 SpecAttributeKind::Model => generate_for_model(attr_tokens, item),
932 SpecAttributeKind::PrintCounterexample => {
933 generate_for_print_counterexample(attr_tokens, item)
934 }
935 };
936 let (new_items, new_attributes) = rewriting_result?;
937 generated_items.extend(new_items);
938 generated_attributes.extend(new_attributes);
939 }
940
941 Ok((generated_items, generated_attributes))
942}
943
944fn generate_for_model(attr: TokenStream, item: &mut syn::DeriveInput) -> GeneratedResult {
946 match syn::Item::from(item.clone()) {
947 syn::Item::Struct(item_struct) => {
948 match type_model::rewrite(item_struct) {
949 Ok(result) => {
950 match result.first() {
951 Some(syn::Item::Struct(new_item)) => {
952 *item = syn::DeriveInput::from(new_item.clone()); Ok((vec![result[1].clone(), result[2].clone()], vec![]))
954 }
955 _ => unreachable!(),
956 }
957 }
958 Err(err) => Err(err),
959 }
960 }
961 _ => Err(syn::Error::new(
962 attr.span(),
963 "Only structs can be attributed with a type model",
964 )),
965 }
966}
967
968fn generate_for_print_counterexample(
970 attr: TokenStream,
971 item: &mut syn::DeriveInput,
972) -> GeneratedResult {
973 match syn::Item::from(item.clone()) {
974 syn::Item::Struct(item_struct) => {
975 match print_counterexample::rewrite_struct(attr, item_struct) {
976 Ok(result) => Ok((result, vec![])),
977 Err(err) => Err(err),
978 }
979 }
980 syn::Item::Enum(item_enum) => {
981 match print_counterexample::rewrite_enum(attr, item_enum) {
982 Ok(result) => {
983 match result.first() {
984 Some(syn::Item::Enum(new_item)) => {
985 *item = syn::DeriveInput::from(new_item.clone()); Ok((vec![result[1].clone()], vec![]))
987 }
988 _ => unreachable!(),
989 }
990 }
991 Err(err) => Err(err),
992 }
993 }
994 _ => Err(syn::Error::new(
995 attr.span(),
996 "Only structs and enums can be attributed with a custom counterexample print",
997 )),
998 }
999}
1000
1001pub fn type_model(attr: TokenStream, tokens: TokenStream) -> TokenStream {
1002 if syn::parse2::<syn::DeriveInput>(tokens.clone()).is_ok() {
1003 rewrite_prusti_attributes_for_types(SpecAttributeKind::Model, attr, tokens)
1004 } else {
1005 syn::Error::new(
1006 attr.span(),
1007 "Only structs can be attributed with a type model",
1008 )
1009 .to_compile_error()
1010 }
1011}
1012
1013pub fn print_counterexample(attr: TokenStream, tokens: TokenStream) -> TokenStream {
1014 if syn::parse2::<syn::DeriveInput>(tokens.clone()).is_ok() {
1015 rewrite_prusti_attributes_for_types(SpecAttributeKind::PrintCounterexample, attr, tokens)
1016 } else {
1017 syn::Error::new(
1018 attr.span(),
1019 "Only structs and enums can be attributed with print_counterexample",
1020 )
1021 .to_compile_error()
1022 }
1023}
1024pub fn ghost(tokens: TokenStream) -> TokenStream {
1025 let mut rewriter = rewriter::AstRewriter::new();
1026 let callsite_span = Span::call_site();
1027
1028 let spec_id = rewriter.generate_spec_id();
1029 let spec_id_str = spec_id.to_string();
1030
1031 let make_closure = |kind| {
1032 quote! {
1033 #[allow(unused_must_use, unused_variables, unused_braces, unused_parens)]
1034 if false {
1035 #[prusti::spec_only]
1036 #[prusti::#kind]
1037 #[prusti::spec_id = #spec_id_str]
1038 || -> () {};
1039 }
1040 }
1041 };
1042
1043 struct Visitor {
1044 loops: Vec<(Option<syn::Ident>, Span)>,
1045 breaks: Vec<(Option<syn::Ident>, Span)>,
1046 returns: Option<Span>,
1047 }
1048
1049 impl<'ast> Visit<'ast> for Visitor {
1050 fn visit_expr_for_loop(&mut self, ex: &'ast syn::ExprForLoop) {
1051 let e = ex.clone();
1052 let lbl = e.label.map(|c| c.name.ident);
1053 let span = e.body.brace_token.span;
1054 self.loops.push((lbl, span));
1055 syn::visit::visit_expr_for_loop(self, ex);
1056 }
1057 fn visit_expr_while(&mut self, ex: &'ast syn::ExprWhile) {
1058 let e = ex.clone();
1059 let lbl = e.label.map(|c| c.name.ident);
1060 let span = e.body.brace_token.span;
1061 self.loops.push((lbl, span));
1062 syn::visit::visit_expr_while(self, ex);
1063 }
1064 fn visit_expr_loop(&mut self, ex: &'ast syn::ExprLoop) {
1065 let e = ex.clone();
1066 let lbl = e.label.map(|c| c.name.ident);
1067 let span = e.body.brace_token.span;
1068 self.loops.push((lbl, span));
1069 syn::visit::visit_expr_loop(self, ex);
1070 }
1071 fn visit_expr_continue(&mut self, ex: &'ast syn::ExprContinue) {
1072 let e = ex.clone();
1073 let lbl = e.label.map(|c| c.ident);
1074 self.breaks.push((lbl, ex.span()));
1075 syn::visit::visit_expr_continue(self, ex);
1076 }
1077 fn visit_expr_break(&mut self, ex: &'ast syn::ExprBreak) {
1078 let e = ex.clone();
1079 let lbl = e.label.map(|c| c.ident);
1080 self.breaks.push((lbl, ex.span()));
1081 syn::visit::visit_expr_break(self, ex);
1082 }
1083 fn visit_expr_return(&mut self, e: &'ast syn::ExprReturn) {
1084 let e = e.clone();
1085 self.returns = Some(e.span());
1086 }
1087 }
1088
1089 let mut visitor = Visitor {
1090 loops: vec![],
1091 breaks: vec![],
1092 returns: None,
1093 };
1094
1095 let tokens = quote! {
1096 {#tokens}
1097 };
1098
1099 let input = syn::parse::<syn::Block>(tokens.clone().into()).unwrap();
1100
1101 visitor.visit_block(&input);
1102
1103 let mut exit_errors = visitor.returns.into_iter().collect::<Vec<_>>();
1104
1105 'breaks: for (break_label, break_span) in visitor.breaks.iter() {
1106 for (loop_label, loop_span) in visitor.loops.iter() {
1107 let loop_span = loop_span.unwrap();
1108 let label_match = break_label == loop_label || break_label.is_none();
1109 let break_inside = loop_span.join(break_span.unwrap()).unwrap().eq(&loop_span);
1110 if label_match && break_inside {
1111 continue 'breaks;
1112 }
1113 }
1114 exit_errors.push(*break_span);
1115 }
1116
1117 let begin = make_closure(quote! {ghost_begin});
1118 let end = make_closure(quote! {ghost_end});
1119
1120 if exit_errors.is_empty() {
1121 quote_spanned! {callsite_span=>
1122 {
1123 #begin
1124 #[prusti::specs_version = #SPECS_VERSION]
1125 let ghost_result = Ghost::new(#tokens);
1126 #end
1127 ghost_result
1128 }
1129 }
1130 } else {
1131 let mut syn_errors = quote! {};
1132 for error in exit_errors {
1133 let error =
1134 syn::Error::new(error, "Can't leave the ghost block early").to_compile_error();
1135 syn_errors = quote! {
1136 #syn_errors
1137 #error
1138 }
1139 }
1140 syn_errors
1141 }
1142}