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::parse::{Parse, ParseStream};
16use syn::{
17 Data, DeriveInput, Error, Fields, Member, Result, Token, Type, parse_macro_input, parse_quote,
18};
19
20fn base_crate() -> TokenStream {
21 quote!(::m2_syn)
22}
23
24fn expand_syntax(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
25 let expansion = match syntax::generate(input.into()) {
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)
36}
37
38#[proc_macro]
45pub fn token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
46 let base = base_crate();
47 match tokens::token_type_name(&input.into()) {
48 Ok(name) => quote!(#base::nodes::#name).into(),
49 Err(error) => error.into_compile_error().into(),
50 }
51}
52#[proc_macro]
58pub fn quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
59 quote_m2::expand(input.into()).into()
60}
61
62#[proc_macro]
64pub fn parse_quote_m2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
65 let input = TokenStream::from(input);
66 let base = base_crate();
67 quote!({
68 let tokens = #base::quote_m2!(#input);
69 #base::parse2(tokens).expect("parse_quote_m2! input should parse")
70 })
71 .into()
72}
73
74#[proc_macro_derive(Spanned)]
76pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
77 let input = parse_macro_input!(input as DeriveInput);
78 match expand_spanned(&input) {
79 Ok(expansion) => expansion.into(),
80 Err(error) => error.to_compile_error().into(),
81 }
82}
83
84#[doc(hidden)]
86#[proc_macro_derive(__SyntaxWalk)]
87pub fn derive_syntax_walk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
88 let input = parse_macro_input!(input as DeriveInput);
89 expand_syntax_walk(&input).into()
90}
91
92struct SyntaxTokenWalk {
93 name: syn::Ident,
94 ty: Type,
95}
96
97impl Parse for SyntaxTokenWalk {
98 fn parse(input: ParseStream<'_>) -> Result<Self> {
99 let name = input.parse()?;
100 input.parse::<Token![,]>()?;
101 let ty = input.parse()?;
102 Ok(Self { name, ty })
103 }
104}
105
106#[doc(hidden)]
108#[proc_macro]
109pub fn __syntax_token_walk(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
110 let SyntaxTokenWalk { name, ty } = parse_macro_input!(input as SyntaxTokenWalk);
111 expand_syntax_token_walk(&name, &ty).into()
112}
113
114fn expand_syntax_token_walk(name: &syn::Ident, ty: &Type) -> TokenStream {
115 let snake = syntax::to_snake_case(&name.to_string());
116 let visit_method = format_ident!("visit_{snake}");
117 let visit_mut_method = format_ident!("visit_{snake}_mut");
118 let fold_method = format_ident!("fold_{snake}");
119 quote! {
120 impl __walk::WalkChildren for #ty {
121 fn walk_children<'ast, V>(&'ast self, _visitor: &mut V)
122 where
123 V: visit::Visit<'ast> + ?Sized,
124 {}
125 }
126
127 impl __walk::WalkChildrenMut for #ty {
128 fn walk_children_mut<V>(&mut self, _visitor: &mut V)
129 where
130 V: visit_mut::VisitMut + ?Sized,
131 {}
132 }
133
134 impl __walk::FoldChildren for #ty {
135 fn fold_children<F>(self, _folder: &mut F) -> Self
136 where
137 F: fold::Fold + ?Sized,
138 {
139 self
140 }
141 }
142
143 impl __walk::VisitValue for #ty {
144 fn visit_value<'ast, V>(&'ast self, visitor: &mut V)
145 where
146 V: visit::Visit<'ast> + ?Sized,
147 {
148 visitor.#visit_method(self);
149 }
150 }
151
152 impl __walk::VisitValueMut for #ty {
153 fn visit_value_mut<V>(&mut self, visitor: &mut V)
154 where
155 V: visit_mut::VisitMut + ?Sized,
156 {
157 visitor.#visit_mut_method(self);
158 }
159 }
160
161 impl __walk::FoldValue for #ty {
162 fn fold_value<F>(self, folder: &mut F) -> Self
163 where
164 F: fold::Fold + ?Sized,
165 {
166 folder.#fold_method(self)
167 }
168 }
169
170 }
171}
172
173struct SyntaxWalkFunction {
174 traversal: syn::Ident,
175 name: syn::Ident,
176 ty: Type,
177}
178
179impl Parse for SyntaxWalkFunction {
180 fn parse(input: ParseStream<'_>) -> Result<Self> {
181 let traversal = input.parse()?;
182 input.parse::<Token![,]>()?;
183 let name = input.parse()?;
184 input.parse::<Token![,]>()?;
185 let ty = input.parse()?;
186 Ok(Self {
187 traversal,
188 name,
189 ty,
190 })
191 }
192}
193
194#[doc(hidden)]
196#[proc_macro]
197pub fn __syntax_walk_function(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
198 let input = parse_macro_input!(input as SyntaxWalkFunction);
199 expand_syntax_walk_function(&input).into()
200}
201
202fn expand_syntax_walk_function(input: &SyntaxWalkFunction) -> TokenStream {
203 let name = &input.name;
204 let ty = &input.ty;
205 let snake = syntax::to_snake_case(&name.to_string());
206 match input.traversal.to_string().as_str() {
207 "visit" => {
208 let method = format_ident!("visit_{snake}");
209 quote! {
210 #[doc = concat!("Walks the children of a `", stringify!(#name), "` node.")]
211 pub fn #method<'ast, V>(visitor: &mut V, node: &'ast #ty)
212 where
213 V: Visit<'ast> + ?Sized,
214 {
215 super::__walk::WalkChildren::walk_children(node, visitor);
216 }
217 }
218 }
219 "visit_mut" => {
220 let method = format_ident!("visit_{snake}_mut");
221 quote! {
222 #[doc = concat!("Walks the mutable children of a `", stringify!(#name), "` node.")]
223 pub fn #method<V>(visitor: &mut V, node: &mut #ty)
224 where
225 V: VisitMut + ?Sized,
226 {
227 super::__walk::WalkChildrenMut::walk_children_mut(node, visitor);
228 }
229 }
230 }
231 "fold" => {
232 let method = format_ident!("fold_{snake}");
233 quote! {
234 #[doc = concat!("Folds the children of a `", stringify!(#name), "` node.")]
235 pub fn #method<F>(folder: &mut F, node: #ty) -> #ty
236 where
237 F: Fold + ?Sized,
238 {
239 super::__walk::FoldChildren::fold_children(node, folder)
240 }
241 }
242 }
243 _ => Error::new_spanned(&input.traversal, "expected `visit`, `visit_mut`, or `fold`")
244 .to_compile_error(),
245 }
246}
247
248fn is_syntax_storage(member: &Member) -> bool {
249 matches!(
250 member,
251 Member::Named(name)
252 if matches!(
253 name.to_string().as_str(),
254 "span"
255 | "raw"
256 | "package_qualifier"
257 | "index_qualifier"
258 | "leading_trivia"
259 | "trailing_trivia"
260 | "delimiter"
261 | "comments"
262 )
263 )
264}
265
266fn walk_struct_fields(fields: &Fields, mutable: bool) -> TokenStream {
267 let calls = fields
268 .members()
269 .filter(|member| !is_syntax_storage(member))
270 .map(|member| {
271 if mutable {
272 quote!(__walk::VisitValueMut::visit_value_mut(&mut self.#member, visitor);)
273 } else {
274 quote!(__walk::VisitValue::visit_value(&self.#member, visitor);)
275 }
276 });
277 quote!(#(#calls)*)
278}
279
280fn walk_enum_fields(input: &DeriveInput, mutable: bool) -> TokenStream {
281 let Data::Enum(data) = &input.data else {
282 unreachable!("enum walking requires an enum")
283 };
284 let arms = data.variants.iter().map(|variant| {
285 let name = &variant.ident;
286 let members = variant.fields.members().collect::<Vec<_>>();
287 let bindings = members
288 .iter()
289 .enumerate()
290 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
291 .collect::<Vec<_>>();
292 let selected = members
293 .iter()
294 .zip(&bindings)
295 .filter(|(member, _)| !is_syntax_storage(member));
296 let calls = selected.map(|(_, binding)| {
297 if mutable {
298 quote!(__walk::VisitValueMut::visit_value_mut(#binding, visitor);)
299 } else {
300 quote!(__walk::VisitValue::visit_value(#binding, visitor);)
301 }
302 });
303 match &variant.fields {
304 Fields::Named(_) => {
305 let patterns = members
306 .iter()
307 .zip(&bindings)
308 .map(|(member, binding)| quote!(#member: #binding));
309 quote!(Self::#name { #(#patterns),* } => { #(#calls)* })
310 }
311 Fields::Unnamed(_) => quote!(Self::#name(#(#bindings),*) => { #(#calls)* }),
312 Fields::Unit => quote!(Self::#name => {}),
313 }
314 });
315 quote!(match self { #(#arms,)* })
316}
317
318fn fold_struct(input: &DeriveInput) -> TokenStream {
319 let Data::Struct(data) = &input.data else {
320 unreachable!("struct folding requires a struct")
321 };
322 let name = &input.ident;
323 let members = data.fields.members().collect::<Vec<_>>();
324 let bindings = members
325 .iter()
326 .enumerate()
327 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
328 .collect::<Vec<_>>();
329 let values = members.iter().zip(&bindings).map(|(member, binding)| {
330 if is_syntax_storage(member) {
331 quote!(#binding)
332 } else {
333 quote!(__walk::FoldValue::fold_value(#binding, folder))
334 }
335 });
336 match &data.fields {
337 Fields::Named(_) => {
338 let patterns = members
339 .iter()
340 .zip(&bindings)
341 .map(|(member, binding)| quote!(#member: #binding));
342 let fields = members
343 .iter()
344 .zip(values)
345 .map(|(member, value)| quote!(#member: #value));
346 quote! {
347 let #name { #(#patterns),* } = self;
348 #name { #(#fields),* }
349 }
350 }
351 Fields::Unnamed(_) => quote! {
352 let #name(#(#bindings),*) = self;
353 #name(#(#values),*)
354 },
355 Fields::Unit => quote!(self),
356 }
357}
358
359fn fold_enum(input: &DeriveInput) -> TokenStream {
360 let Data::Enum(data) = &input.data else {
361 unreachable!("enum folding requires an enum")
362 };
363 let arms = data.variants.iter().map(|variant| {
364 let name = &variant.ident;
365 let members = variant.fields.members().collect::<Vec<_>>();
366 let bindings = members
367 .iter()
368 .enumerate()
369 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
370 .collect::<Vec<_>>();
371 let values = members.iter().zip(&bindings).map(|(member, binding)| {
372 if is_syntax_storage(member) {
373 quote!(#binding)
374 } else {
375 quote!(__walk::FoldValue::fold_value(#binding, folder))
376 }
377 });
378 match &variant.fields {
379 Fields::Named(_) => {
380 let patterns = members
381 .iter()
382 .zip(&bindings)
383 .map(|(member, binding)| quote!(#member: #binding));
384 let fields = members
385 .iter()
386 .zip(values)
387 .map(|(member, value)| quote!(#member: #value));
388 quote!(Self::#name { #(#patterns),* } => Self::#name { #(#fields),* })
389 }
390 Fields::Unnamed(_) => {
391 quote!(Self::#name(#(#bindings),*) => Self::#name(#(#values),*))
392 }
393 Fields::Unit => quote!(Self::#name => Self::#name),
394 }
395 });
396 quote!(match self { #(#arms,)* })
397}
398
399fn expand_syntax_walk(input: &DeriveInput) -> TokenStream {
400 let name = &input.ident;
401 if !input.generics.params.is_empty() {
402 return Error::new_spanned(input, "syntax walking requires a concrete syntax type")
403 .to_compile_error();
404 }
405 let snake = syntax::to_snake_case(&name.to_string());
406 let visit_method = format_ident!("visit_{snake}");
407 let visit_mut_method = format_ident!("visit_{snake}_mut");
408 let fold_method = format_ident!("fold_{snake}");
409 let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
410 let visit = match &input.data {
411 Data::Struct(data) => walk_struct_fields(&data.fields, false),
412 Data::Enum(_) => walk_enum_fields(input, false),
413 Data::Union(_) => {
414 return Error::new_spanned(input, "syntax walking does not support unions")
415 .to_compile_error();
416 }
417 };
418 let visit_mut = match &input.data {
419 Data::Struct(data) => walk_struct_fields(&data.fields, true),
420 Data::Enum(_) => walk_enum_fields(input, true),
421 Data::Union(_) => unreachable!(),
422 };
423 let fold = match &input.data {
424 Data::Struct(_) => fold_struct(input),
425 Data::Enum(_) => fold_enum(input),
426 Data::Union(_) => unreachable!(),
427 };
428 quote! {
429 impl #impl_generics __walk::WalkChildren for #name #type_generics #where_clause {
430 fn walk_children<'ast, V>(&'ast self, visitor: &mut V)
431 where
432 V: visit::Visit<'ast> + ?Sized,
433 {
434 #visit
435 }
436 }
437
438 impl #impl_generics __walk::WalkChildrenMut for #name #type_generics #where_clause {
439 fn walk_children_mut<V>(&mut self, visitor: &mut V)
440 where
441 V: visit_mut::VisitMut + ?Sized,
442 {
443 #visit_mut
444 }
445 }
446
447 impl #impl_generics __walk::FoldChildren for #name #type_generics #where_clause {
448 fn fold_children<F>(self, folder: &mut F) -> Self
449 where
450 F: fold::Fold + ?Sized,
451 {
452 #fold
453 }
454 }
455
456 impl __walk::VisitValue for #name {
457 fn visit_value<'ast, V>(&'ast self, visitor: &mut V)
458 where
459 V: visit::Visit<'ast> + ?Sized,
460 {
461 visitor.#visit_method(self);
462 }
463 }
464
465 impl __walk::VisitValueMut for #name {
466 fn visit_value_mut<V>(&mut self, visitor: &mut V)
467 where
468 V: visit_mut::VisitMut + ?Sized,
469 {
470 visitor.#visit_mut_method(self);
471 }
472 }
473
474 impl __walk::FoldValue for #name {
475 fn fold_value<F>(self, folder: &mut F) -> Self
476 where
477 F: fold::Fold + ?Sized,
478 {
479 folder.#fold_method(self)
480 }
481 }
482
483 }
484}
485
486fn expand_spanned_members(members: impl IntoIterator<Item = (Member, TokenStream)>) -> TokenStream {
487 let members = members.into_iter().collect::<Vec<_>>();
488 let base = base_crate();
489 let span = quote! {#base::Spanned::try_span};
490 if members.is_empty() {
491 return quote!(None);
492 }
493 if let Some((_, value)) = members
494 .iter()
495 .find(|(member, _)| matches!(member, Member::Named(ident) if ident == "span"))
496 {
497 quote!(#span(#value))
498 } else {
499 let values = members.iter().map(|(_, value)| value);
500 quote! {
501 #base::Span::join_all(
502 [#(#base::Spanned::try_span(#values)),*]
503 )
504 }
505 }
506}
507
508fn expand_spanned(input: &DeriveInput) -> Result<TokenStream> {
509 let name = &input.ident;
510 let base = base_crate();
511 let span_ty = quote! {#base::Span};
512 let mut generics = input.generics.clone();
513 let type_parameters = input
514 .generics
515 .type_params()
516 .map(|parameter| parameter.ident.to_string())
517 .collect::<BTreeSet<_>>();
518 for ty in spanned_field_types(input)
519 .into_iter()
520 .filter(|ty| type_mentions_parameter(ty, &type_parameters))
521 {
522 generics
523 .make_where_clause()
524 .predicates
525 .push(parse_quote!(#ty: #base::Spanned));
526 }
527 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
528 let body = match &input.data {
529 Data::Struct(data) => {
530 let members = data.fields.members().map(|member| {
531 let value = quote!(&self.#member);
532 (member, value)
533 });
534 expand_spanned_members(members)
535 }
536 Data::Enum(data) => {
537 let arms = data
538 .variants
539 .iter()
540 .map(|variant| {
541 let variant_name = &variant.ident;
542 let members = variant.fields.members().collect::<Vec<_>>();
543 let bindings = members
544 .iter()
545 .enumerate()
546 .map(|(index, _)| format_ident!("__m2_syn_field_{index}"))
547 .collect::<Vec<_>>();
548 let values = bindings.iter().map(|binding| quote!(#binding));
549 let span = expand_spanned_members(members.iter().cloned().zip(values));
550
551 match &variant.fields {
552 Fields::Named(_) => {
553 let fields = members
554 .iter()
555 .zip(&bindings)
556 .map(|(member, binding)| quote!(#member: #binding));
557 quote! {
558 Self::#variant_name { #(#fields),* } => #span
559 }
560 }
561 Fields::Unnamed(_) => quote! {
562 Self::#variant_name(#(#bindings),*) => #span
563 },
564 Fields::Unit => quote! {
565 Self::#variant_name => #span
566 },
567 }
568 })
569 .collect::<Vec<_>>();
570 let empty_fallback = data.variants.is_empty().then(|| quote!(_ => None));
571 quote! {
572 match self {
573 #(#arms,)*
574 #empty_fallback
575 }
576 }
577 }
578 Data::Union(_) => {
579 return Err(Error::new_spanned(
580 name,
581 "Spanned cannot be derived for unions",
582 ));
583 }
584 };
585
586 Ok(quote! {
587 impl #impl_generics
588 #base::Spanned for #name #type_generics #where_clause
589 {
590 fn try_span(&self) -> Option<#span_ty> {
591 #body
592 }
593 }
594 })
595}
596
597fn type_mentions_parameter(ty: &impl ToTokens, parameters: &BTreeSet<String>) -> bool {
598 fn stream_mentions_parameter(stream: TokenStream, parameters: &BTreeSet<String>) -> bool {
599 stream.into_iter().any(|token| match token {
600 TokenTree::Ident(ident) => parameters.contains(&ident.to_string()),
601 TokenTree::Group(group) => stream_mentions_parameter(group.stream(), parameters),
602 _ => false,
603 })
604 }
605 stream_mentions_parameter(ty.to_token_stream(), parameters)
606}
607
608fn spanned_field_types(input: &DeriveInput) -> Vec<&Type> {
609 fn selected(fields: &Fields) -> Vec<&Type> {
610 fields
611 .iter()
612 .find(|field| field.ident.as_ref().is_some_and(|ident| ident == "span"))
613 .map_or_else(
614 || fields.iter().map(|field| &field.ty).collect(),
615 |field| vec![&field.ty],
616 )
617 }
618
619 match &input.data {
620 Data::Struct(data) => selected(&data.fields),
621 Data::Enum(data) => data
622 .variants
623 .iter()
624 .flat_map(|variant| selected(&variant.fields))
625 .collect(),
626 Data::Union(_) => Vec::new(),
627 }
628}