diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/proc-macro-api/src')
5 files changed, 102 insertions, 48 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 7921fda33..90d06967e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -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 }) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index f9c2b9fda..4040efe93 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -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 diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index b178c4626..fd3202e0b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -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() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 54dcb17f4..1ccbd780f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -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"); + + 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) + } + } + } - let srv = ProcMacroProcessSrv { _process: process, stdin, stdout }; + pub(crate) fn version_check(&mut self) -> Result<u32, ServerError> { + let request = Request::ApiVersionCheck {}; + let response = self.send_task(request)?; - Ok(srv) + 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() } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs index 40125c2a5..cf637ec35 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs @@ -120,17 +120,20 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> { let version = u32::from_be_bytes([dot_rustc[4], dot_rustc[5], dot_rustc[6], dot_rustc[7]]); // Last supported version is: // https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632 - match version { - 5 | 6 => {} + let snappy_portion = match version { + 5 | 6 => &dot_rustc[8..], + 7 => { + let len_bytes = &dot_rustc[8..12]; + let data_len = u32::from_be_bytes(len_bytes.try_into().unwrap()) as usize; + &dot_rustc[12..data_len + 12] + } _ => { return Err(io::Error::new( io::ErrorKind::InvalidData, format!("unsupported metadata version {version}"), )); } - } - - let snappy_portion = &dot_rustc[8..]; + }; let mut snappy_decoder = SnapDecoder::new(snappy_portion); |