rust/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

471 lines
15 KiB
Rust
Raw Normal View History

//! 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
2022-07-20 11:36:10 -05:00
use super::proc_macro::{
self,
bridge::{self, server},
};
2022-07-20 11:32:27 -05:00
mod token_stream;
2022-07-20 11:43:59 -05:00
pub use token_stream::TokenStream;
use token_stream::TokenStreamBuilder;
2022-07-20 11:32:27 -05:00
2022-07-20 11:36:10 -05:00
mod symbol;
pub use symbol::*;
2022-07-20 11:43:59 -05:00
use std::{iter::FromIterator, ops::Bound};
type Group = tt::Subtree;
type TokenTree = tt::TokenTree;
type Punct = tt::Punct;
type Spacing = tt::Spacing;
type Literal = tt::Literal;
type Span = tt::TokenId;
#[derive(Clone)]
pub struct SourceFile {
// FIXME stub
}
type Level = super::proc_macro::Level;
type LineColumn = super::proc_macro::LineColumn;
/// A structure representing a diagnostic message and associated children
/// messages.
#[derive(Clone, Debug)]
pub struct Diagnostic {
level: Level,
message: String,
spans: Vec<Span>,
children: Vec<Diagnostic>,
}
impl Diagnostic {
/// Creates a new diagnostic with the given `level` and `message`.
pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
}
}
pub struct FreeFunctions;
#[derive(Default)]
pub struct RustAnalyzer {
// FIXME: store span information here.
}
impl server::Types for RustAnalyzer {
type FreeFunctions = FreeFunctions;
type TokenStream = TokenStream;
type SourceFile = SourceFile;
2022-07-20 11:32:27 -05:00
type MultiSpan = Vec<Span>;
type Diagnostic = Diagnostic;
type Span = Span;
2022-07-20 11:32:27 -05:00
type Symbol = Symbol;
}
impl server::FreeFunctions for RustAnalyzer {
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
// FIXME: track env var accesses
// https://github.com/rust-lang/rust/pull/71858
}
fn track_path(&mut self, _path: &str) {}
2022-07-20 11:43:59 -05:00
fn literal_from_str(
&mut self,
_s: &str,
) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
todo!()
}
}
impl server::TokenStream for RustAnalyzer {
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
stream.is_empty()
}
fn from_str(&mut self, src: &str) -> Self::TokenStream {
use std::str::FromStr;
Self::TokenStream::from_str(src).expect("cannot parse string")
}
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
stream.to_string()
}
fn from_token_tree(
&mut self,
2022-07-20 11:43:59 -05:00
tree: bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>,
) -> Self::TokenStream {
match tree {
bridge::TokenTree::Group(group) => {
let group = Group {
delimiter: delim_to_internal(group.delimiter),
token_trees: match group.stream {
Some(stream) => stream.into_iter().collect(),
None => Vec::new(),
},
};
let tree = TokenTree::from(group);
Self::TokenStream::from_iter(vec![tree])
}
bridge::TokenTree::Ident(ident) => {
// FIXME: handle raw idents
let text = SYMBOL_INTERNER.lock().unwrap().get(&ident.sym).clone();
let ident: tt::Ident = tt::Ident { text, id: ident.span };
let leaf = tt::Leaf::from(ident);
let tree = TokenTree::from(leaf);
Self::TokenStream::from_iter(vec![tree])
}
bridge::TokenTree::Literal(literal) => {
let symbol = SYMBOL_INTERNER.lock().unwrap().get(&literal.symbol).clone();
let text: tt::SmolStr = if let Some(suffix) = literal.suffix {
let suffix = SYMBOL_INTERNER.lock().unwrap().get(&suffix).clone();
format!("{symbol}{suffix}").into()
} else {
symbol
};
let literal = tt::Literal { text, id: literal.span };
let leaf = tt::Leaf::from(literal);
let tree = TokenTree::from(leaf);
Self::TokenStream::from_iter(vec![tree])
}
bridge::TokenTree::Punct(p) => {
let punct = tt::Punct {
char: p.ch as char,
spacing: if p.joint { Spacing::Joint } else { Spacing::Alone },
id: p.span,
};
let leaf = tt::Leaf::from(punct);
let tree = TokenTree::from(leaf);
Self::TokenStream::from_iter(vec![tree])
}
}
}
fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
Ok(self_.clone())
}
fn concat_trees(
&mut self,
base: Option<Self::TokenStream>,
2022-07-20 11:43:59 -05:00
trees: Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>,
) -> Self::TokenStream {
let mut builder = TokenStreamBuilder::new();
if let Some(base) = base {
builder.push(base);
}
for tree in trees {
builder.push(self.from_token_tree(tree));
}
builder.build()
}
fn concat_streams(
&mut self,
base: Option<Self::TokenStream>,
streams: Vec<Self::TokenStream>,
) -> Self::TokenStream {
let mut builder = TokenStreamBuilder::new();
if let Some(base) = base {
builder.push(base);
}
for stream in streams {
builder.push(stream);
}
builder.build()
}
fn into_trees(
&mut self,
stream: Self::TokenStream,
2022-07-20 11:43:59 -05:00
) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> {
stream
.into_iter()
.map(|tree| match tree {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
bridge::TokenTree::Ident(bridge::Ident {
sym: SYMBOL_INTERNER.lock().unwrap().intern(&ident.text),
// FIXME: handle raw idents
is_raw: false,
span: ident.id,
})
}
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
bridge::TokenTree::Literal(bridge::Literal {
// FIXME: handle literal kinds
kind: bridge::LitKind::Err,
symbol: SYMBOL_INTERNER.lock().unwrap().intern(&lit.text),
// FIXME: handle suffixes
suffix: None,
span: lit.id,
})
2022-07-20 11:43:59 -05:00
}
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
bridge::TokenTree::Punct(bridge::Punct {
ch: punct.char as u8,
joint: punct.spacing == Spacing::Joint,
span: punct.id,
})
}
tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group {
delimiter: delim_to_external(subtree.delimiter),
stream: if subtree.token_trees.is_empty() {
None
} else {
Some(subtree.token_trees.into_iter().collect())
},
span: bridge::DelimSpan::from_single(
subtree.delimiter.map_or(Span::unspecified(), |del| del.id),
),
}),
})
.collect()
}
}
2022-07-20 11:36:10 -05:00
fn delim_to_internal(d: proc_macro::Delimiter) -> Option<tt::Delimiter> {
let kind = match d {
2022-07-20 11:36:10 -05:00
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 => return None,
};
Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind })
}
2022-07-20 11:36:10 -05:00
fn delim_to_external(d: Option<tt::Delimiter>) -> proc_macro::Delimiter {
match d.map(|it| it.kind) {
2022-07-20 11:36:10 -05:00
Some(tt::DelimiterKind::Parenthesis) => proc_macro::Delimiter::Parenthesis,
Some(tt::DelimiterKind::Brace) => proc_macro::Delimiter::Brace,
Some(tt::DelimiterKind::Bracket) => proc_macro::Delimiter::Bracket,
None => proc_macro::Delimiter::None,
}
}
2022-07-20 11:36:10 -05:00
fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
match spacing {
2022-07-20 11:36:10 -05:00
proc_macro::Spacing::Alone => Spacing::Alone,
proc_macro::Spacing::Joint => Spacing::Joint,
}
}
2022-07-20 11:36:10 -05:00
fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
match spacing {
2022-07-20 11:36:10 -05:00
Spacing::Alone => proc_macro::Spacing::Alone,
Spacing::Joint => proc_macro::Spacing::Joint,
}
}
impl server::SourceFile for RustAnalyzer {
// FIXME these are all stubs
fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
true
}
fn path(&mut self, _file: &Self::SourceFile) -> String {
String::new()
}
fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
true
}
}
impl server::Diagnostic for RustAnalyzer {
fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
let mut diag = Diagnostic::new(level, msg);
diag.spans = spans;
diag
}
fn sub(
&mut self,
_diag: &mut Self::Diagnostic,
_level: Level,
_msg: &str,
_spans: Self::MultiSpan,
) {
// FIXME handle diagnostic
//
}
fn emit(&mut self, _diag: Self::Diagnostic) {
// FIXME handle diagnostic
// diag.emit()
}
}
impl server::Span for RustAnalyzer {
fn debug(&mut self, span: Self::Span) -> String {
format!("{:?}", span.0)
}
fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
SourceFile {}
}
fn save_span(&mut self, _span: Self::Span) -> usize {
// FIXME stub
0
}
fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
// FIXME stub
tt::TokenId::unspecified()
}
/// Recent feature, not yet in the proc_macro
///
/// See PR:
/// https://github.com/rust-lang/rust/pull/55780
fn source_text(&mut self, _span: Self::Span) -> Option<String> {
None
}
fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
// FIXME handle span
None
}
fn source(&mut self, span: Self::Span) -> Self::Span {
// FIXME handle span
span
}
fn start(&mut self, _span: Self::Span) -> LineColumn {
// FIXME handle span
LineColumn { line: 0, column: 0 }
}
fn end(&mut self, _span: Self::Span) -> LineColumn {
// FIXME handle span
LineColumn { line: 0, column: 0 }
}
fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
// Just return the first span again, because some macros will unwrap the result.
Some(first)
}
2022-07-20 11:43:59 -05:00
fn subspan(
&mut self,
span: Self::Span,
_start: Bound<usize>,
_end: Bound<usize>,
) -> Option<Self::Span> {
// Just return the span again, because some macros will unwrap the result.
Some(span)
}
fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
// FIXME handle span
tt::TokenId::unspecified()
}
fn after(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
fn before(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
}
impl server::MultiSpan for RustAnalyzer {
fn new(&mut self) -> Self::MultiSpan {
// FIXME handle span
vec![]
}
fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
//TODP
other.push(span)
}
}
2022-07-20 11:43:59 -05:00
impl server::Symbol for RustAnalyzer {
fn normalize_and_validate_ident(&mut self, _string: &str) -> Result<Self::Symbol, ()> {
todo!()
}
}
impl server::Server for RustAnalyzer {
fn globals(&mut self) -> bridge::ExpnGlobals<Self::Span> {
bridge::ExpnGlobals {
def_site: Span::unspecified(),
call_site: Span::unspecified(),
mixed_site: Span::unspecified(),
}
}
2022-07-20 11:43:59 -05:00
fn intern_symbol(ident: &str) -> Self::Symbol {
SYMBOL_INTERNER.lock().unwrap().intern(&tt::SmolStr::from(ident))
2022-07-20 11:43:59 -05:00
}
fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
f(SYMBOL_INTERNER.lock().unwrap().get(symbol).as_str())
2022-07-20 11:43:59 -05:00
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ra_server_to_string() {
let s = TokenStream {
token_trees: vec![
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
text: "struct".into(),
id: tt::TokenId::unspecified(),
})),
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
text: "T".into(),
id: tt::TokenId::unspecified(),
})),
tt::TokenTree::Subtree(tt::Subtree {
delimiter: Some(tt::Delimiter {
id: tt::TokenId::unspecified(),
kind: tt::DelimiterKind::Brace,
}),
token_trees: vec![],
}),
],
};
assert_eq!(s.to_string(), "struct T {}");
}
#[test]
fn test_ra_server_from_str() {
use std::str::FromStr;
let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
delimiter: Some(tt::Delimiter {
id: tt::TokenId::unspecified(),
kind: tt::DelimiterKind::Parenthesis,
}),
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
text: "a".into(),
id: tt::TokenId::unspecified(),
}))],
});
let t1 = TokenStream::from_str("(a)").unwrap();
assert_eq!(t1.token_trees.len(), 1);
assert_eq!(t1.token_trees[0], subtree_paren_a);
let t2 = TokenStream::from_str("(a);").unwrap();
assert_eq!(t2.token_trees.len(), 2);
assert_eq!(t2.token_trees[0], subtree_paren_a);
let underscore = TokenStream::from_str("_").unwrap();
assert_eq!(
underscore.token_trees[0],
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
text: "_".into(),
id: tt::TokenId::unspecified(),
}))
);
}
}