Pass crate environment to proc macros
This commit is contained in:
@@ -151,8 +151,12 @@ pub enum ProcMacroKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
|
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
|
||||||
fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
|
fn expand(
|
||||||
-> Result<Subtree, ExpansionError>;
|
&self,
|
||||||
|
subtree: &Subtree,
|
||||||
|
attrs: Option<&Subtree>,
|
||||||
|
env: &Env,
|
||||||
|
) -> Result<Subtree, ExpansionError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -418,6 +422,10 @@ impl Env {
|
|||||||
pub fn get(&self, env: &str) -> Option<String> {
|
pub fn get(&self, env: &str) -> Option<String> {
|
||||||
self.entries.get(env).cloned()
|
self.entries.get(env).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
|
||||||
|
self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ fn expand_proc_macro(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
expander.expand(db, lazy_id, ¯o_arg.0)
|
expander.expand(db, loc.krate, ¯o_arg.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
|
fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Proc Macro Expander stub
|
//! Proc Macro Expander stub
|
||||||
|
|
||||||
use crate::{db::AstDatabase, LazyMacroId};
|
use crate::db::AstDatabase;
|
||||||
use base_db::{CrateId, ProcMacroId};
|
use base_db::{CrateId, ProcMacroId};
|
||||||
use tt::buffer::{Cursor, TokenBuffer};
|
use tt::buffer::{Cursor, TokenBuffer};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ impl ProcMacroExpander {
|
|||||||
pub fn expand(
|
pub fn expand(
|
||||||
self,
|
self,
|
||||||
db: &dyn AstDatabase,
|
db: &dyn AstDatabase,
|
||||||
_id: LazyMacroId,
|
calling_crate: CrateId,
|
||||||
tt: &tt::Subtree,
|
tt: &tt::Subtree,
|
||||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||||
match self.proc_macro_id {
|
match self.proc_macro_id {
|
||||||
@@ -47,7 +47,10 @@ impl ProcMacroExpander {
|
|||||||
let tt = remove_derive_attrs(tt)
|
let tt = remove_derive_attrs(tt)
|
||||||
.ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
|
.ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
|
||||||
|
|
||||||
proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
|
// Proc macros have access to the environment variables of the invoking crate.
|
||||||
|
let env = &krate_graph[calling_crate].env;
|
||||||
|
|
||||||
|
proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from)
|
||||||
}
|
}
|
||||||
None => Err(mbe::ExpandError::UnresolvedProcMacro),
|
None => Err(mbe::ExpandError::UnresolvedProcMacro),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use std::{
|
|||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use base_db::ProcMacro;
|
use base_db::{Env, ProcMacro};
|
||||||
use tt::{SmolStr, Subtree};
|
use tt::{SmolStr, Subtree};
|
||||||
|
|
||||||
use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
|
use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
|
||||||
@@ -44,12 +44,14 @@ impl base_db::ProcMacroExpander for ProcMacroProcessExpander {
|
|||||||
&self,
|
&self,
|
||||||
subtree: &Subtree,
|
subtree: &Subtree,
|
||||||
attr: Option<&Subtree>,
|
attr: Option<&Subtree>,
|
||||||
|
env: &Env,
|
||||||
) -> Result<Subtree, tt::ExpansionError> {
|
) -> Result<Subtree, tt::ExpansionError> {
|
||||||
let task = ExpansionTask {
|
let task = ExpansionTask {
|
||||||
macro_body: subtree.clone(),
|
macro_body: subtree.clone(),
|
||||||
macro_name: self.name.to_string(),
|
macro_name: self.name.to_string(),
|
||||||
attributes: attr.cloned(),
|
attributes: attr.cloned(),
|
||||||
lib: self.dylib_path.to_path_buf(),
|
lib: self.dylib_path.to_path_buf(),
|
||||||
|
env: env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?;
|
let result: ExpansionResult = self.process.send_task(msg::Request::ExpansionMacro(task))?;
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ pub struct ExpansionTask {
|
|||||||
pub attributes: Option<Subtree>,
|
pub attributes: Option<Subtree>,
|
||||||
|
|
||||||
pub lib: PathBuf,
|
pub lib: PathBuf,
|
||||||
|
|
||||||
|
/// Environment variables to set during macro expansion.
|
||||||
|
pub env: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||||
@@ -251,6 +254,7 @@ mod tests {
|
|||||||
macro_name: Default::default(),
|
macro_name: Default::default(),
|
||||||
attributes: None,
|
attributes: None,
|
||||||
lib: Default::default(),
|
lib: Default::default(),
|
||||||
|
env: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let json = serde_json::to_string(&task).unwrap();
|
let json = serde_json::to_string(&task).unwrap();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use proc_macro::bridge::client::TokenStream;
|
|||||||
use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
|
use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, HashMap},
|
||||||
fs,
|
env, fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
@@ -37,7 +37,23 @@ pub(crate) struct ProcMacroSrv {
|
|||||||
impl ProcMacroSrv {
|
impl ProcMacroSrv {
|
||||||
pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
|
pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
|
||||||
let expander = self.expander(&task.lib)?;
|
let expander = self.expander(&task.lib)?;
|
||||||
match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
|
|
||||||
|
let mut prev_env = HashMap::new();
|
||||||
|
for (k, v) in &task.env {
|
||||||
|
prev_env.insert(k.as_str(), env::var_os(k));
|
||||||
|
env::set_var(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref());
|
||||||
|
|
||||||
|
for (k, _) in &task.env {
|
||||||
|
match &prev_env[k.as_str()] {
|
||||||
|
Some(v) => env::set_var(k, v),
|
||||||
|
None => env::remove_var(k),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match result {
|
||||||
Ok(expansion) => Ok(ExpansionResult { expansion }),
|
Ok(expansion) => Ok(ExpansionResult { expansion }),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
let msg = msg.as_str().unwrap_or("<unknown error>");
|
let msg = msg.as_str().unwrap_or("<unknown error>");
|
||||||
|
|||||||
Reference in New Issue
Block a user