Pass crate environment to proc macros

This commit is contained in:
Jonas Schievink
2020-12-11 14:57:50 +01:00
parent 798968e1e3
commit 70877428a8
6 changed files with 42 additions and 9 deletions

View File

@@ -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)]

View File

@@ -271,7 +271,7 @@ fn expand_proc_macro(
_ => unreachable!(), _ => unreachable!(),
}; };
expander.expand(db, lazy_id, &macro_arg.0) expander.expand(db, loc.krate, &macro_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> {

View File

@@ -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),
} }

View File

@@ -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))?;

View File

@@ -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();

View File

@@ -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>");