anodized_core/instrument/traits/
mod.rs1#[cfg(test)]
2mod tests;
3
4use quote::quote;
5use syn::{
6 Attribute, Block, FnArg, ImplItem, ImplItemFn, Pat, TraitItem, TraitItemFn, Visibility,
7 parse_quote,
8};
9
10use crate::{
11 DataSpec, Spec,
12 instrument::{Config, find_spec_attr, make_item_error},
13};
14
15impl Config {
16 pub fn instrument_trait(
23 &self,
24 spec: DataSpec,
25 mut the_trait: syn::ItemTrait,
26 ) -> syn::Result<syn::ItemTrait> {
27 if !spec.is_empty() {
29 return Err(spec.spec_err(
30 "Unsupported spec element on trait. Try placing it on an item inside the trait",
31 ));
32 }
33 let _ = move || spec;
34
35 let mut new_trait_items = Vec::with_capacity(the_trait.items.len() * 5);
36
37 for item in the_trait.items.into_iter() {
38 match item {
39 TraitItem::Fn(mut func) => {
40 let (spec_attr, other_attrs) = find_spec_attr(func.attrs)?;
41 func.attrs = other_attrs.clone();
47
48 let fn_spec: Spec = match spec_attr {
49 Some(spec_attr) => spec_attr.parse_args()?,
50 None => Spec::empty(),
51 };
52
53 if self.embed_spec {
54 let attrs: [Attribute; 2] = [
55 parse_quote!(#[doc(hidden)]),
56 parse_quote!(#[allow(warnings)]),
57 ];
58
59 let spec_trait_qualifiers_const = Self::build_qualifier_const_item(
61 &attrs,
62 "__anodized_fn_qualifiers_trait",
63 fn_spec.qualifiers,
64 &func.sig.ident,
65 );
66 let spec_qualifiers_const = Self::build_qualifier_const_item(
67 &attrs,
68 "__anodized_fn_qualifiers",
69 fn_spec.qualifiers,
70 &func.sig.ident,
71 );
72 let spec_requires_fn = TraitItemFn {
73 attrs: attrs.to_vec(),
74 sig: Self::build_spec_fn_sig("__anodized_fn_requires", &func.sig),
75 default: Some(Self::build_precondition_fn_body(&fn_spec.requires)),
76 semi_token: None,
77 };
78 let spec_maintains_fn = TraitItemFn {
79 attrs: attrs.to_vec(),
80 sig: Self::build_spec_fn_sig("__anodized_fn_maintains", &func.sig),
81 default: Some(Self::build_precondition_fn_body(&fn_spec.maintains)),
82 semi_token: None,
83 };
84 let spec_ensures_fn = TraitItemFn {
85 attrs: attrs.to_vec(),
86 sig: Self::build_spec_fn_sig("__anodized_fn_ensures", &func.sig),
87 default: Some(Self::build_poscondition_fn_body(
88 &fn_spec.captures,
89 &fn_spec.ensures,
90 &func.sig.output,
91 )?),
92 semi_token: None,
93 };
94
95 new_trait_items.push(TraitItem::Const(spec_trait_qualifiers_const));
96 new_trait_items.push(TraitItem::Const(spec_qualifiers_const));
97 new_trait_items.push(TraitItem::Fn(spec_requires_fn));
98 new_trait_items.push(TraitItem::Fn(spec_maintains_fn));
99 new_trait_items.push(TraitItem::Fn(spec_ensures_fn));
100 }
101
102 if self.emit_anything() {
103 let mangled_ident = mangle_ident(&func.sig.ident);
104
105 let mut mangled_fn = func.clone();
106 mangled_fn.sig.ident = mangled_ident.clone();
107 mangled_fn.attrs.retain(|attr| !attr.path().is_ident("doc"));
108 mangled_fn.attrs.push(parse_quote!(#[doc(hidden)]));
109 new_trait_items.push(TraitItem::Fn(mangled_fn));
110
111 let call_args = build_call_args(&func.sig.inputs)?;
112 let forwarding_body: Block = parse_quote!({
113 Self::#mangled_ident(#(#call_args),*)
114 });
115 func.default = Some(forwarding_body);
116 func.semi_token = None;
117 }
118
119 func.attrs = other_attrs;
120
121 if let Some(default_body) = &mut func.default {
122 self.instrument_fn(&fn_spec, &func.sig, default_body)?;
124 }
125 new_trait_items.push(TraitItem::Fn(func));
126 }
127 TraitItem::Const(mut const_item) => {
128 let (spec, attrs) = find_spec_attr(const_item.attrs)?;
129 if let Some(ref spec_attr) = spec {
130 return Err(make_item_error(&spec_attr, "trait const"));
131 }
132 const_item.attrs = attrs;
133 new_trait_items.push(TraitItem::Const(const_item));
134 }
135 TraitItem::Type(mut type_item) => {
136 let (spec, attrs) = find_spec_attr(type_item.attrs)?;
137 if let Some(ref spec_attr) = spec {
138 return Err(make_item_error(&spec_attr, "trait type"));
139 }
140 type_item.attrs = attrs;
141 new_trait_items.push(TraitItem::Type(type_item));
142 }
143 TraitItem::Macro(mut macro_item) => {
144 let (spec, attrs) = find_spec_attr(macro_item.attrs)?;
145 if let Some(ref spec_attr) = spec {
146 return Err(make_item_error(&spec_attr, "trait macro"));
147 }
148 macro_item.attrs = attrs;
149 new_trait_items.push(TraitItem::Macro(macro_item));
150 }
151 TraitItem::Verbatim(token_stream) => {
152 new_trait_items.push(TraitItem::Verbatim(token_stream));
153 }
154 _ => unimplemented!(),
155 }
156 }
157 the_trait.items = new_trait_items;
158 Ok(the_trait)
159 }
160
161 pub fn instrument_trait_impl(
167 &self,
168 spec: DataSpec,
169 mut the_impl: syn::ItemImpl,
170 ) -> syn::Result<syn::ItemImpl> {
171 let Some((trait_bang, ref trait_path, _trait_for)) = the_impl.trait_ else {
172 return Err(make_item_error(&the_impl, "inherent impl"));
173 };
174
175 if trait_bang.is_some() {
176 return Err(make_item_error(&the_impl, "negative trait impl"));
177 }
178
179 if !spec.is_empty() {
180 return Err(spec.spec_err("Unsupported spec element on trait impl."));
181 }
182
183 let mut new_items = Vec::with_capacity(the_impl.items.len() * 4);
184
185 for item in the_impl.items.into_iter() {
186 match item {
187 ImplItem::Fn(mut func) => {
188 let (spec_attr, func_attrs) = find_spec_attr(func.attrs)?;
189 func.attrs = func_attrs;
190
191 if func.sig.ident.to_string().starts_with("__anodized_") {
192 return Err(syn::Error::new_spanned(
193 func.sig.ident,
194 r#"An item with the `__anodized_` prefix is internal. Do not implement it directly.
195Instead, ensure that both the trait and the impl fn have a `#[spec]` annotation."#,
196 ));
197 }
198
199 let fn_spec: Spec = match spec_attr {
200 Some(spec_attr) => spec_attr.parse_args()?,
201 None => Spec::empty(),
202 };
203
204 if self.embed_spec {
205 let attrs: [Attribute; 2] = [
206 parse_quote!(#[doc(hidden)]),
207 parse_quote!(#[allow(warnings)]),
208 ];
209
210 let spec_qualifiers_const = Self::build_qualifier_const_item(
212 &attrs,
213 "__anodized_fn_qualifiers",
214 fn_spec.qualifiers,
215 &func.sig.ident,
216 );
217 let spec_requires_fn = ImplItemFn {
218 attrs: attrs.to_vec(),
219 sig: Self::build_spec_fn_sig("__anodized_fn_requires", &func.sig),
220 block: Self::build_precondition_fn_body(&fn_spec.requires),
221 vis: Visibility::Inherited,
222 defaultness: None,
223 };
224 let spec_maintains_fn = ImplItemFn {
225 attrs: attrs.to_vec(),
226 sig: Self::build_spec_fn_sig("__anodized_fn_maintains", &func.sig),
227 block: Self::build_precondition_fn_body(&fn_spec.maintains),
228 vis: Visibility::Inherited,
229 defaultness: None,
230 };
231 let spec_ensures_fn = ImplItemFn {
232 attrs: attrs.to_vec(),
233 sig: Self::build_spec_fn_sig("__anodized_fn_ensures", &func.sig),
234 block: Self::build_poscondition_fn_body(
235 &fn_spec.captures,
236 &fn_spec.ensures,
237 &func.sig.output,
238 )?,
239 vis: Visibility::Inherited,
240 defaultness: None,
241 };
242
243 new_items.push(ImplItem::Const(spec_qualifiers_const));
244 new_items.push(ImplItem::Fn(spec_requires_fn));
245 new_items.push(ImplItem::Fn(spec_maintains_fn));
246 new_items.push(ImplItem::Fn(spec_ensures_fn));
247 }
248
249 self.instrument_fn(&fn_spec, &func.sig, &mut func.block)?;
250
251 if self.embed_spec {
252 func.block.stmts.insert(
254 0,
255 Self::build_qualifier_check_stmt(
256 &func.sig.ident,
257 &the_impl.self_ty,
258 trait_path,
259 ),
260 );
261 }
262
263 if self.emit_anything() {
264 func.sig.ident = mangle_ident(&func.sig.ident);
265
266 if !has_inline_attr(&func.attrs) {
269 func.attrs.push(parse_quote!(#[inline]));
270 }
271 }
272 new_items.push(ImplItem::Fn(func));
273 }
274 ImplItem::Const(mut const_item) => {
275 let (spec, attrs) = find_spec_attr(const_item.attrs)?;
276 if let Some(ref spec_attr) = spec {
277 return Err(make_item_error(&spec_attr, "trait impl const"));
278 }
279 const_item.attrs = attrs;
280 new_items.push(ImplItem::Const(const_item));
281 }
282 ImplItem::Type(mut type_item) => {
283 let (spec, attrs) = find_spec_attr(type_item.attrs)?;
284 if let Some(ref spec_attr) = spec {
285 return Err(make_item_error(&spec_attr, "trait impl type"));
286 }
287 type_item.attrs = attrs;
288 new_items.push(ImplItem::Type(type_item));
289 }
290 ImplItem::Macro(mut macro_item) => {
291 let (spec, attrs) = find_spec_attr(macro_item.attrs)?;
292 if let Some(ref spec_attr) = spec {
293 return Err(make_item_error(&spec_attr, "trait impl macro"));
294 }
295 macro_item.attrs = attrs;
296 new_items.push(ImplItem::Macro(macro_item));
297 }
298 ImplItem::Verbatim(token_stream) => {
299 new_items.push(ImplItem::Verbatim(token_stream))
300 }
301 _ => unimplemented!(),
302 };
303 }
304
305 the_impl.items = new_items;
306 Ok(the_impl)
307 }
308}
309
310fn build_call_args(
325 inputs: &syn::punctuated::Punctuated<FnArg, syn::Token![,]>,
326) -> syn::Result<Vec<proc_macro2::TokenStream>> {
327 let mut args = Vec::new();
328 for input in inputs.iter() {
329 match input {
330 FnArg::Receiver(_) => {
331 args.push(quote! { self });
332 }
333 FnArg::Typed(pat) => match pat.pat.as_ref() {
334 Pat::Ident(pat_ident) => {
335 let ident = &pat_ident.ident;
336 args.push(quote! { #ident });
337 }
338 _ => {
339 return Err(syn::Error::new_spanned(
340 &pat.pat,
341 "unsupported pattern in trait method arguments",
342 ));
343 }
344 },
345 }
346 }
347 Ok(args)
348}
349
350fn mangle_ident(original_ident: &syn::Ident) -> syn::Ident {
353 syn::Ident::new(
354 &format!("__anodized_{original_ident}"),
355 original_ident.span(),
356 )
357}
358
359fn has_inline_attr(attrs: &[syn::Attribute]) -> bool {
361 attrs.iter().any(|attr| attr.path().is_ident("inline"))
362}