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