1#![warn(missing_docs)]
3
4extern crate proc_macro;
5
6mod quote_m2;
7mod roles;
8mod syntax;
9mod tokens;
10
11use std::collections::BTreeSet;
12
13use proc_macro2::{TokenStream, TokenTree};
14use quote::{ToTokens, format_ident, quote};
15use syn::{Data, DeriveInput, Error, Fields, Member, Result, Type, parse_macro_input, parse_quote};
16
17fn base_crate() -> TokenStream {
18 quote!(::m2_syn)
19}
20
21fn expand_syntax(
22 input: proc_macro::TokenStream,
23 features: syntax::SyntaxFeatures,
24) -> proc_macro::TokenStream {
25 let expansion = match syntax::generate(input.into(), features) {
26 Ok(expansion) => expansion,
27 Err(error) => return error.into_compile_error().into(),
28 };
29 expansion.into()
30}
31
32#[proc_macro]
34pub fn syntax(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
35 expand_syntax(input, syntax::SyntaxFeatures::CORE)
36}
37
38#[doc(hidden)]
40#[proc_macro]
41pub fn syntax_pretty(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
42 expand_syntax(input, syntax::SyntaxFeatures::PRETTY)
43}
44
45#[doc(hidden)]
47#[proc_macro]
48pub fn syntax_walk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
49 expand_syntax(input, syntax::SyntaxFeatures::WALK)
50}
51
52#[doc(hidden)]
54#[proc_macro]
55pub fn syntax_pretty_walk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
56 expand_syntax(input, syntax::SyntaxFeatures::PRETTY_WALK)
57}
58
59#[proc_macro]
65pub fn token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
66 let base = base_crate();
67 match tokens::token_type_name(&input.into()) {
68 Ok(name) => quote!(#base::nodes::#name).into(),
69 Err(error) => error.into_compile_error().into(),
70 }
71}
72#[proc_macro]
78pub fn quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
79 quote_m2::expand(input.into()).into()
80}
81
82#[proc_macro]
84pub fn parse_quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
85 let input = TokenStream::from(input);
86 let base = base_crate();
87 quote!({
88 let tokens = #base::quote_m2!(#input);
89 #base::parse2(tokens).expect("parse_quote_m2! input should parse")
90 })
91 .into()
92}
93
94#[proc_macro_derive(Spanned)]
96pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
97 let input = parse_macro_input!(input as DeriveInput);
98 match expand_spanned(&input) {
99 Ok(expansion) => expansion.into(),
100 Err(error) => error.to_compile_error().into(),
101 }
102}
103
104fn expand_spanned_members(members: impl IntoIterator<Item = (Member, TokenStream)>) -> TokenStream {
105 let members = members.into_iter().collect::<Vec<_>>();
106 let base = base_crate();
107 let span = quote! {#base::Spanned::try_span};
108 if members.is_empty() {
109 return quote!(None);
110 }
111 if let Some((_, value)) = members
112 .iter()
113 .find(|(member, _)| matches!(member, Member::Named(ident) if ident == "span"))
114 {
115 quote!(#span(#value))
116 } else {
117 let values = members.iter().map(|(_, value)| value);
118 quote! {
119 #base::Span::join_all(
120 [#(#base::Spanned::try_span(#values)),*]
121 )
122 }
123 }
124}
125
126fn expand_spanned(input: &DeriveInput) -> Result<TokenStream> {
127 let name = &input.ident;
128 let base = base_crate();
129 let span_ty = quote! {#base::Span};
130 let mut generics = input.generics.clone();
131 let type_parameters = input
132 .generics
133 .type_params()
134 .map(|parameter| parameter.ident.to_string())
135 .collect::<BTreeSet<_>>();
136 for ty in spanned_field_types(input)
137 .into_iter()
138 .filter(|ty| type_mentions_parameter(ty, &type_parameters))
139 {
140 generics
141 .make_where_clause()
142 .predicates
143 .push(parse_quote!(#ty: #base::Spanned));
144 }
145 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
146 let body = match &input.data {
147 Data::Struct(data) => {
148 let members = data.fields.members().map(|member| {
149 let value = quote!(&self.#member);
150 (member, value)
151 });
152 expand_spanned_members(members)
153 }
154 Data::Enum(data) => {
155 let arms = data
156 .variants
157 .iter()
158 .map(|variant| {
159 let variant_name = &variant.ident;
160 let members = variant.fields.members().collect::<Vec<_>>();
161 let bindings = members
162 .iter()
163 .enumerate()
164 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
165 .collect::<Vec<_>>();
166 let values = bindings.iter().map(|binding| quote!(#binding));
167 let span = expand_spanned_members(members.iter().cloned().zip(values));
168
169 match &variant.fields {
170 Fields::Named(_) => {
171 let fields = members
172 .iter()
173 .zip(&bindings)
174 .map(|(member, binding)| quote!(#member: #binding));
175 quote! {
176 Self::#variant_name { #(#fields),* } => #span
177 }
178 }
179 Fields::Unnamed(_) => quote! {
180 Self::#variant_name(#(#bindings),*) => #span
181 },
182 Fields::Unit => quote! {
183 Self::#variant_name => #span
184 },
185 }
186 })
187 .collect::<Vec<_>>();
188 let empty_fallback = data.variants.is_empty().then(|| quote!(_ => None));
189 quote! {
190 match self {
191 #(#arms,)*
192 #empty_fallback
193 }
194 }
195 }
196 Data::Union(_) => {
197 return Err(Error::new_spanned(
198 name,
199 "Spanned cannot be derived for unions",
200 ));
201 }
202 };
203
204 Ok(quote! {
205 impl #impl_generics
206 #base::Spanned for #name #type_generics #where_clause
207 {
208 fn try_span(&self) -> Option<#span_ty> {
209 #body
210 }
211 }
212 })
213}
214
215fn type_mentions_parameter(ty: &impl ToTokens, parameters: &BTreeSet<String>) -> bool {
216 fn stream_mentions_parameter(stream: TokenStream, parameters: &BTreeSet<String>) -> bool {
217 stream.into_iter().any(|token| match token {
218 TokenTree::Ident(ident) => parameters.contains(&ident.to_string()),
219 TokenTree::Group(group) => stream_mentions_parameter(group.stream(), parameters),
220 _ => false,
221 })
222 }
223 stream_mentions_parameter(ty.to_token_stream(), parameters)
224}
225
226fn spanned_field_types(input: &DeriveInput) -> Vec<&Type> {
227 fn selected(fields: &Fields) -> Vec<&Type> {
228 fields
229 .iter()
230 .find(|field| field.ident.as_ref().is_some_and(|ident| ident == "span"))
231 .map_or_else(
232 || fields.iter().map(|field| &field.ty).collect(),
233 |field| vec![&field.ty],
234 )
235 }
236
237 match &input.data {
238 Data::Struct(data) => selected(&data.fields),
239 Data::Enum(data) => data
240 .variants
241 .iter()
242 .flat_map(|variant| selected(&variant.fields))
243 .collect(),
244 Data::Union(_) => Vec::new(),
245 }
246}