//! proc-macro server implementation //! //! Based on idea from //! The lib-proc-macro server backend is `TokenStream`-agnostic, such that //! we could provide any TokenStream implementation. //! The original idea from fedochet is using proc-macro2 as backend, //! we use tt instead for better integration with RA. //! //! FIXME: No span and source file information is implemented yet use std::fmt; use intern::Symbol; use proc_macro::bridge; mod token_stream; pub use token_stream::TokenStream; pub mod rust_analyzer_span; pub mod token_id; use tt::Spacing; #[derive(Clone)] pub(crate) struct TopSubtree(pub(crate) Vec>); impl fmt::Debug for TopSubtree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&tt::TokenTreesView::new(&self.0), f) } } impl TopSubtree { pub(crate) fn top_subtree(&self) -> &tt::Subtree { let tt::TokenTree::Subtree(subtree) = &self.0[0] else { unreachable!("the first token tree is always the top subtree"); }; subtree } pub(crate) fn from_bridge(group: bridge::Group, S>) -> Self { let delimiter = delim_to_internal(group.delimiter, group.span); let mut tts = group.stream.map(|it| it.token_trees).unwrap_or_else(|| Vec::with_capacity(1)); tts.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: tts.len() as u32 })); TopSubtree(tts) } } fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter { let kind = match d { proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, proc_macro::Delimiter::Brace => tt::DelimiterKind::Brace, proc_macro::Delimiter::Bracket => tt::DelimiterKind::Bracket, proc_macro::Delimiter::None => tt::DelimiterKind::Invisible, }; tt::Delimiter { open: span.open, close: span.close, kind } } fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter { match d.kind { tt::DelimiterKind::Parenthesis => proc_macro::Delimiter::Parenthesis, tt::DelimiterKind::Brace => proc_macro::Delimiter::Brace, tt::DelimiterKind::Bracket => proc_macro::Delimiter::Bracket, tt::DelimiterKind::Invisible => proc_macro::Delimiter::None, } } #[allow(unused)] fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { match spacing { proc_macro::Spacing::Alone => Spacing::Alone, proc_macro::Spacing::Joint => Spacing::Joint, } } #[allow(unused)] fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing { match spacing { Spacing::Alone | Spacing::JointHidden => proc_macro::Spacing::Alone, Spacing::Joint => proc_macro::Spacing::Joint, } } fn literal_kind_to_external(kind: tt::LitKind) -> bridge::LitKind { match kind { tt::LitKind::Byte => bridge::LitKind::Byte, tt::LitKind::Char => bridge::LitKind::Char, tt::LitKind::Integer => bridge::LitKind::Integer, tt::LitKind::Float => bridge::LitKind::Float, tt::LitKind::Str => bridge::LitKind::Str, tt::LitKind::StrRaw(r) => bridge::LitKind::StrRaw(r), tt::LitKind::ByteStr => bridge::LitKind::ByteStr, tt::LitKind::ByteStrRaw(r) => bridge::LitKind::ByteStrRaw(r), tt::LitKind::CStr => bridge::LitKind::CStr, tt::LitKind::CStrRaw(r) => bridge::LitKind::CStrRaw(r), tt::LitKind::Err(_) => bridge::LitKind::ErrWithGuar, } } fn literal_kind_to_internal(kind: bridge::LitKind) -> tt::LitKind { match kind { bridge::LitKind::Byte => tt::LitKind::Byte, bridge::LitKind::Char => tt::LitKind::Char, bridge::LitKind::Str => tt::LitKind::Str, bridge::LitKind::StrRaw(r) => tt::LitKind::StrRaw(r), bridge::LitKind::ByteStr => tt::LitKind::ByteStr, bridge::LitKind::ByteStrRaw(r) => tt::LitKind::ByteStrRaw(r), bridge::LitKind::CStr => tt::LitKind::CStr, bridge::LitKind::CStrRaw(r) => tt::LitKind::CStrRaw(r), bridge::LitKind::Integer => tt::LitKind::Integer, bridge::LitKind::Float => tt::LitKind::Float, bridge::LitKind::ErrWithGuar => tt::LitKind::Err(()), } } pub(super) fn literal_from_str( s: &str, span: Span, ) -> Result, ()> { use proc_macro::bridge::LitKind; use rustc_lexer::{LiteralKind, Token, TokenKind}; let mut tokens = rustc_lexer::tokenize(s, rustc_lexer::FrontmatterAllowed::No); let minus_or_lit = tokens.next().unwrap_or(Token { kind: TokenKind::Eof, len: 0 }); let lit = if minus_or_lit.kind == TokenKind::Minus { let lit = tokens.next().ok_or(())?; if !matches!( lit.kind, TokenKind::Literal { kind: LiteralKind::Int { .. } | LiteralKind::Float { .. }, .. } ) { return Err(()); } lit } else { minus_or_lit }; if tokens.next().is_some() { return Err(()); } let TokenKind::Literal { kind, suffix_start } = lit.kind else { return Err(()) }; let (kind, start_offset, end_offset) = match kind { LiteralKind::Int { .. } => (LitKind::Integer, 0, 0), LiteralKind::Float { .. } => (LitKind::Float, 0, 0), LiteralKind::Char { terminated } => (LitKind::Char, 1, terminated as usize), LiteralKind::Byte { terminated } => (LitKind::Byte, 2, terminated as usize), LiteralKind::Str { terminated } => (LitKind::Str, 1, terminated as usize), LiteralKind::ByteStr { terminated } => (LitKind::ByteStr, 2, terminated as usize), LiteralKind::CStr { terminated } => (LitKind::CStr, 2, terminated as usize), LiteralKind::RawStr { n_hashes } => ( LitKind::StrRaw(n_hashes.unwrap_or_default()), 2 + n_hashes.unwrap_or_default() as usize, 1 + n_hashes.unwrap_or_default() as usize, ), LiteralKind::RawByteStr { n_hashes } => ( LitKind::ByteStrRaw(n_hashes.unwrap_or_default()), 3 + n_hashes.unwrap_or_default() as usize, 1 + n_hashes.unwrap_or_default() as usize, ), LiteralKind::RawCStr { n_hashes } => ( LitKind::CStrRaw(n_hashes.unwrap_or_default()), 3 + n_hashes.unwrap_or_default() as usize, 1 + n_hashes.unwrap_or_default() as usize, ), }; let (lit, suffix) = s.split_at(suffix_start as usize); let lit = &lit[start_offset..lit.len() - end_offset]; let suffix = match suffix { "" | "_" => None, suffix => Some(Symbol::intern(suffix)), }; Ok(bridge::Literal { kind, symbol: Symbol::intern(lit), suffix, span }) } pub(super) fn from_token_tree( tree: bridge::TokenTree, Span, Symbol>, ) -> TokenStream { match tree { bridge::TokenTree::Group(group) => { let group = TopSubtree::from_bridge(group); TokenStream { token_trees: group.0 } } bridge::TokenTree::Ident(ident) => { let text = ident.sym; let ident: tt::Ident = tt::Ident { sym: text, span: ident.span, is_raw: if ident.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }, }; let leaf = tt::Leaf::from(ident); let tree = tt::TokenTree::from(leaf); TokenStream { token_trees: vec![tree] } } bridge::TokenTree::Literal(literal) => { let mut token_trees = Vec::new(); let mut symbol = literal.symbol; if matches!( literal.kind, proc_macro::bridge::LitKind::Integer | proc_macro::bridge::LitKind::Float ) && symbol.as_str().starts_with('-') { token_trees.push(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { spacing: tt::Spacing::Alone, span: literal.span, char: '-' as char, }))); symbol = Symbol::intern(&symbol.as_str()[1..]); } let literal = tt::Literal { symbol, suffix: literal.suffix, span: literal.span, kind: literal_kind_to_internal(literal.kind), }; let leaf: tt::Leaf = tt::Leaf::from(literal); let tree = tt::TokenTree::from(leaf); token_trees.push(tree); TokenStream { token_trees } } bridge::TokenTree::Punct(p) => { let punct = tt::Punct { char: p.ch as char, spacing: if p.joint { tt::Spacing::Joint } else { tt::Spacing::Alone }, span: p.span, }; let leaf = tt::Leaf::from(punct); let tree = tt::TokenTree::from(leaf); TokenStream { token_trees: vec![tree] } } } }