summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs')
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs518
1 files changed, 518 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
new file mode 100644
index 000000000..46882845a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
@@ -0,0 +1,518 @@
+//! 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 super::proc_macro::{
+ self,
+ bridge::{self, server},
+};
+
+mod token_stream;
+pub use token_stream::TokenStream;
+use token_stream::TokenStreamBuilder;
+
+mod symbol;
+pub use symbol::*;
+
+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;
+ type MultiSpan = Vec<Span>;
+ type Diagnostic = Diagnostic;
+ type Span = Span;
+ 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) {}
+
+ fn literal_from_str(
+ &mut self,
+ s: &str,
+ ) -> Result<bridge::Literal<Self::Span, Self::Symbol>, ()> {
+ // FIXME: keep track of LitKind and Suffix
+ Ok(bridge::Literal {
+ kind: bridge::LitKind::Err,
+ symbol: Symbol::intern(s),
+ suffix: None,
+ span: tt::TokenId::unspecified(),
+ })
+ }
+}
+
+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,
+ 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 = ident.sym.text();
+ 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 literal = LiteralFormatter(literal);
+ let text = literal
+ .with_stringify_parts(|parts| tt::SmolStr::from_iter(parts.iter().copied()));
+
+ let literal = tt::Literal { text, id: literal.0.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>,
+ 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,
+ ) -> 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::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::intern(&lit.text),
+ // FIXME: handle suffixes
+ suffix: None,
+ span: lit.id,
+ })
+ }
+ 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()
+ }
+}
+
+fn delim_to_internal(d: proc_macro::Delimiter) -> Option<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 => return None,
+ };
+ Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind })
+}
+
+fn delim_to_external(d: Option<tt::Delimiter>) -> proc_macro::Delimiter {
+ match d.map(|it| it.kind) {
+ 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,
+ }
+}
+
+fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
+ match spacing {
+ proc_macro::Spacing::Alone => Spacing::Alone,
+ proc_macro::Spacing::Joint => Spacing::Joint,
+ }
+}
+
+fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
+ match spacing {
+ 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)
+ }
+ 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)
+ }
+}
+
+impl server::Symbol for RustAnalyzer {
+ fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> {
+ // FIXME: nfc-normalize and validate idents
+ Ok(<Self as server::Server>::intern_symbol(string))
+ }
+}
+
+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(),
+ }
+ }
+
+ fn intern_symbol(ident: &str) -> Self::Symbol {
+ Symbol::intern(&tt::SmolStr::from(ident))
+ }
+
+ fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
+ f(symbol.text().as_str())
+ }
+}
+
+struct LiteralFormatter(bridge::Literal<tt::TokenId, Symbol>);
+
+impl LiteralFormatter {
+ /// 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, 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(|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, f: impl FnOnce(&str, &str) -> R) -> R {
+ let symbol = self.0.symbol.text();
+ let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default();
+ f(symbol.as_str(), suffix.as_str())
+ }
+}
+
+#[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(),
+ }))
+ );
+ }
+}