xref: /linux-6.15/rust/macros/paste.rs (revision 211dcf77)
1823d4737SGary Guo // SPDX-License-Identifier: GPL-2.0
2823d4737SGary Guo 
3823d4737SGary Guo use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
4823d4737SGary Guo 
concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)>57e06561fSEthan D. Twardy fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
6823d4737SGary Guo     let mut tokens = tokens.iter();
7823d4737SGary Guo     let mut segments = Vec::new();
8823d4737SGary Guo     let mut span = None;
9823d4737SGary Guo     loop {
10823d4737SGary Guo         match tokens.next() {
11823d4737SGary Guo             None => break,
122dc318eaSTrevor Gross             Some(TokenTree::Literal(lit)) => {
132dc318eaSTrevor Gross                 // Allow us to concat string literals by stripping quotes
142dc318eaSTrevor Gross                 let mut value = lit.to_string();
152dc318eaSTrevor Gross                 if value.starts_with('"') && value.ends_with('"') {
162dc318eaSTrevor Gross                     value.remove(0);
172dc318eaSTrevor Gross                     value.pop();
182dc318eaSTrevor Gross                 }
192dc318eaSTrevor Gross                 segments.push((value, lit.span()));
202dc318eaSTrevor Gross             }
21823d4737SGary Guo             Some(TokenTree::Ident(ident)) => {
22823d4737SGary Guo                 let mut value = ident.to_string();
23823d4737SGary Guo                 if value.starts_with("r#") {
24823d4737SGary Guo                     value.replace_range(0..2, "");
25823d4737SGary Guo                 }
26823d4737SGary Guo                 segments.push((value, ident.span()));
27823d4737SGary Guo             }
28823d4737SGary Guo             Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
29823d4737SGary Guo                 let Some(TokenTree::Ident(ident)) = tokens.next() else {
30823d4737SGary Guo                     panic!("expected identifier as modifier");
31823d4737SGary Guo                 };
32823d4737SGary Guo 
33823d4737SGary Guo                 let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
34823d4737SGary Guo                 match ident.to_string().as_str() {
35823d4737SGary Guo                     // Set the overall span of concatenated token as current span
36823d4737SGary Guo                     "span" => {
37823d4737SGary Guo                         assert!(
38823d4737SGary Guo                             span.is_none(),
39823d4737SGary Guo                             "span modifier should only appear at most once"
40823d4737SGary Guo                         );
41823d4737SGary Guo                         span = Some(sp);
42823d4737SGary Guo                     }
43823d4737SGary Guo                     "lower" => value = value.to_lowercase(),
44823d4737SGary Guo                     "upper" => value = value.to_uppercase(),
45823d4737SGary Guo                     v => panic!("unknown modifier `{v}`"),
46823d4737SGary Guo                 };
47823d4737SGary Guo                 segments.push((value, sp));
48823d4737SGary Guo             }
497e06561fSEthan D. Twardy             Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => {
507e06561fSEthan D. Twardy                 let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>();
517e06561fSEthan D. Twardy                 segments.append(&mut concat_helper(tokens.as_slice()));
527e06561fSEthan D. Twardy             }
53*211dcf77SMiguel Ojeda             token => panic!("unexpected token in paste segments: {token:?}"),
54823d4737SGary Guo         };
55823d4737SGary Guo     }
56823d4737SGary Guo 
577e06561fSEthan D. Twardy     segments
587e06561fSEthan D. Twardy }
597e06561fSEthan D. Twardy 
concat(tokens: &[TokenTree], group_span: Span) -> TokenTree607e06561fSEthan D. Twardy fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
617e06561fSEthan D. Twardy     let segments = concat_helper(tokens);
62823d4737SGary Guo     let pasted: String = segments.into_iter().map(|x| x.0).collect();
637e06561fSEthan D. Twardy     TokenTree::Ident(Ident::new(&pasted, group_span))
64823d4737SGary Guo }
65823d4737SGary Guo 
expand(tokens: &mut Vec<TokenTree>)66823d4737SGary Guo pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
67823d4737SGary Guo     for token in tokens.iter_mut() {
68823d4737SGary Guo         if let TokenTree::Group(group) = token {
69823d4737SGary Guo             let delimiter = group.delimiter();
70823d4737SGary Guo             let span = group.span();
71823d4737SGary Guo             let mut stream: Vec<_> = group.stream().into_iter().collect();
72823d4737SGary Guo             // Find groups that looks like `[< A B C D >]`
73823d4737SGary Guo             if delimiter == Delimiter::Bracket
74823d4737SGary Guo                 && stream.len() >= 3
75823d4737SGary Guo                 && matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
76823d4737SGary Guo                 && matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
77823d4737SGary Guo             {
78823d4737SGary Guo                 // Replace the group with concatenated token
79823d4737SGary Guo                 *token = concat(&stream[1..stream.len() - 1], span);
80823d4737SGary Guo             } else {
81823d4737SGary Guo                 // Recursively expand tokens inside the group
82823d4737SGary Guo                 expand(&mut stream);
83823d4737SGary Guo                 let mut group = Group::new(delimiter, stream.into_iter().collect());
84823d4737SGary Guo                 group.set_span(span);
85823d4737SGary Guo                 *token = TokenTree::Group(group);
86823d4737SGary Guo             }
87823d4737SGary Guo         }
88823d4737SGary Guo     }
89823d4737SGary Guo 
90823d4737SGary Guo     // Path segments cannot contain invisible delimiter group, so remove them if any.
91823d4737SGary Guo     for i in (0..tokens.len().saturating_sub(3)).rev() {
92823d4737SGary Guo         // Looking for a double colon
93823d4737SGary Guo         if matches!(
94823d4737SGary Guo             (&tokens[i + 1], &tokens[i + 2]),
95823d4737SGary Guo             (TokenTree::Punct(a), TokenTree::Punct(b))
96823d4737SGary Guo                 if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
97823d4737SGary Guo         ) {
98823d4737SGary Guo             match &tokens[i + 3] {
99823d4737SGary Guo                 TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
100823d4737SGary Guo                     tokens.splice(i + 3..i + 4, group.stream());
101823d4737SGary Guo                 }
102823d4737SGary Guo                 _ => (),
103823d4737SGary Guo             }
104823d4737SGary Guo 
105823d4737SGary Guo             match &tokens[i] {
106823d4737SGary Guo                 TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
107823d4737SGary Guo                     tokens.splice(i..i + 1, group.stream());
108823d4737SGary Guo                 }
109823d4737SGary Guo                 _ => (),
110823d4737SGary Guo             }
111823d4737SGary Guo         }
112823d4737SGary Guo     }
113823d4737SGary Guo }
114