tracked_impl/
lib.rs

1//! This crate provides `tracked`'s procedural macro.
2//!
3//! Please refer to the `tracked` crate for details.
4
5#![forbid(unsafe_code)]
6
7use quote::quote;
8use syn::parse_quote_spanned;
9use syn::spanned::Spanned;
10use syn::visit_mut::{self, VisitMut};
11
12struct TrackReplace(String);
13
14impl VisitMut for TrackReplace {
15	fn visit_expr_try_mut(&mut self, node: &mut syn::ExprTry) {
16		let fn_name = &self.0;
17		let expr = &node.expr;
18		let span = node.question_token.span();
19		*node.expr = parse_quote_spanned!(span => tracked::Track::t_named( #expr, #fn_name ));
20		visit_mut::visit_expr_try_mut(self, node);
21	}
22	fn visit_macro_mut(&mut self, node: &mut syn::Macro) {
23		// Macros that successfully parse as a block of valid Rust code or a
24		// comma-separated list of expressions are recursed into.
25		if let Ok(mut body) = node.parse_body_with(syn::Block::parse_within) {
26			for stmt in &mut body {
27				TrackReplace(self.0.clone()).visit_stmt_mut(stmt);
28			}
29			node.tokens = quote!( #(#body)* );
30		} else if let Ok(mut punctuated) =
31			node.parse_body_with(<syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>>::parse_terminated)
32		{
33			for expr in punctuated.iter_mut() {
34				TrackReplace(self.0.clone()).visit_expr_mut(expr);
35			}
36			node.tokens = quote!( #punctuated );
37		}
38		visit_mut::visit_macro_mut(self, node);
39	}
40}
41
42/// Apply this to a `fn` to track line numbers for `?` errors.
43#[proc_macro_attribute]
44// #[proc_macro_error]
45pub fn tracked(
46	_args: proc_macro::TokenStream,
47	input: proc_macro::TokenStream,
48) -> proc_macro::TokenStream {
49	let mut new_fn = syn::parse_macro_input!(input as syn::ItemFn);
50	TrackReplace(new_fn.sig.ident.to_string()).visit_block_mut(&mut new_fn.block);
51	#[cfg(test)]
52	// #[allow(clippy::needless_question_mark)] should be on expression instead of whole fn
53	todo_or_die::issue_closed!("rust-lang", "rust", 15701);
54	let output = quote! {
55		#[allow(clippy::needless_question_mark)]
56		#new_fn
57	};
58	output.into()
59}
60
61// #[proc_macro]
62// pub fn track(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
63//  let mut block = syn::Block {
64//   brace_token: syn::token::Brace::default(),
65//   stmts: syn::parse_macro_input!(input with syn::Block::parse_within),
66//  };
67//  // let mut block = syn::parse_macro_input!(input as syn::Block);
68//  TrackReplace.visit_block_mut(&mut block);
69//  let stmts = block.stmts;
70//  let output = quote! {
71//   #(#stmts)*
72//  };
73//  output.into()
74// }