1extern crate proc_macro;
2
3mod quote_m2;
4mod syntax;
5mod tokens;
6
7use proc_macro::TokenStream;
8use std::collections::BTreeSet;
9
10use proc_macro2::{TokenStream as TokenStream2, TokenTree};
11use quote::{ToTokens, format_ident, quote};
12use syn::{Data, DeriveInput, Error, Fields, Member, Result, Type, parse_macro_input, parse_quote};
13
14#[proc_macro]
15pub fn syntax(input: TokenStream) -> TokenStream {
16 match syntax::generate(input.into()) {
17 Ok(expansion) => expansion.combined().into(),
18 Err(error) => error.into_compile_error().into(),
19 }
20}
21
22#[proc_macro]
23pub fn quote_m2(input: TokenStream) -> TokenStream {
29 quote_m2::expand(input)
30}
31
32#[proc_macro]
33pub fn parse_quote_m2(input: TokenStream) -> TokenStream {
35 let input = TokenStream2::from(input);
36 quote!({
37 let tokens = ::m2_syn::quote_m2!(#input);
38 ::m2_syn::parse1(tokens).expect("parse_quote_m2! input should parse")
39 })
40 .into()
41}
42
43#[proc_macro_derive(Spanned)]
44pub fn derive_spanned(input: TokenStream) -> TokenStream {
45 let input = parse_macro_input!(input as DeriveInput);
46 match expand_spanned(&input) {
47 Ok(expansion) => expansion.into(),
48 Err(error) => error.to_compile_error().into(),
49 }
50}
51
52fn expand_spanned_members(
53 members: impl IntoIterator<Item = (Member, TokenStream2)>,
54) -> TokenStream2 {
55 let members = members.into_iter().collect::<Vec<_>>();
56 let span = quote! {::m2_syn::Spanned::span};
57 if let Some((_, value)) = members
58 .iter()
59 .find(|(member, _)| matches!(member, Member::Named(ident) if ident == "span"))
60 {
61 quote!(#span(#value))
62 } else {
63 let values = members.iter().map(|(_, value)| value);
64 quote! {
65 ::m2_syn::Span::join_all(
66 [#(::m2_syn::Spanned::span(#values)),*]
67 )
68 }
69 }
70}
71
72fn expand_spanned(input: &DeriveInput) -> Result<TokenStream2> {
73 let name = &input.ident;
74 let span_ty = quote! {::m2_syn::Span};
75 let mut generics = input.generics.clone();
76 let type_parameters = input
77 .generics
78 .type_params()
79 .map(|parameter| parameter.ident.to_string())
80 .collect::<BTreeSet<_>>();
81 for ty in spanned_field_types(input)
82 .into_iter()
83 .filter(|ty| type_mentions_parameter(ty, &type_parameters))
84 {
85 generics
86 .make_where_clause()
87 .predicates
88 .push(parse_quote!(#ty: ::m2_syn::Spanned));
89 }
90 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
91 let body = match &input.data {
92 Data::Struct(data) => {
93 let members = data.fields.members().map(|member| {
94 let value = quote!(&self.#member);
95 (member, value)
96 });
97 expand_spanned_members(members)
98 }
99 Data::Enum(data) => {
100 let arms = data
101 .variants
102 .iter()
103 .map(|variant| {
104 let variant_name = &variant.ident;
105 let members = variant.fields.members().collect::<Vec<_>>();
106 let bindings = members
107 .iter()
108 .enumerate()
109 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
110 .collect::<Vec<_>>();
111 let values = bindings.iter().map(|binding| quote!(#binding));
112 let span = expand_spanned_members(members.iter().cloned().zip(values));
113
114 match &variant.fields {
115 Fields::Named(_) => {
116 let fields = members
117 .iter()
118 .zip(&bindings)
119 .map(|(member, binding)| quote!(#member: #binding));
120 quote! {
121 Self::#variant_name { #(#fields),* } => #span
122 }
123 }
124 Fields::Unnamed(_) => quote! {
125 Self::#variant_name(#(#bindings),*) => #span
126 },
127 Fields::Unit => quote! {
128 Self::#variant_name => #span
129 },
130 }
131 })
132 .collect::<Vec<_>>();
133 let empty_fallback = data
134 .variants
135 .is_empty()
136 .then(|| quote!(_ => #span_ty::detached()));
137 quote! {
138 match self {
139 #(#arms,)*
140 #empty_fallback
141 }
142 }
143 }
144 Data::Union(_) => {
145 return Err(Error::new_spanned(
146 name,
147 "Spanned cannot be derived for unions",
148 ));
149 }
150 };
151
152 Ok(quote! {
153 impl #impl_generics
154 ::m2_syn::Spanned for #name #type_generics #where_clause
155 {
156 fn span(&self) -> #span_ty {
157 #body
158 }
159 }
160 })
161}
162
163fn type_mentions_parameter(ty: &Type, parameters: &BTreeSet<String>) -> bool {
164 fn stream_mentions_parameter(stream: TokenStream2, parameters: &BTreeSet<String>) -> bool {
165 stream.into_iter().any(|token| match token {
166 TokenTree::Ident(ident) => parameters.contains(&ident.to_string()),
167 TokenTree::Group(group) => stream_mentions_parameter(group.stream(), parameters),
168 TokenTree::Punct(_) | TokenTree::Literal(_) => false,
169 })
170 }
171
172 stream_mentions_parameter(ty.to_token_stream(), parameters)
173}
174
175fn spanned_field_types(input: &DeriveInput) -> Vec<&Type> {
176 fn selected(fields: &Fields) -> Vec<&Type> {
177 fields
178 .iter()
179 .find(|field| field.ident.as_ref().is_some_and(|ident| ident == "span"))
180 .map_or_else(
181 || fields.iter().map(|field| &field.ty).collect(),
182 |field| vec![&field.ty],
183 )
184 }
185
186 match &input.data {
187 Data::Struct(data) => selected(&data.fields),
188 Data::Enum(data) => data
189 .variants
190 .iter()
191 .flat_map(|variant| selected(&variant.fields))
192 .collect(),
193 Data::Union(_) => Vec::new(),
194 }
195}