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