]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / proc-macro-srv / src / lib.rs
CommitLineData
064997fb
FG
1//! RA Proc Macro Server
2//!
3//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
4//! The general idea here is based on <https://github.com/fedochet/rust-proc-macro-expander>.
5//!
6//! But we adapt it to better fit RA needs:
7//!
8//! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with
9//! RA than `proc-macro2` token stream.
10//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
11//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
12
13#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
14#![cfg_attr(
15 feature = "sysroot-abi",
16 feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
17)]
18#![allow(unreachable_pub)]
19
20mod dylib;
21mod abis;
22
2b03887a
FG
23pub mod cli;
24
064997fb
FG
25use std::{
26 collections::{hash_map::Entry, HashMap},
27 env,
28 ffi::OsString,
29 fs,
30 path::{Path, PathBuf},
f2b60f7d 31 thread,
064997fb
FG
32 time::SystemTime,
33};
34
35use proc_macro_api::{
36 msg::{ExpandMacro, FlatTree, PanicMessage},
37 ProcMacroKind,
38};
39
40#[derive(Default)]
41pub(crate) struct ProcMacroSrv {
42 expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
43}
44
45const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
46
47impl ProcMacroSrv {
48 pub fn expand(&mut self, task: ExpandMacro) -> Result<FlatTree, PanicMessage> {
49 let expander = self.expander(task.lib.as_ref()).map_err(|err| {
50 debug_assert!(false, "should list macros before asking to expand");
f25598a0 51 PanicMessage(format!("failed to load macro: {err}"))
064997fb
FG
52 })?;
53
54 let prev_env = EnvSnapshot::new();
55 for (k, v) in &task.env {
56 env::set_var(k, v);
57 }
58 let prev_working_dir = match task.current_dir {
59 Some(dir) => {
60 let prev_working_dir = std::env::current_dir().ok();
61 if let Err(err) = std::env::set_current_dir(&dir) {
f25598a0 62 eprintln!("Failed to set the current working dir to {dir}. Error: {err:?}")
064997fb
FG
63 }
64 prev_working_dir
65 }
66 None => None,
67 };
68
69 let macro_body = task.macro_body.to_subtree();
70 let attributes = task.attributes.map(|it| it.to_subtree());
f2b60f7d
FG
71 let result = thread::scope(|s| {
72 let thread = thread::Builder::new()
064997fb
FG
73 .stack_size(EXPANDER_STACK_SIZE)
74 .name(task.macro_name.clone())
f2b60f7d 75 .spawn_scoped(s, || {
064997fb
FG
76 expander
77 .expand(&task.macro_name, &macro_body, attributes.as_ref())
78 .map(|it| FlatTree::new(&it))
f2b60f7d
FG
79 });
80 let res = match thread {
064997fb
FG
81 Ok(handle) => handle.join(),
82 Err(e) => std::panic::resume_unwind(Box::new(e)),
83 };
84
85 match res {
86 Ok(res) => res,
87 Err(e) => std::panic::resume_unwind(e),
88 }
89 });
064997fb
FG
90
91 prev_env.rollback();
92
93 if let Some(dir) = prev_working_dir {
94 if let Err(err) = std::env::set_current_dir(&dir) {
95 eprintln!(
96 "Failed to set the current working dir to {}. Error: {:?}",
97 dir.display(),
98 err
99 )
100 }
101 }
102
103 result.map_err(PanicMessage)
104 }
105
106 pub(crate) fn list_macros(
107 &mut self,
108 dylib_path: &Path,
109 ) -> Result<Vec<(String, ProcMacroKind)>, String> {
110 let expander = self.expander(dylib_path)?;
111 Ok(expander.list_macros())
112 }
113
114 fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
f25598a0
FG
115 let time = fs::metadata(path)
116 .and_then(|it| it.modified())
117 .map_err(|err| format!("Failed to get file metadata for {}: {err}", path.display()))?;
064997fb
FG
118
119 Ok(match self.expanders.entry((path.to_path_buf(), time)) {
f25598a0
FG
120 Entry::Vacant(v) => {
121 v.insert(dylib::Expander::new(path).map_err(|err| {
122 format!("Cannot create expander for {}: {err}", path.display())
123 })?)
124 }
064997fb
FG
125 Entry::Occupied(e) => e.into_mut(),
126 })
127 }
128}
129
130struct EnvSnapshot {
131 vars: HashMap<OsString, OsString>,
132}
133
134impl EnvSnapshot {
135 fn new() -> EnvSnapshot {
136 EnvSnapshot { vars: env::vars_os().collect() }
137 }
138
139 fn rollback(self) {
140 let mut old_vars = self.vars;
141 for (name, value) in env::vars_os() {
142 let old_value = old_vars.remove(&name);
143 if old_value != Some(value) {
144 match old_value {
145 None => env::remove_var(name),
146 Some(old_value) => env::set_var(name, old_value),
147 }
148 }
149 }
150 for (name, old_value) in old_vars {
151 env::set_var(name, old_value)
152 }
153 }
154}
155
2b03887a
FG
156#[cfg(all(feature = "sysroot-abi", test))]
157mod tests;
064997fb
FG
158
159#[cfg(test)]
2b03887a
FG
160pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
161 proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
162}