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]
66pub fn token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
67 let base = base_crate();
68 match tokens::token_type_name(&input.into()) {
69 Ok(name) => quote!(#base::nodes::#name).into(),
70 Err(error) => error.into_compile_error().into(),
71 }
72}
73#[proc_macro]
79pub fn quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
80 quote_m2::expand(input.into()).into()
81}
82
83#[proc_macro]
85pub fn parse_quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
86 let input = TokenStream::from(input);
87 let base = base_crate();
88 quote!({
89 let tokens = #base::quote_m2!(#input);
90 #base::parse2(tokens).expect("parse_quote_m2! input should parse")
91 })
92 .into()
93}
94
95#[proc_macro_derive(Spanned)]
97pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
98 let input = parse_macro_input!(input as DeriveInput);
99 match expand_spanned(&input) {
100 Ok(expansion) => expansion.into(),
101 Err(error) => error.to_compile_error().into(),
102 }
103}
104
105fn expand_spanned_members(members: impl IntoIterator<Item = (Member, TokenStream)>) -> TokenStream {
106 let members = members.into_iter().collect::<Vec<_>>();
107 let base = base_crate();
108 let span = quote! {#base::Spanned::try_span};
109 if members.is_empty() {
110 return quote!(None);
111 }
112 if let Some((_, value)) = members
113 .iter()
114 .find(|(member, _)| matches!(member, Member::Named(ident) if ident == "span"))
115 {
116 quote!(#span(#value))
117 } else {
118 let values = members.iter().map(|(_, value)| value);
119 quote! {
120 #base::Span::join_all(
121 [#(#base::Spanned::try_span(#values)),*]
122 )
123 }
124 }
125}
126
127fn expand_spanned(input: &DeriveInput) -> Result<TokenStream> {
128 let name = &input.ident;
129 let base = base_crate();
130 let span_ty = quote! {#base::Span};
131 let mut generics = input.generics.clone();
132 let type_parameters = input
133 .generics
134 .type_params()
135 .map(|parameter| parameter.ident.to_string())
136 .collect::<BTreeSet<_>>();
137 for ty in spanned_field_types(input)
138 .into_iter()
139 .filter(|ty| type_mentions_parameter(ty, &type_parameters))
140 {
141 generics
142 .make_where_clause()
143 .predicates
144 .push(parse_quote!(#ty: #base::Spanned));
145 }
146 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
147 let body = match &input.data {
148 Data::Struct(data) => {
149 let members = data.fields.members().map(|member| {
150 let value = quote!(&self.#member);
151 (member, value)
152 });
153 expand_spanned_members(members)
154 }
155 Data::Enum(data) => {
156 let arms = data
157 .variants
158 .iter()
159 .map(|variant| {
160 let variant_name = &variant.ident;
161 let members = variant.fields.members().collect::<Vec<_>>();
162 let bindings = members
163 .iter()
164 .enumerate()
165 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
166 .collect::<Vec<_>>();
167 let values = bindings.iter().map(|binding| quote!(#binding));
168 let span = expand_spanned_members(members.iter().cloned().zip(values));
169
170 match &variant.fields {
171 Fields::Named(_) => {
172 let fields = members
173 .iter()
174 .zip(&bindings)
175 .map(|(member, binding)| quote!(#member: #binding));
176 quote! {
177 Self::#variant_name { #(#fields),* } => #span
178 }
179 }
180 Fields::Unnamed(_) => quote! {
181 Self::#variant_name(#(#bindings),*) => #span
182 },
183 Fields::Unit => quote! {
184 Self::#variant_name => #span
185 },
186 }
187 })
188 .collect::<Vec<_>>();
189 let empty_fallback = data.variants.is_empty().then(|| quote!(_ => None));
190 quote! {
191 match self {
192 #(#arms,)*
193 #empty_fallback
194 }
195 }
196 }
197 Data::Union(_) => {
198 return Err(Error::new_spanned(
199 name,
200 "Spanned cannot be derived for unions",
201 ));
202 }
203 };
204
205 Ok(quote! {
206 impl #impl_generics
207 #base::Spanned for #name #type_generics #where_clause
208 {
209 fn try_span(&self) -> Option<#span_ty> {
210 #body
211 }
212 }
213 })
214}
215
216fn type_mentions_parameter(ty: &impl ToTokens, parameters: &BTreeSet<String>) -> bool {
217 fn stream_mentions_parameter(stream: TokenStream, parameters: &BTreeSet<String>) -> bool {
218 stream.into_iter().any(|token| match token {
219 TokenTree::Ident(ident) => parameters.contains(&ident.to_string()),
220 TokenTree::Group(group) => stream_mentions_parameter(group.stream(), parameters),
221 _ => false,
222 })
223 }
224 stream_mentions_parameter(ty.to_token_stream(), parameters)
225}
226
227fn spanned_field_types(input: &DeriveInput) -> Vec<&Type> {
228 fn selected(fields: &Fields) -> Vec<&Type> {
229 fields
230 .iter()
231 .find(|field| field.ident.as_ref().is_some_and(|ident| ident == "span"))
232 .map_or_else(
233 || fields.iter().map(|field| &field.ty).collect(),
234 |field| vec![&field.ty],
235 )
236 }
237
238 match &input.data {
239 Data::Struct(data) => selected(&data.fields),
240 Data::Enum(data) => data
241 .variants
242 .iter()
243 .flat_map(|variant| selected(&variant.fields))
244 .collect(),
245 Data::Union(_) => Vec::new(),
246 }
247}