brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · f40d42b Raw
105 lines · rust
1// SPDX-License-Identifier: GPL-2.02 3use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};4 5fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {6    let mut tokens = tokens.iter();7    let mut segments = Vec::new();8    let mut span = None;9    loop {10        match tokens.next() {11            None => break,12            Some(TokenTree::Literal(lit)) => {13                // Allow us to concat string literals by stripping quotes14                let mut value = lit.to_string();15                if value.starts_with('"') && value.ends_with('"') {16                    value.remove(0);17                    value.pop();18                }19                segments.push((value, lit.span()));20            }21            Some(TokenTree::Ident(ident)) => {22                let mut value = ident.to_string();23                if value.starts_with("r#") {24                    value.replace_range(0..2, "");25                }26                segments.push((value, ident.span()));27            }28            Some(TokenTree::Punct(p)) if p.as_char() == ':' => {29                let Some(TokenTree::Ident(ident)) = tokens.next() else {30                    panic!("expected identifier as modifier");31                };32 33                let (mut value, sp) = segments.pop().expect("expected identifier before modifier");34                match ident.to_string().as_str() {35                    // Set the overall span of concatenated token as current span36                    "span" => {37                        assert!(38                            span.is_none(),39                            "span modifier should only appear at most once"40                        );41                        span = Some(sp);42                    }43                    "lower" => value = value.to_lowercase(),44                    "upper" => value = value.to_uppercase(),45                    v => panic!("unknown modifier `{v}`"),46                };47                segments.push((value, sp));48            }49            _ => panic!("unexpected token in paste segments"),50        };51    }52 53    let pasted: String = segments.into_iter().map(|x| x.0).collect();54    TokenTree::Ident(Ident::new(&pasted, span.unwrap_or(group_span)))55}56 57pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {58    for token in tokens.iter_mut() {59        if let TokenTree::Group(group) = token {60            let delimiter = group.delimiter();61            let span = group.span();62            let mut stream: Vec<_> = group.stream().into_iter().collect();63            // Find groups that looks like `[< A B C D >]`64            if delimiter == Delimiter::Bracket65                && stream.len() >= 366                && matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')67                && matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')68            {69                // Replace the group with concatenated token70                *token = concat(&stream[1..stream.len() - 1], span);71            } else {72                // Recursively expand tokens inside the group73                expand(&mut stream);74                let mut group = Group::new(delimiter, stream.into_iter().collect());75                group.set_span(span);76                *token = TokenTree::Group(group);77            }78        }79    }80 81    // Path segments cannot contain invisible delimiter group, so remove them if any.82    for i in (0..tokens.len().saturating_sub(3)).rev() {83        // Looking for a double colon84        if matches!(85            (&tokens[i + 1], &tokens[i + 2]),86            (TokenTree::Punct(a), TokenTree::Punct(b))87                if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'88        ) {89            match &tokens[i + 3] {90                TokenTree::Group(group) if group.delimiter() == Delimiter::None => {91                    tokens.splice(i + 3..i + 4, group.stream());92                }93                _ => (),94            }95 96            match &tokens[i] {97                TokenTree::Group(group) if group.delimiter() == Delimiter::None => {98                    tokens.splice(i..i + 1, group.stream());99                }100                _ => (),101            }102        }103    }104}105