mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 22:31:43 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
3e0e51c108
commit
bc45c7659a
321 changed files with 11210 additions and 9720 deletions
|
@ -2,15 +2,17 @@
|
|||
name = "proc-macro-api"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
object = { version = "0.29.0", default-features = false, features = [
|
||||
object = { version = "0.30.2", default-features = false, features = [
|
||||
"std",
|
||||
"read_core",
|
||||
"elf",
|
||||
|
@ -21,11 +23,12 @@ serde = { version = "1.0.137", features = ["derive"] }
|
|||
serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
|
||||
tracing = "0.1.37"
|
||||
memmap2 = "0.5.4"
|
||||
snap = "1.0.5"
|
||||
snap = "1.1.0"
|
||||
|
||||
paths = { path = "../paths", version = "0.0.0" }
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
# local deps
|
||||
paths.workspace = true
|
||||
tt.workspace = true
|
||||
stdx.workspace = true
|
||||
profile.workspace = true
|
||||
# Intentionally *not* depend on anything salsa-related
|
||||
# base-db = { path = "../base-db", version = "0.0.0" }
|
||||
# base-db.workspace = true
|
||||
|
|
|
@ -19,7 +19,8 @@ use std::{
|
|||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tt::Subtree;
|
||||
|
||||
use ::tt::token_id as tt;
|
||||
|
||||
use crate::{
|
||||
msg::{ExpandMacro, FlatTree, PanicMessage},
|
||||
|
@ -70,7 +71,7 @@ impl MacroDylib {
|
|||
|
||||
/// A handle to a specific macro (a `#[proc_macro]` annotated function).
|
||||
///
|
||||
/// It exists withing a context of a specific [`ProcMacroProcess`] -- currently
|
||||
/// It exists within a context of a specific [`ProcMacroProcess`] -- currently
|
||||
/// we share a single expander process for all macros.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProcMacro {
|
||||
|
@ -114,14 +115,14 @@ impl ProcMacroServer {
|
|||
/// Spawns an external process as the proc macro server and returns a client connected to it.
|
||||
pub fn spawn(
|
||||
process_path: AbsPathBuf,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
|
||||
) -> io::Result<ProcMacroServer> {
|
||||
let process = ProcMacroProcessSrv::run(process_path, args)?;
|
||||
Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) })
|
||||
}
|
||||
|
||||
pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> {
|
||||
let _p = profile::span("ProcMacroClient::by_dylib_path");
|
||||
let _p = profile::span("ProcMacroClient::load_dylib");
|
||||
let macros =
|
||||
self.process.lock().unwrap_or_else(|e| e.into_inner()).find_proc_macros(&dylib.path)?;
|
||||
|
||||
|
@ -151,10 +152,10 @@ impl ProcMacro {
|
|||
|
||||
pub fn expand(
|
||||
&self,
|
||||
subtree: &Subtree,
|
||||
attr: Option<&Subtree>,
|
||||
subtree: &tt::Subtree,
|
||||
attr: Option<&tt::Subtree>,
|
||||
env: Vec<(String, String)>,
|
||||
) -> Result<Result<Subtree, PanicMessage>, ServerError> {
|
||||
) -> Result<Result<tt::Subtree, PanicMessage>, ServerError> {
|
||||
let current_dir = env
|
||||
.iter()
|
||||
.find(|(name, _)| name == "CARGO_MANIFEST_DIR")
|
||||
|
@ -173,7 +174,7 @@ impl ProcMacro {
|
|||
let response = self.process.lock().unwrap_or_else(|e| e.into_inner()).send_task(request)?;
|
||||
match response {
|
||||
msg::Response::ExpandMacro(it) => Ok(it.map(FlatTree::to_subtree)),
|
||||
msg::Response::ListMacros { .. } => {
|
||||
msg::Response::ListMacros(..) | msg::Response::ApiVersionCheck(..) => {
|
||||
Err(ServerError { message: "unexpected response".to_string(), io: None })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,16 +12,21 @@ use crate::ProcMacroKind;
|
|||
|
||||
pub use crate::msg::flat::FlatTree;
|
||||
|
||||
pub const NO_VERSION_CHECK_VERSION: u32 = 0;
|
||||
pub const CURRENT_API_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Request {
|
||||
ListMacros { dylib_path: PathBuf },
|
||||
ExpandMacro(ExpandMacro),
|
||||
ApiVersionCheck {},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Response {
|
||||
ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
|
||||
ExpandMacro(Result<FlatTree, PanicMessage>),
|
||||
ApiVersionCheck(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -107,27 +112,31 @@ fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tt::*;
|
||||
use crate::tt::*;
|
||||
|
||||
fn fixture_token_tree() -> Subtree {
|
||||
let mut subtree = Subtree::default();
|
||||
let mut subtree = Subtree { delimiter: Delimiter::unspecified(), token_trees: Vec::new() };
|
||||
subtree
|
||||
.token_trees
|
||||
.push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into()));
|
||||
.push(TokenTree::Leaf(Ident { text: "struct".into(), span: TokenId(0) }.into()));
|
||||
subtree
|
||||
.token_trees
|
||||
.push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into()));
|
||||
.push(TokenTree::Leaf(Ident { text: "Foo".into(), span: TokenId(1) }.into()));
|
||||
subtree.token_trees.push(TokenTree::Leaf(Leaf::Literal(Literal {
|
||||
text: "Foo".into(),
|
||||
id: TokenId::unspecified(),
|
||||
span: TokenId::unspecified(),
|
||||
})));
|
||||
subtree.token_trees.push(TokenTree::Leaf(Leaf::Punct(Punct {
|
||||
char: '@',
|
||||
id: TokenId::unspecified(),
|
||||
span: TokenId::unspecified(),
|
||||
spacing: Spacing::Joint,
|
||||
})));
|
||||
subtree.token_trees.push(TokenTree::Subtree(Subtree {
|
||||
delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }),
|
||||
delimiter: Delimiter {
|
||||
open: TokenId(2),
|
||||
close: TokenId::UNSPECIFIED,
|
||||
kind: DelimiterKind::Brace,
|
||||
},
|
||||
token_trees: vec![],
|
||||
}));
|
||||
subtree
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tt::TokenId;
|
||||
|
||||
use crate::tt::{self, TokenId};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FlatTree {
|
||||
|
@ -52,7 +53,7 @@ pub struct FlatTree {
|
|||
|
||||
struct SubtreeRepr {
|
||||
id: tt::TokenId,
|
||||
kind: Option<tt::DelimiterKind>,
|
||||
kind: tt::DelimiterKind,
|
||||
tt: [u32; 2],
|
||||
}
|
||||
|
||||
|
@ -124,19 +125,19 @@ impl FlatTree {
|
|||
impl SubtreeRepr {
|
||||
fn write(self) -> [u32; 4] {
|
||||
let kind = match self.kind {
|
||||
None => 0,
|
||||
Some(tt::DelimiterKind::Parenthesis) => 1,
|
||||
Some(tt::DelimiterKind::Brace) => 2,
|
||||
Some(tt::DelimiterKind::Bracket) => 3,
|
||||
tt::DelimiterKind::Invisible => 0,
|
||||
tt::DelimiterKind::Parenthesis => 1,
|
||||
tt::DelimiterKind::Brace => 2,
|
||||
tt::DelimiterKind::Bracket => 3,
|
||||
};
|
||||
[self.id.0, kind, self.tt[0], self.tt[1]]
|
||||
}
|
||||
fn read([id, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
|
||||
let kind = match kind {
|
||||
0 => None,
|
||||
1 => Some(tt::DelimiterKind::Parenthesis),
|
||||
2 => Some(tt::DelimiterKind::Brace),
|
||||
3 => Some(tt::DelimiterKind::Bracket),
|
||||
0 => tt::DelimiterKind::Invisible,
|
||||
1 => tt::DelimiterKind::Parenthesis,
|
||||
2 => tt::DelimiterKind::Brace,
|
||||
3 => tt::DelimiterKind::Bracket,
|
||||
other => panic!("bad kind {other}"),
|
||||
};
|
||||
SubtreeRepr { id: TokenId(id), kind, tt: [lo, len] }
|
||||
|
@ -216,7 +217,7 @@ impl<'a> Writer<'a> {
|
|||
tt::Leaf::Literal(lit) => {
|
||||
let idx = self.literal.len() as u32;
|
||||
let text = self.intern(&lit.text);
|
||||
self.literal.push(LiteralRepr { id: lit.id, text });
|
||||
self.literal.push(LiteralRepr { id: lit.span, text });
|
||||
idx << 2 | 0b01
|
||||
}
|
||||
tt::Leaf::Punct(punct) => {
|
||||
|
@ -224,14 +225,14 @@ impl<'a> Writer<'a> {
|
|||
self.punct.push(PunctRepr {
|
||||
char: punct.char,
|
||||
spacing: punct.spacing,
|
||||
id: punct.id,
|
||||
id: punct.span,
|
||||
});
|
||||
idx << 2 | 0b10
|
||||
}
|
||||
tt::Leaf::Ident(ident) => {
|
||||
let idx = self.ident.len() as u32;
|
||||
let text = self.intern(&ident.text);
|
||||
self.ident.push(IdentRepr { id: ident.id, text });
|
||||
self.ident.push(IdentRepr { id: ident.span, text });
|
||||
idx << 2 | 0b11
|
||||
}
|
||||
},
|
||||
|
@ -243,8 +244,8 @@ impl<'a> Writer<'a> {
|
|||
|
||||
fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 {
|
||||
let idx = self.subtree.len();
|
||||
let delimiter_id = subtree.delimiter.map_or(TokenId::unspecified(), |it| it.id);
|
||||
let delimiter_kind = subtree.delimiter.map(|it| it.kind);
|
||||
let delimiter_id = subtree.delimiter.open;
|
||||
let delimiter_kind = subtree.delimiter.kind;
|
||||
self.subtree.push(SubtreeRepr { id: delimiter_id, kind: delimiter_kind, tt: [!0, !0] });
|
||||
self.work.push_back((idx, subtree));
|
||||
idx as u32
|
||||
|
@ -276,7 +277,11 @@ impl Reader {
|
|||
let repr = &self.subtree[i];
|
||||
let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
|
||||
let s = tt::Subtree {
|
||||
delimiter: repr.kind.map(|kind| tt::Delimiter { id: repr.id, kind }),
|
||||
delimiter: tt::Delimiter {
|
||||
open: repr.id,
|
||||
close: TokenId::UNSPECIFIED,
|
||||
kind: repr.kind,
|
||||
},
|
||||
token_trees: token_trees
|
||||
.iter()
|
||||
.copied()
|
||||
|
@ -291,7 +296,7 @@ impl Reader {
|
|||
let repr = &self.literal[idx];
|
||||
tt::Leaf::Literal(tt::Literal {
|
||||
text: self.text[repr.text as usize].as_str().into(),
|
||||
id: repr.id,
|
||||
span: repr.id,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
@ -300,7 +305,7 @@ impl Reader {
|
|||
tt::Leaf::Punct(tt::Punct {
|
||||
char: repr.char,
|
||||
spacing: repr.spacing,
|
||||
id: repr.id,
|
||||
span: repr.id,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
@ -308,7 +313,7 @@ impl Reader {
|
|||
let repr = &self.ident[idx];
|
||||
tt::Leaf::Ident(tt::Ident {
|
||||
text: self.text[repr.text as usize].as_str().into(),
|
||||
id: repr.id,
|
||||
span: repr.id,
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use paths::{AbsPath, AbsPathBuf};
|
|||
use stdx::JodChild;
|
||||
|
||||
use crate::{
|
||||
msg::{Message, Request, Response},
|
||||
msg::{Message, Request, Response, CURRENT_API_VERSION},
|
||||
ProcMacroKind, ServerError,
|
||||
};
|
||||
|
||||
|
@ -19,19 +19,53 @@ pub(crate) struct ProcMacroProcessSrv {
|
|||
_process: Process,
|
||||
stdin: ChildStdin,
|
||||
stdout: BufReader<ChildStdout>,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
impl ProcMacroProcessSrv {
|
||||
pub(crate) fn run(
|
||||
process_path: AbsPathBuf,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
|
||||
) -> io::Result<ProcMacroProcessSrv> {
|
||||
let mut process = Process::run(process_path, args)?;
|
||||
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
|
||||
let create_srv = |null_stderr| {
|
||||
let mut process = Process::run(process_path.clone(), args.clone(), null_stderr)?;
|
||||
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
|
||||
|
||||
let srv = ProcMacroProcessSrv { _process: process, stdin, stdout };
|
||||
io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 })
|
||||
};
|
||||
let mut srv = create_srv(true)?;
|
||||
tracing::info!("sending version check");
|
||||
match srv.version_check() {
|
||||
Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!(
|
||||
"proc-macro server's api version ({}) is newer than rust-analyzer's ({})",
|
||||
v, CURRENT_API_VERSION
|
||||
),
|
||||
)),
|
||||
Ok(v) => {
|
||||
tracing::info!("got version {v}");
|
||||
srv = create_srv(false)?;
|
||||
srv.version = v;
|
||||
Ok(srv)
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::info!(%e, "proc-macro version check failed, restarting and assuming version 0");
|
||||
create_srv(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(srv)
|
||||
pub(crate) fn version_check(&mut self) -> Result<u32, ServerError> {
|
||||
let request = Request::ApiVersionCheck {};
|
||||
let response = self.send_task(request)?;
|
||||
|
||||
match response {
|
||||
Response::ApiVersionCheck(version) => Ok(version),
|
||||
Response::ExpandMacro { .. } | Response::ListMacros { .. } => {
|
||||
Err(ServerError { message: "unexpected response".to_string(), io: None })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_proc_macros(
|
||||
|
@ -44,7 +78,7 @@ impl ProcMacroProcessSrv {
|
|||
|
||||
match response {
|
||||
Response::ListMacros(it) => Ok(it),
|
||||
Response::ExpandMacro { .. } => {
|
||||
Response::ExpandMacro { .. } | Response::ApiVersionCheck { .. } => {
|
||||
Err(ServerError { message: "unexpected response".to_string(), io: None })
|
||||
}
|
||||
}
|
||||
|
@ -65,9 +99,10 @@ impl Process {
|
|||
fn run(
|
||||
path: AbsPathBuf,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
|
||||
null_stderr: bool,
|
||||
) -> io::Result<Process> {
|
||||
let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
|
||||
let child = JodChild(mk_child(&path, args)?);
|
||||
let child = JodChild(mk_child(&path, args, null_stderr)?);
|
||||
Ok(Process { child })
|
||||
}
|
||||
|
||||
|
@ -83,13 +118,14 @@ impl Process {
|
|||
fn mk_child(
|
||||
path: &AbsPath,
|
||||
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
|
||||
null_stderr: bool,
|
||||
) -> io::Result<Child> {
|
||||
Command::new(path.as_os_str())
|
||||
.args(args)
|
||||
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() })
|
||||
.spawn()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue