Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! proc-macro server implementation
//!
//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
//! 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 proc_macro::bridge;

mod token_stream;
pub use token_stream::TokenStream;

pub mod rust_analyzer_span;
mod symbol;
pub mod token_id;
pub use symbol::*;
use syntax::ast::{self, HasModuleItem, IsString};
use tt::Spacing;

fn delim_to_internal<S>(d: proc_macro::Delimiter, span: bridge::DelimSpan<S>) -> tt::Delimiter<S> {
    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<S>(d: tt::Delimiter<S>) -> 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 => proc_macro::Spacing::Alone,
        Spacing::Joint => proc_macro::Spacing::Joint,
    }
}

fn literal_to_external(literal: ast::LiteralKind) -> Option<proc_macro::bridge::LitKind> {
    Some(match lit.kind() {
        ast::LiteralKind::String(data) => {
            if data.is_raw() {
                bridge::LitKind::StrRaw(data.raw_delimiter_count()?)
            } else {
                bridge::LitKind::Str
            }
        }
        ast::LiteralKind::ByteString(data) => {
            if data.is_raw() {
                bridge::LitKind::ByteStrRaw(data.raw_delimiter_count()?)
            } else {
                bridge::LitKind::ByteStr
            }
        }
        ast::LiteralKind::CString(data) => {
            if data.is_raw() {
                bridge::LitKind::CStrRaw(data.raw_delimiter_count()?)
            } else {
                bridge::LitKind::CStr
            }
        }
        ast::LiteralKind::IntNumber(num) => bridge::LitKind::Integer,
        ast::LiteralKind::FloatNumber(num) => bridge::LitKind::Float,
        ast::LiteralKind::Char(_) => bridge::LitKind::Char,
        ast::LiteralKind::Byte(_) => bridge::LitKind::Byte,
        ast::LiteralKind::Bool(_) => unreachable!(),
    })
}

fn str_to_lit_node(input: &str) -> Option<ast::Literal> {
    let input = input.trim();
    let source_code = format!("fn f() {{ let _ = {input}; }}");

    let parse = ast::SourceFile::parse(&source_code);
    let file = parse.tree();

    let ast::Item::Fn(func) = file.items().next()? else { return None };
    let ast::Stmt::LetStmt(stmt) = func.body()?.stmt_list()?.statements().next()? else {
        return None;
    };
    let ast::Expr::Literal(lit) = stmt.initializer()? else { return None };

    Some(lit)
}

struct LiteralFormatter<S>(bridge::Literal<S, Symbol>);

impl<S> LiteralFormatter<S> {
    /// Invokes the callback with a `&[&str]` consisting of each part of the
    /// literal's representation. This is done to allow the `ToString` and
    /// `Display` implementations to borrow references to symbol values, and
    /// both be optimized to reduce overhead.
    fn with_stringify_parts<R>(
        &self,
        interner: SymbolInternerRef,
        f: impl FnOnce(&[&str]) -> R,
    ) -> R {
        /// Returns a string containing exactly `num` '#' characters.
        /// Uses a 256-character source string literal which is always safe to
        /// index with a `u8` index.
        fn get_hashes_str(num: u8) -> &'static str {
            const HASHES: &str = "\
                        ################################################################\
                        ################################################################\
                        ################################################################\
                        ################################################################\
                        ";
            const _: () = assert!(HASHES.len() == 256);
            &HASHES[..num as usize]
        }

        self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind {
            bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
            bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
            bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
            bridge::LitKind::StrRaw(n) => {
                let hashes = get_hashes_str(n);
                f(&["r", hashes, "\"", symbol, "\"", hashes, suffix])
            }
            bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]),
            bridge::LitKind::ByteStrRaw(n) => {
                let hashes = get_hashes_str(n);
                f(&["br", hashes, "\"", symbol, "\"", hashes, suffix])
            }
            _ => f(&[symbol, suffix]),
        })
    }

    fn with_symbol_and_suffix<R>(
        &self,
        interner: SymbolInternerRef,
        f: impl FnOnce(&str, &str) -> R,
    ) -> R {
        let symbol = self.0.symbol.text(interner);
        let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default();
        f(symbol.as_str(), suffix.as_str())
    }
}