| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- // SPDX-License-Identifier: GPL-2.0
- use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenTree};
- fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> {
- let mut tokens = tokens.iter();
- let mut segments = Vec::new();
- let mut span = None;
- loop {
- match tokens.next() {
- None => break,
- Some(TokenTree::Literal(lit)) => {
- // Allow us to concat string literals by stripping quotes
- let mut value = lit.to_string();
- if value.starts_with('"') && value.ends_with('"') {
- value.remove(0);
- value.pop();
- }
- segments.push((value, lit.span()));
- }
- Some(TokenTree::Ident(ident)) => {
- let mut value = ident.to_string();
- if value.starts_with("r#") {
- value.replace_range(0..2, "");
- }
- segments.push((value, ident.span()));
- }
- Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
- let Some(TokenTree::Ident(ident)) = tokens.next() else {
- panic!("expected identifier as modifier");
- };
- let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
- match ident.to_string().as_str() {
- // Set the overall span of concatenated token as current span
- "span" => {
- assert!(
- span.is_none(),
- "span modifier should only appear at most once"
- );
- span = Some(sp);
- }
- "lower" => value = value.to_lowercase(),
- "upper" => value = value.to_uppercase(),
- v => panic!("unknown modifier `{v}`"),
- };
- segments.push((value, sp));
- }
- Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => {
- let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>();
- segments.append(&mut concat_helper(tokens.as_slice()));
- }
- token => panic!("unexpected token in paste segments: {token:?}"),
- };
- }
- segments
- }
- fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
- let segments = concat_helper(tokens);
- let pasted: String = segments.into_iter().map(|x| x.0).collect();
- TokenTree::Ident(Ident::new(&pasted, group_span))
- }
- pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
- for token in tokens.iter_mut() {
- if let TokenTree::Group(group) = token {
- let delimiter = group.delimiter();
- let span = group.span();
- let mut stream: Vec<_> = group.stream().into_iter().collect();
- // Find groups that looks like `[< A B C D >]`
- if delimiter == Delimiter::Bracket
- && stream.len() >= 3
- && matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
- && matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
- {
- // Replace the group with concatenated token
- *token = concat(&stream[1..stream.len() - 1], span);
- } else {
- // Recursively expand tokens inside the group
- expand(&mut stream);
- let mut group = Group::new(delimiter, stream.into_iter().collect());
- group.set_span(span);
- *token = TokenTree::Group(group);
- }
- }
- }
- // Path segments cannot contain invisible delimiter group, so remove them if any.
- for i in (0..tokens.len().saturating_sub(3)).rev() {
- // Looking for a double colon
- if matches!(
- (&tokens[i + 1], &tokens[i + 2]),
- (TokenTree::Punct(a), TokenTree::Punct(b))
- if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
- ) {
- match &tokens[i + 3] {
- TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
- tokens.splice(i + 3..i + 4, group.stream());
- }
- _ => (),
- }
- match &tokens[i] {
- TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
- tokens.splice(i..i + 1, group.stream());
- }
- _ => (),
- }
- }
- }
- }
|