Skip to main content

anodized_core/instrument/
impls.rs

1#[cfg(test)]
2#[path = "impls_tests.rs"]
3mod impls_tests;
4
5use syn::{
6    Attribute, Error, ImplItem, ImplItemFn, ItemImpl, Result, ReturnType, Visibility, parse_quote,
7};
8
9use crate::{
10    DataSpec, Spec,
11    instrument::{Mode, find_spec_attr, make_item_error},
12};
13
14impl Mode {
15    /// Expand items inside an inherent impl.
16    ///
17    /// Reasons why impl functions must be treated differently from free-standing functions:
18    /// - The `__anodized_fn_try_*` function must be qualified as `Self::` inside an impl.
19    pub fn instrument_impl(&self, spec: DataSpec, mut the_impl: ItemImpl) -> Result<ItemImpl> {
20        if the_impl.trait_.is_some() {
21            return Err(make_item_error(&the_impl, "trait impl"));
22        };
23
24        if !spec.is_empty() {
25            return Err(spec.spec_err("Unsupported spec element on inherent impl."));
26        }
27
28        let mut new_items = Vec::with_capacity(the_impl.items.len() * 4);
29
30        for item in the_impl.items.into_iter() {
31            match item {
32                ImplItem::Fn(mut item_fn) => {
33                    let (spec_attr, func_attrs) = find_spec_attr(item_fn.attrs)?;
34                    item_fn.attrs = func_attrs;
35
36                    if item_fn.sig.ident.to_string().starts_with("__anodized_") {
37                        return Err(Error::new_spanned(
38                            item_fn.sig.ident,
39                            r#"An item with the `__anodized_` prefix is internal. Do not implement it directly.
40Instead, ensure that both the impl block and the fn have a `#[spec]` annotation."#,
41                        ));
42                    }
43
44                    let fn_spec: Spec = match spec_attr {
45                        Some(spec_attr) => spec_attr.parse_args()?,
46                        None => Spec::empty(),
47                    };
48
49                    if let Self::EmbedSpecs = self {
50                        // Embed `spec` elements as `__anodized_fn_*` items.
51                        let attrs: [Attribute; 2] = [
52                            parse_quote!(#[doc(hidden)]),
53                            parse_quote!(#[allow(warnings)]),
54                        ];
55
56                        let spec_qualifiers_const = Self::build_qualifier_const_item(
57                            &attrs,
58                            "__anodized_fn_qualifiers",
59                            fn_spec.qualifiers,
60                            &item_fn.sig.ident,
61                        );
62                        let spec_requires_fn = ImplItemFn {
63                            attrs: attrs.to_vec(),
64                            sig: Self::build_precondition_fn_sig(
65                                "__anodized_fn_requires",
66                                &item_fn.sig,
67                            ),
68                            block: Self::build_precondition_fn_body(
69                                &fn_spec.requires,
70                                &fn_spec.maintains,
71                            ),
72                            vis: Visibility::Inherited,
73                            defaultness: None,
74                        };
75                        let spec_ensures_fn = ImplItemFn {
76                            attrs: attrs.to_vec(),
77                            sig: Self::build_postcondition_fn_sig(
78                                "__anodized_fn_ensures",
79                                &item_fn.sig,
80                            ),
81                            block: Self::build_postcondition_fn_body(
82                                &fn_spec.maintains,
83                                &fn_spec.captures,
84                                &fn_spec.ensures,
85                            )?,
86                            vis: Visibility::Inherited,
87                            defaultness: None,
88                        };
89
90                        new_items.push(ImplItem::Const(spec_qualifiers_const));
91                        new_items.push(ImplItem::Fn(spec_requires_fn));
92                        new_items.push(ImplItem::Fn(spec_ensures_fn));
93                    }
94
95                    // Instrument function body.
96                    self.instrument_fn(&fn_spec, &item_fn.sig, &mut item_fn.block)?;
97
98                    if let Self::InjectChecks(check_settings) = self
99                        && let Some(ref panic_settings) = check_settings.does_panic
100                        && panic_settings.has_try_fn
101                    {
102                        // Build a wrapper that forwards to the "try_fn" entry point.
103                        let mut wrapper_fn = item_fn.clone();
104                        let mangled_ident = Self::build_try_fn_wrapper(
105                            true,
106                            &mut wrapper_fn.sig,
107                            &mut wrapper_fn.block,
108                        );
109                        new_items.push(ImplItem::Fn(wrapper_fn));
110
111                        // Create the "try_fn" entry point for e.g. fuzzing and PBT.
112                        item_fn.sig.ident = mangled_ident;
113                        item_fn.sig.output = match item_fn.sig.output {
114                            ReturnType::Default => {
115                                parse_quote!(-> ::anodized::result::Result<()>)
116                            }
117                            ReturnType::Type(ra, ty) => {
118                                parse_quote!(#ra ::anodized::result::Result<#ty>)
119                            }
120                        };
121                        item_fn.attrs = vec![parse_quote!(#[doc(hidden)]), parse_quote!(#[inline])];
122                    }
123
124                    new_items.push(ImplItem::Fn(item_fn));
125                }
126                ImplItem::Const(mut const_item) => {
127                    let (spec, attrs) = find_spec_attr(const_item.attrs)?;
128                    if let Some(ref spec_attr) = spec {
129                        return Err(make_item_error(&spec_attr, "impl const"));
130                    }
131                    const_item.attrs = attrs;
132                    new_items.push(ImplItem::Const(const_item));
133                }
134                ImplItem::Type(mut type_item) => {
135                    let (spec, attrs) = find_spec_attr(type_item.attrs)?;
136                    if let Some(ref spec_attr) = spec {
137                        return Err(make_item_error(&spec_attr, "impl type"));
138                    }
139                    type_item.attrs = attrs;
140                    new_items.push(ImplItem::Type(type_item));
141                }
142                ImplItem::Macro(mut macro_item) => {
143                    let (spec, attrs) = find_spec_attr(macro_item.attrs)?;
144                    if let Some(ref spec_attr) = spec {
145                        return Err(make_item_error(&spec_attr, "impl macro"));
146                    }
147                    macro_item.attrs = attrs;
148                    new_items.push(ImplItem::Macro(macro_item));
149                }
150                ImplItem::Verbatim(token_stream) => {
151                    new_items.push(ImplItem::Verbatim(token_stream))
152                }
153                _ => unimplemented!(),
154            };
155        }
156
157        the_impl.items = new_items;
158        Ok(the_impl)
159    }
160}