6820: Pass the crate environment to proc macros r=jonas-schievink a=jonas-schievink

In theory, fixes https://github.com/rust-analyzer/rust-analyzer/issues/6696.

This seems to result in these obscure crashes for some reason:

```
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/scoped_cell.rs:75:51
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
   1: core::panicking::panic_fmt
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:85
   2: core::panicking::panic
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:50
   3: core::option::Option<T>::unwrap
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:383
   4: proc_macro::bridge::scoped_cell::ScopedCell<T>::replace
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/scoped_cell.rs:75
   5: proc_macro::bridge::client::BridgeState::with::{{closure}}
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:291
   6: std:🧵:local::LocalKey<T>::try_with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:272
   7: std:🧵:local::LocalKey<T>::with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:248
   8: proc_macro::bridge::client::BridgeState::with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:290
   9: proc_macro::bridge::client::<impl proc_macro::bridge::Bridge>::with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:329
  10: proc_macro::bridge::client::run_client::{{closure}}::{{closure}}
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:375
  11: proc_macro::bridge::scoped_cell::ScopedCell<T>::set::{{closure}}
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/scoped_cell.rs:80
  12: proc_macro::bridge::scoped_cell::ScopedCell<T>::replace
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/scoped_cell.rs:75
  13: proc_macro::bridge::scoped_cell::ScopedCell<T>::set
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/scoped_cell.rs:80
  14: proc_macro::bridge::client::<impl proc_macro::bridge::Bridge>::enter::{{closure}}
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:325
  15: std:🧵:local::LocalKey<T>::try_with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:272
  16: std:🧵:local::LocalKey<T>::with
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:248
  17: proc_macro::bridge::client::<impl proc_macro::bridge::Bridge>::enter
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:325
  18: proc_macro::bridge::client::run_client::{{closure}}
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:370
  19: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:308
  20: std::panicking::try::do_call
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:381
  21: __rust_try
  22: std::panicking::try
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:345
  23: std::panic::catch_unwind
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:382
  24: proc_macro::bridge::client::run_client
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:369
  25: proc_macro::bridge::client::Client<fn(proc_macro::TokenStream) .> proc_macro::TokenStream>::expand1::run
             at /home/jonas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro/src/bridge/client.rs:410
  26: proc_macro_srv::proc_macro::bridge::server::run_server
  27: proc_macro_srv::dylib::Expander::expand
  28: proc_macro_srv::ProcMacroSrv::expand
  29: proc_macro_srv::cli::run
  30: rust_analyzer::main
```

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2020-12-27 14:39:05 +00:00 committed by GitHub
commit 8f2622199e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 52 additions and 19 deletions

View File

@ -6,12 +6,12 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input.
use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc};
use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
use cfg::CfgOptions;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr;
use tt::TokenExpander;
use tt::{ExpansionError, Subtree};
use vfs::{file_set::FileSet, FileId, VfsPath};
/// Files are grouped into source roots. A source root is a directory on the
@ -150,11 +150,20 @@ pub enum ProcMacroKind {
Attr,
}
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
fn expand(
&self,
subtree: &Subtree,
attrs: Option<&Subtree>,
env: &Env,
) -> Result<Subtree, ExpansionError>;
}
#[derive(Debug, Clone)]
pub struct ProcMacro {
pub name: SmolStr,
pub kind: ProcMacroKind,
pub expander: Arc<dyn TokenExpander>,
pub expander: Arc<dyn ProcMacroExpander>,
}
impl Eq for ProcMacro {}
@ -413,6 +422,10 @@ impl Env {
pub fn get(&self, env: &str) -> Option<String> {
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)]

View File

@ -14,7 +14,7 @@ pub use crate::{
change::Change,
input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env,
ProcMacro, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId,
ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId,
},
};
pub use salsa;

View File

@ -271,7 +271,7 @@ fn expand_proc_macro(
_ => 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> {

View File

@ -1,6 +1,6 @@
//! Proc Macro Expander stub
use crate::{db::AstDatabase, LazyMacroId};
use crate::db::AstDatabase;
use base_db::{CrateId, ProcMacroId};
use tt::buffer::{Cursor, TokenBuffer};
@ -32,7 +32,7 @@ impl ProcMacroExpander {
pub fn expand(
self,
db: &dyn AstDatabase,
_id: LazyMacroId,
calling_crate: CrateId,
tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
match self.proc_macro_id {
@ -47,7 +47,10 @@ impl ProcMacroExpander {
let tt = remove_derive_attrs(tt)
.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),
}

View File

@ -16,7 +16,7 @@ use std::{
sync::Arc,
};
use base_db::ProcMacro;
use base_db::{Env, ProcMacro};
use tt::{SmolStr, Subtree};
use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
@ -39,17 +39,19 @@ impl PartialEq for ProcMacroProcessExpander {
}
}
impl tt::TokenExpander for ProcMacroProcessExpander {
impl base_db::ProcMacroExpander for ProcMacroProcessExpander {
fn expand(
&self,
subtree: &Subtree,
attr: Option<&Subtree>,
env: &Env,
) -> Result<Subtree, tt::ExpansionError> {
let task = ExpansionTask {
macro_body: subtree.clone(),
macro_name: self.name.to_string(),
attributes: attr.cloned(),
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))?;
@ -90,7 +92,7 @@ impl ProcMacroClient {
ProcMacroKind::FuncLike => base_db::ProcMacroKind::FuncLike,
ProcMacroKind::Attr => base_db::ProcMacroKind::Attr,
};
let expander: Arc<dyn tt::TokenExpander> = Arc::new(ProcMacroProcessExpander {
let expander = Arc::new(ProcMacroProcessExpander {
process: self.process.clone(),
name: name.clone(),
dylib_path: dylib_path.into(),

View File

@ -51,6 +51,9 @@ pub struct ExpansionTask {
pub attributes: Option<Subtree>,
pub lib: PathBuf,
/// Environment variables to set during macro expansion.
pub env: Vec<(String, String)>,
}
#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
@ -251,6 +254,7 @@ mod tests {
macro_name: Default::default(),
attributes: None,
lib: Default::default(),
env: Default::default(),
};
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 std::{
collections::{hash_map::Entry, HashMap},
fs,
env, fs,
path::{Path, PathBuf},
time::SystemTime,
};
@ -37,7 +37,23 @@ pub(crate) struct ProcMacroSrv {
impl ProcMacroSrv {
pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
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 }),
Err(msg) => {
let msg = msg.as_str().unwrap_or("<unknown error>");

View File

@ -1,7 +1,7 @@
//! `tt` crate defines a `TokenTree` data structure: this is the interface (both
//! input and output) of macros. It closely mirrors `proc_macro` crate's
//! `TokenTree`.
use std::{fmt, panic::RefUnwindSafe};
use std::fmt;
use stdx::impl_from;
@ -247,8 +247,3 @@ impl fmt::Display for ExpansionError {
}
}
}
pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
-> Result<Subtree, ExpansionError>;
}