1#![allow(dead_code)]
2use std::{
3 collections::{HashMap, VecDeque},
4 fmt,
5 net::UdpSocket,
6};
7
8use proc_macro2::{Punct, Spacing, TokenStream as TokenStream2, TokenTree};
10use quote::{format_ident, quote};
11use syn::{
12 parse::{Parse, ParseStream, Result},
13 punctuated::Punctuated,
14 visit_mut::VisitMut,
15 Attribute, Expr, ExprLit, ExprPath, Lit, Meta, MetaNameValue, PathSegment, Token, TraitItem,
16 TraitItemMethod,
17};
18
19use companion::{companion_addr, Response, Task};
20
21use crate::MAX_UDP_PAYLOAD;
22
23pub struct AspectJointPoint<'a> {
24 pub stream: &'a TokenStream2,
25}
26
27impl<'a> VisitMut for AspectJointPoint<'a> {
28 fn visit_expr_mut(&mut self, item: &mut Expr) {
29 if let Expr::Call(ref node) = item {
30 if let Expr::Path(call) = node.func.as_ref() {
31 let segments = &call.path.segments;
32 if segments.len() == 2 {
33 if let (Some(first), Some(last)) = (segments.first(), segments.last()) {
34 if first.ident.to_string() == "AspectJointPoint"
35 && last.ident.to_string() == "proceed"
36 {
37 let stream = self.stream;
39 *item = syn::parse_quote!(#stream);
40 }
41 }
42 }
43 }
44 }
45
46 syn::visit_mut::visit_expr_mut(self, item);
48 }
49}
50
51#[derive(Default, Debug)]
52pub struct Aspect {
53 pub name: String,
54 pub docs: VecDeque<String>,
55 pub before: Option<TraitItemMethod>,
56 pub after: Option<TraitItemMethod>,
57 pub around: Option<TraitItemMethod>,
58}
59
60impl Aspect {
61 pub fn new(attr: TokenStream2) -> Self {
62 match syn::parse2::<syn::TypePath>(attr.clone()) {
64 Ok(_) => {}
65 Err(_) => panic!("Usage: #[aspect(std::fmt::Display)]"),
66 }
67
68 let key = attr.to_string().replace(" :: ", "::");
69
70 let addr = companion_addr();
71
72 let socket = UdpSocket::bind("[::]:0").unwrap();
73 socket.connect(addr).unwrap();
74 let mut buf = [0; MAX_UDP_PAYLOAD];
75
76 socket.send(&Task::Get(&key).as_bytes()).unwrap();
77 let (len, _src) = socket.recv_from(&mut buf).unwrap();
78 let resp = Response::from(&buf[..len]);
79
80 if let Response::String(data) = resp {
81 let item_trait: syn::ItemTrait = syn_serde::json::from_str(&data).unwrap();
82 let mut aspect = Aspect::default();
83 aspect.name = key;
84 aspect.docs = collect_docs(&item_trait.attrs);
85
86 for item in item_trait.items.iter() {
88 match item {
89 TraitItem::Method(method) => {
90 let name = method.sig.ident.to_string();
92 match name.as_str() {
94 "before" => aspect.before = Some(method.clone()),
95 "after" => aspect.after = Some(method.clone()),
96 "around" => aspect.around = Some(method.clone()),
97 _ => {
98 println!("Incompatible Aspect trait")
99 }
100 }
101 }
102 _ => {
103 println!("Incompatible Aspect trait")
104 }
105 }
106 }
107
108 aspect
109 } else {
110 panic!("Aspect is not registered")
111 }
112 }
113
114 pub fn documentation(&self) -> VecDeque<String> {
115 let mut output: VecDeque<String> = VecDeque::new();
116
117 let mut before_docs: VecDeque<String> = VecDeque::new();
118 let mut after_docs: VecDeque<String> = VecDeque::new();
119 let mut around_docs: VecDeque<String> = VecDeque::new();
120
121 if let Some(ref method) = self.before {
122 before_docs = collect_docs(&method.attrs);
123 }
124
125 if let Some(ref method) = self.around {
126 around_docs = collect_docs(&method.attrs);
127 }
128
129 if let Some(ref method) = self.after {
130 after_docs = collect_docs(&method.attrs);
131 }
132
133 if !self.docs.is_empty()
135 || !before_docs.is_empty()
136 || !after_docs.is_empty()
137 || !around_docs.is_empty()
138 {
139 output.push_back(format!(" ### {}", self.name));
140 self.docs.iter().for_each(|item| {
141 output.push_back(item.clone());
142 });
143
144 if !before_docs.is_empty() {
145 output.push_back(String::from(" - __Before:__"));
147
148 before_docs.iter().for_each(|item| {
149 output.push_back(item.clone());
150 });
151 }
152
153 if !around_docs.is_empty() {
154 output.push_back(String::from(" - __Around:__"));
156
157 around_docs.iter().for_each(|item| {
158 output.push_back(item.clone());
159 });
160 }
161
162 if !after_docs.is_empty() {
163 output.push_back(String::from(" - __After:__"));
165
166 after_docs.iter().for_each(|item| {
167 output.push_back(item.clone());
168 });
169 }
170 }
171
172 output
173 }
174}
175
176fn collect_docs(attrs: &Vec<Attribute>) -> VecDeque<String> {
177 let mut docs: VecDeque<String> = VecDeque::new();
178 attrs.iter().for_each(|attr| {
179 if attr.path.is_ident("doc") {
181 match attr.parse_meta().unwrap() {
182 Meta::NameValue(MetaNameValue {
183 lit: Lit::Str(lit_str),
184 ..
185 }) => {
186 docs.push_back(lit_str.value());
187 }
188 _ => {}
189 }
190 }
191 });
192 docs
193}
194
195fn stream_ranges(input: &Vec<TokenTree>) -> Vec<(usize, usize)> {
197 let mut ranges: Vec<(usize, usize)> = vec![];
198
199 let mut tail = &TokenTree::Punct(Punct::new(' ', Spacing::Alone));
200 let mut start_current = 0;
201
202 for (idx, attr) in input.iter().enumerate() {
204 if let TokenTree::Punct(cur) = attr {
205 if cur.as_char() == '>' && cur.spacing() == Spacing::Alone {
206 if let TokenTree::Punct(prev) = tail {
207 if prev.as_char() == '-' && prev.spacing() == Spacing::Joint {
208 ranges.push((start_current, idx - 1));
209 start_current = idx + 1;
210 }
211 }
212 }
213 }
214
215 tail = attr;
216 }
217
218 if start_current < input.len() {
219 ranges.push((start_current, input.len()));
220 }
221
222 ranges
223}
224
225#[derive(Debug, Copy, Clone, Eq, PartialEq)]
227pub enum Mode {
228 Always,
230 Disabled,
232 Debug,
234 Test,
236 LogOnly,
239}
240
241impl Mode {
242 pub fn name(self) -> Option<&'static str> {
244 match self {
245 Mode::Always => Some(""),
246 Mode::Disabled => None,
247 Mode::Debug => Some("debug_"),
248 Mode::Test => Some("test_"),
249 Mode::LogOnly => None,
250 }
251 }
252
253 pub fn final_mode(self) -> Self {
255 if self == Mode::Disabled || self == Mode::Test {
258 return self;
259 }
260
261 if cfg!(feature = "disable_contracts") {
262 Mode::Disabled
263 } else if cfg!(feature = "override_debug") {
264 if self == Mode::LogOnly {
266 self
267 } else {
268 Mode::Debug
269 }
270 } else if cfg!(feature = "override_log") {
271 Mode::LogOnly
272 } else {
273 self
274 }
275 }
276}
277
278#[derive(Debug, Copy, Clone, PartialEq, Eq)]
280pub enum Type {
281 Requires,
282 Ensures,
283 Invariant,
284 Aspect,
285}
286
287impl Type {
288 pub fn message_name(self) -> &'static str {
291 match self {
292 Type::Requires => "Pre-condition",
293 Type::Ensures => "Post-condition",
294 Type::Invariant => "Invariant",
295 Type::Aspect => "Invariant",
296 }
297 }
298
299 pub fn type_and_mode(ident: &str) -> Option<(Type, Mode)> {
301 match ident {
302 "aspect" => Some((Type::Aspect, Mode::Always)),
303 "requires" => Some((Type::Requires, Mode::Always)),
304 "ensures" => Some((Type::Ensures, Mode::Always)),
305 "invariant" => Some((Type::Invariant, Mode::Always)),
306 "debug_requires" => Some((Type::Requires, Mode::Debug)),
307 "debug_ensures" => Some((Type::Ensures, Mode::Debug)),
308 "debug_invariant" => Some((Type::Invariant, Mode::Debug)),
309 "test_requires" => Some((Type::Requires, Mode::Test)),
310 "test_ensures" => Some((Type::Ensures, Mode::Test)),
311 "test_invariant" => Some((Type::Invariant, Mode::Test)),
312 _ => None,
313 }
314 }
315}
316
317#[derive(Debug)]
319pub struct CaseRule(Vec<RuleExpression>);
320
321impl CaseRule {
322 fn add(&mut self, value: RuleExpression) {
323 self.0.push(value);
324 }
325}
326
327impl Default for CaseRule {
328 fn default() -> Self {
329 Self(Default::default())
330 }
331}
332
333#[derive(Debug)]
335enum RuleExpression {
336 If(Expr),
337 Expr(Expr),
338 }
340
341pub struct Contract {
342 pub ty: Type,
343 pub mode: Mode,
344 pub desc: Option<String>,
345 pub rules: Vec<CaseRule>,
346 pub decls: HashMap<String, Expr>,
348}
349
350impl Contract {
351 pub fn syntax(&self) -> Vec<TokenStream2> {
352 let assert = match self.mode {
353 Mode::Always => format_ident!("assert"),
354 Mode::Debug | Mode::Test => {
355 format_ident!("debug_assert")
356 }
357 Mode::Disabled | Mode::LogOnly => return vec![],
358 };
359
360 self.rules
361 .iter()
362 .map(|rule| {
363 let mut holder = quote! {};
364
365 for expr in rule.0.iter().rev() {
366 match expr {
367 RuleExpression::If(e) => {
368 holder = quote! {
369 if (#e) {
370 #holder
371 }
372 };
373 }
374 RuleExpression::Expr(e) => match self.desc {
375 Some(ref desc) => {
376 holder = quote! {
377 #assert!(#e, #desc);
378 };
379 }
380 None => {
381 holder = quote! {
382 #assert!(#e);
383 };
384 }
385 },
386 }
387 }
388 holder
389 })
390 .collect()
391 }
392}
393
394impl fmt::Debug for Contract {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 match &self.desc {
397 Some(desc) => {
398 write!(
399 f,
400 "{:?}:{:?}, {} rules [{desc}]",
401 self.mode,
402 self.ty,
403 self.rules.len()
404 )
405 }
406 None => {
407 write!(
408 f,
409 "{:?}:{:?}, {} rules",
410 self.mode,
411 self.ty,
412 self.rules.len()
413 )
414 }
415 }
416 }
417}
418
419impl Contract {
420 pub fn new(ty: Type, mode: Mode) -> Self {
421 Self {
422 ty,
423 mode,
424 desc: None,
425 rules: Vec::new(),
426 decls: HashMap::new(),
427 }
428 }
429
430 pub fn parse_attributes(&mut self, stream: TokenStream2) {
433 let stream = stream.into_iter().collect::<Vec<_>>();
435
436 let ranges = stream_ranges(&stream);
437
438 let mut range_iter = ranges.iter().peekable();
440
441 let mut rule = CaseRule::default();
443 while let Some(range) = range_iter.next() {
444 let stream = stream[range.0..range.1]
445 .iter()
446 .map(|x| x.clone())
447 .collect::<TokenStream2>();
448 let Segment { expressions } = syn::parse2(stream).unwrap();
449
450 let is_last_segment = range_iter.peek().is_none();
451
452 let mut expr_iter = expressions.iter().peekable();
453 while let Some(expr) = expr_iter.next() {
454 let is_last_expr = expr_iter.peek().is_none();
455
456 if !is_last_segment && is_last_expr {
457 match expr {
458 Expr::Lit(_) => panic!("Description should be last element"),
459 _ => {
460 let mut expr = expr.clone();
461 let mut replacer = OldPseudo::default();
462 replacer.visit_expr_mut(&mut expr);
463
464 if self.ty != Type::Ensures && !replacer.items.is_empty() {
465 panic!("Only ensures support 'old' pseudo-expressions")
466 }
467
468 replacer.items.into_iter().for_each(|(k, v)| {
469 self.decls.insert(k, v);
470 });
471
472 rule.add(RuleExpression::If(expr));
473 }
474 }
475 } else if is_last_segment && !is_last_expr {
476 match expr {
477 Expr::Lit(_) => panic!("Description should be last element"),
478 _ => {
479 let mut expr = expr.clone();
480 let mut replacer = OldPseudo::default();
481 replacer.visit_expr_mut(&mut expr);
482
483 if self.ty != Type::Ensures && !replacer.items.is_empty() {
484 panic!("Only ensures support 'old' pseudo-expressions")
485 }
486
487 replacer.items.into_iter().for_each(|(k, v)| {
488 self.decls.insert(k, v);
489 });
490
491 rule.add(RuleExpression::Expr(expr));
492 self.rules.push(rule);
493 rule = CaseRule::default();
494 }
495 }
496 } else if is_last_segment && is_last_expr {
497 match expr {
499 Expr::Lit(ExprLit {
500 lit: Lit::Str(token),
501 ..
502 }) => {
503 self.desc = Some(token.value());
504 }
505 _ => {
506 let mut expr = expr.clone();
507 let mut replacer = OldPseudo::default();
508 replacer.visit_expr_mut(&mut expr);
509
510 if self.ty != Type::Ensures && !replacer.items.is_empty() {
511 panic!("Only ensures support 'old' pseudo-expressions")
512 }
513
514 replacer.items.into_iter().for_each(|(k, v)| {
515 self.decls.insert(k, v);
516 });
517
518 rule.add(RuleExpression::Expr(expr));
519 self.rules.push(rule);
520 rule = CaseRule::default();
521 }
522 }
523 } else {
524 match expr {
525 Expr::Lit(_) => panic!("Description should be last element"),
526 _ => {
527 let mut expr = expr.clone();
528 let mut replacer = OldPseudo::default();
529 replacer.visit_expr_mut(&mut expr);
530
531 if self.ty != Type::Ensures && !replacer.items.is_empty() {
532 panic!("Only ensures support 'old' pseudo-expressions")
533 }
534
535 replacer.items.into_iter().for_each(|(k, v)| {
536 self.decls.insert(k, v);
537 });
538
539 rule.add(RuleExpression::Expr(expr));
540 self.rules.push(rule);
541 rule = CaseRule::default();
542 }
543 }
544 }
545 }
546 }
547 }
548}
549
550pub fn trim_whitespace(s: &str) -> String {
552 let mut new_str = s.trim().to_owned();
553 let mut prev = ' '; new_str.retain(|ch| {
555 let result = ch != ' ' || prev != ' ';
556 prev = ch;
557 result
558 });
559 new_str
560}
561
562#[derive(Debug, Default)]
563struct OldPseudo {
564 items: HashMap<String, Expr>,
565}
566
567impl VisitMut for OldPseudo {
568 fn visit_expr_mut(&mut self, item: &mut Expr) {
569 if let Expr::Call(ref node) = item {
570 if let syn::Expr::Path(call) = node.func.as_ref() {
571 let segments = &call.path.segments;
572 if let Some(path) = segments.first() {
573 if path.ident.to_string() == "old" {
574 let name = format!("{}", quote!(#node));
576 let cleaned: String = name
577 .chars()
578 .filter_map(|x| match x {
579 'A'..='Z' | 'a'..='z' | '0'..='9' | ' ' => Some(x),
580 _ => None,
581 })
582 .collect();
583
584 let name = trim_whitespace(&cleaned)
585 .split(' ')
586 .filter(|s| !s.is_empty())
587 .collect::<Vec<_>>()
588 .join("_");
589
590 let var = format_ident!("{name}");
591
592 let old_arg = node
593 .args
594 .first()
595 .expect("The 'old' pseudo-function have exactly one parameter");
596
597 self.items.insert(name, old_arg.clone());
599
600 let expr: ExprPath = syn::parse_quote!(#var);
601 *item = Expr::Path(expr);
602 }
603 }
604 }
605 }
606 syn::visit_mut::visit_expr_mut(self, item);
608 }
609}
610
611#[derive(Debug, Clone)]
612pub struct Segment {
613 pub expressions: Vec<Expr>,
614}
615
616impl Parse for Segment {
617 fn parse(input: ParseStream) -> Result<Self> {
618 let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input).unwrap();
619 let expressions: Vec<Expr> = vars.into_iter().collect();
620
621 Ok(Segment { expressions })
623 }
624}
625
626#[derive(Default, Debug)]
627pub struct ContractAspectState {
628 pub docs: VecDeque<String>,
629 pub requires: Vec<Contract>,
630 pub invariants: Vec<Contract>,
631 pub ensures: Vec<Contract>,
632 pub aspects: Vec<Aspect>,
633}
634
635impl ContractAspectState {
636 pub fn process(&mut self, attrs: &Vec<Attribute>) -> Vec<Attribute> {
639 attrs
640 .iter()
641 .filter_map(|attr| {
642 if let Some(PathSegment { ident, .. }) = attr.path.segments.last() {
644 let pair = Type::type_and_mode(&ident.to_string());
645 if let Some((ty, mode)) = pair {
646 match ty {
648 Type::Requires | Type::Ensures | Type::Invariant => {
649 let mut case = Contract::new(ty, mode);
650 let tokens = attr.tokens.clone().into_iter().collect::<Vec<_>>();
651 assert!(
652 tokens.len() == 1,
653 "Wrong contract sintax: not parenthesized expression"
654 );
655
656 if let Some(TokenTree::Group(group)) = tokens.first() {
657 case.parse_attributes(group.stream());
658 match ty {
659 Type::Requires => self.requires.push(case),
660 Type::Ensures => self.ensures.push(case),
661 Type::Invariant => self.invariants.push(case),
662 _ => unreachable!(),
663 }
664 } else {
665 panic!("Wrong contract sintax: not parenthesized expression")
666 }
667 }
668 Type::Aspect => {
669 let tokens = attr.tokens.clone().into_iter().collect::<Vec<_>>();
670 assert!(
671 tokens.len() == 1,
672 "Wrong aspect sintax: not parenthesized expression"
673 );
674 if let Some(TokenTree::Group(group)) = tokens.first() {
675 let aspect = Aspect::new(group.stream());
676 self.aspects.push(aspect);
677 } else {
678 panic!("Wrong contract sintax: not parenthesized expression")
679 }
680 }
681 };
682
683 None
685 } else {
686 if attr.path.is_ident("doc") {
689 match attr.parse_meta().unwrap() {
690 Meta::NameValue(MetaNameValue {
691 lit: Lit::Str(lit_str),
692 ..
693 }) => {
694 self.docs.push_back(lit_str.value());
696 None
697 }
698 _ => Some(attr.clone()),
699 }
700 } else {
701 Some(attr.clone())
702 }
703 }
704 } else {
705 unreachable!()
707 }
708 })
709 .collect::<Vec<_>>()
710 }
711
712 pub fn variables(&self) -> Vec<TokenStream2> {
714 let mut decls: HashMap<String, Expr> = HashMap::new();
715 self.ensures.iter().for_each(|case| {
716 case.decls.iter().for_each(|(name, expr)| {
717 decls.insert(name.to_string(), expr.clone());
718 });
719 });
720
721 decls
722 .into_iter()
723 .map(|(name, expr)| {
724 let name = format_ident!("{name}");
725 quote! {
726 let #name = #expr;
727 }
728 })
729 .collect::<Vec<_>>()
730 }
731}
732
733pub trait SyntaxAndDocs {
734 fn generate(&self) -> (Vec<TokenStream2>, Vec<String>);
735}
736
737impl SyntaxAndDocs for Vec<Contract> {
738 fn generate(&self) -> (Vec<TokenStream2>, Vec<String>) {
739 let mut docs = vec![];
740 let stream = self
741 .iter()
742 .map(|case| {
743 if let Some(ref desc) = case.desc {
744 docs.push(format!(" - {}", desc))
745 }
746 case.syntax()
747 })
748 .flatten()
749 .collect::<Vec<_>>();
750 (stream, docs)
751 }
752}
753
754pub(crate) fn contracts_aspect(
756 ty: Type,
757 mode: Mode,
758 attrs: TokenStream2,
759 item: TokenStream2,
760) -> TokenStream2 {
761 let mut state: ContractAspectState = ContractAspectState::default();
762
763 match ty {
766 Type::Requires | Type::Ensures | Type::Invariant => {
767 let mut case = Contract::new(ty, mode);
768 case.parse_attributes(attrs);
769
770 match ty {
771 Type::Requires => state.requires.push(case),
772 Type::Ensures => state.ensures.push(case),
773 Type::Invariant => state.invariants.push(case),
774 _ => unreachable!(),
775 }
776 }
777 Type::Aspect => {
778 state.aspects.push(Aspect::new(attrs));
779 }
780 }
781
782 let input: syn::ItemFn = syn::parse2(item.clone().into()).unwrap();
784
785 let syn::ItemFn {
786 attrs,
787 vis,
788 sig,
789 block,
790 } = input;
791
792 let attrs = state.process(&attrs);
793
794 let variables = state.variables();
795
796 let mut contract_docs: VecDeque<String> = VecDeque::new();
798
799 let (requires, docs) = state.requires.generate();
800 contract_docs.extend(docs);
801
802 let (invariants, docs) = state.invariants.generate();
803 contract_docs.extend(docs);
804
805 let (ensures, docs) = state.ensures.generate();
806 contract_docs.extend(docs);
807
808 if !contract_docs.is_empty() {
809 contract_docs.push_front(String::from(" # Contract"));
810 }
811
812 let before = state
813 .aspects
814 .iter()
815 .filter_map(|aspect| {
816 if let Some(item) = &aspect.before {
817 item.default.as_ref().map(|block| {
818 let stmts = &block.stmts;
819 quote! {
820 #(#stmts)*
821 }
822 })
823 } else {
824 None
825 }
826 })
827 .collect::<Vec<_>>();
828
829 let after = state
830 .aspects
831 .iter()
832 .rev()
833 .filter_map(|aspect| {
834 if let Some(item) = &aspect.after {
835 item.default.as_ref().map(|block| {
836 let stmts = &block.stmts;
837 quote! {
838 #(#stmts)*
839 }
840 })
841 } else {
842 None
843 }
844 })
845 .collect::<Vec<_>>();
846
847 let mut around = quote! {inner()};
850 let mut has_around = false;
851
852 let mut it = state.aspects.iter().rev().peekable();
853 while let Some(aspect) = it.next() {
854 if let Some(item) = &aspect.around {
855 item.default.as_ref().map(|block| {
856 let mut replacer = AspectJointPoint { stream: &around };
857
858 let mut stmts = block.stmts.clone();
859
860 for stmt in stmts.iter_mut() {
861 replacer.visit_stmt_mut(stmt);
862 }
863
864 if it.peek().is_none() {
865 around = quote!(#(#stmts)*);
866 } else {
867 around = quote!({#(#stmts)*});
868 }
869 has_around = true;
870 });
871 }
872 }
873
874 let attrs = {
875 let mut new_attrs: Vec<Attribute> = Vec::new();
876
877 if !state.docs.is_empty() {
878 let mut it = state.docs.iter().peekable();
879 while let Some(comment) = it.next() {
880 if it.peek().is_some() {
881 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
882 } else if !comment.trim().is_empty() {
883 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
884 }
885 }
886 new_attrs.push(syn::parse_quote!(#[doc = ""]));
887 }
888
889 if !contract_docs.is_empty() {
890 let mut it = contract_docs.iter().peekable();
891 while let Some(comment) = it.next() {
892 if it.peek().is_some() {
893 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
894 } else if !comment.trim().is_empty() {
895 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
896 }
897 }
898 new_attrs.push(syn::parse_quote!(#[doc = ""]));
899 }
900
901 let mut aspect_docs: VecDeque<String> = VecDeque::new();
902 state.aspects.iter().for_each(|aspect| {
904 let mut docs = aspect.documentation();
906 if !docs.is_empty() {
907 docs.push_back(String::new());
908 aspect_docs.extend(docs);
909 }
910 });
911
912 if !aspect_docs.is_empty() {
913 aspect_docs.push_front(String::from(" # Aspects"));
914 let mut it = aspect_docs.iter().peekable();
915 while let Some(comment) = it.next() {
916 if it.peek().is_some() {
917 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
918 } else if !comment.trim().is_empty() {
919 new_attrs.push(syn::parse_quote!(#[doc = #comment]));
920 }
921 }
922 new_attrs.push(syn::parse_quote!(#[doc = ""]));
923 }
924 attrs.iter().cloned().for_each(|attr| {
925 new_attrs.push(attr);
926 });
927 new_attrs
928 };
929
930 let stmts = &block.stmts;
931 let result = if has_around {
932 quote! {
933 let result = {
934 #around
935 };
936 }
937 } else {
938 quote! {
939 let result = inner();
940 }
941 };
942
943 quote! {
944 #(#attrs)* #vis #sig {
945
946 #(#requires)*
947 #(#invariants)*
948
949 #(#variables)*
950
951 let inner = || {
952 #(#stmts)*
953 };
954
955 #(#before)*
956
957 #result
958
959 #(#after)*
960
961 #(#invariants)*
962 #(#ensures)*
963
964 result
965 }
966 }
967}