3727: Introduce ra_proc_macro r=matklad a=edwin0cheng

This PR implemented:

1.  Reading dylib path of proc-macro crate from cargo check , similar to how `OUTDIR` is obtained.
2.  Added a new crate `ra_proc_macro` and implement the foot-work for reading result from external proc-macro expander. 
3. Added a struct `ProcMacroClient` , which will be responsible to the client side communication to the External process.



Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
bors[bot] 2020-03-26 17:09:32 +00:00 committed by GitHub
commit b1594f1080
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 259 additions and 38 deletions

9
Cargo.lock generated
View File

@ -928,6 +928,7 @@ dependencies = [
"ra_cfg",
"ra_prof",
"ra_syntax",
"ra_tt",
"relative-path",
"rustc-hash",
"salsa",
@ -1081,6 +1082,13 @@ dependencies = [
"drop_bomb",
]
[[package]]
name = "ra_proc_macro"
version = "0.1.0"
dependencies = [
"ra_tt",
]
[[package]]
name = "ra_prof"
version = "0.1.0"
@ -1102,6 +1110,7 @@ dependencies = [
"ra_cargo_watch",
"ra_cfg",
"ra_db",
"ra_proc_macro",
"rustc-hash",
"serde",
"serde_json",

View File

@ -15,4 +15,5 @@ rustc-hash = "1.1.0"
ra_syntax = { path = "../ra_syntax" }
ra_cfg = { path = "../ra_cfg" }
ra_prof = { path = "../ra_prof" }
ra_tt = { path = "../ra_tt" }
test_utils = { path = "../test_utils" }

View File

@ -70,6 +70,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
meta.cfg,
meta.env,
Default::default(),
Default::default(),
);
crate_graph
} else {
@ -81,6 +82,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
crate_graph
};
@ -130,6 +132,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
meta.cfg,
meta.env,
Default::default(),
Default::default(),
);
let prev = crates.insert(krate.clone(), crate_id);
assert!(prev.is_none());
@ -167,6 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
} else {
for (from, to) in crate_deps {

View File

@ -10,6 +10,7 @@ use std::{
fmt, ops,
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};
use ra_cfg::CfgOptions;
@ -19,6 +20,7 @@ use rustc_hash::FxHashSet;
use crate::{RelativePath, RelativePathBuf};
use fmt::Display;
use ra_tt::TokenExpander;
/// `FileId` is an integer which uniquely identifies a file. File paths are
/// messy and system-dependent, so most of the code should work directly with
@ -115,6 +117,22 @@ impl Display for CrateName {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ProcMacroId(pub u32);
#[derive(Debug, Clone)]
pub struct ProcMacro {
pub name: SmolStr,
pub expander: Arc<dyn TokenExpander>,
}
impl Eq for ProcMacro {}
impl PartialEq for ProcMacro {
fn eq(&self, other: &ProcMacro) -> bool {
self.name == other.name && Arc::ptr_eq(&self.expander, &other.expander)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CrateData {
pub root_file_id: FileId,
@ -127,6 +145,7 @@ pub struct CrateData {
pub env: Env,
pub extern_source: ExternSource,
pub dependencies: Vec<Dependency>,
pub proc_macro: Vec<ProcMacro>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -166,7 +185,11 @@ impl CrateGraph {
cfg_options: CfgOptions,
env: Env,
extern_source: ExternSource,
proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>,
) -> CrateId {
let proc_macro =
proc_macro.into_iter().map(|(name, it)| ProcMacro { name, expander: it }).collect();
let data = CrateData {
root_file_id: file_id,
edition,
@ -174,6 +197,7 @@ impl CrateGraph {
cfg_options,
env,
extern_source,
proc_macro,
dependencies: Vec::new(),
};
let crate_id = CrateId(self.arena.len() as u32);
@ -345,6 +369,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@ -353,6 +378,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let crate3 = graph.add_crate_root(
FileId(3u32),
@ -361,6 +387,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@ -377,6 +404,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@ -385,6 +413,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let crate3 = graph.add_crate_root(
FileId(3u32),
@ -393,6 +422,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@ -408,6 +438,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@ -416,6 +447,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
assert!(graph
.add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)

View File

@ -12,7 +12,7 @@ pub use crate::{
cancellation::Canceled,
input::{
CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId,
FileId, SourceRoot, SourceRootId,
FileId, ProcMacroId, SourceRoot, SourceRootId,
},
};
pub use relative_path::{RelativePath, RelativePathBuf};

View File

@ -11,7 +11,7 @@ use hir_expand::{
HirFileId, MacroCallId, MacroDefId, MacroDefKind,
};
use ra_cfg::CfgOptions;
use ra_db::{CrateId, FileId};
use ra_db::{CrateId, FileId, ProcMacroId};
use ra_syntax::ast;
use rustc_hash::FxHashMap;
use test_utils::tested_by;
@ -53,6 +53,16 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
}
let cfg_options = &crate_graph[def_map.krate].cfg_options;
let proc_macros = &crate_graph[def_map.krate].proc_macro;
let proc_macros = proc_macros
.iter()
.enumerate()
.map(|(idx, it)| {
// FIXME: a hacky way to create a Name from string.
let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() };
(name.as_name(), ProcMacroExpander::new(def_map.krate, ProcMacroId(idx as u32)))
})
.collect();
let mut collector = DefCollector {
db,
@ -65,9 +75,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
unexpanded_attribute_macros: Vec::new(),
mod_dirs: FxHashMap::default(),
cfg_options,
// FIXME: pass proc-macro from crate-graph
proc_macros: Default::default(),
proc_macros,
};
collector.collect();
collector.finish()

View File

@ -1,33 +1,32 @@
//! Proc Macro Expander stub
use crate::{db::AstDatabase, LazyMacroId, MacroCallKind, MacroCallLoc};
use ra_db::CrateId;
use crate::{db::AstDatabase, LazyMacroId};
use ra_db::{CrateId, ProcMacroId};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander {
krate: CrateId,
proc_macro_id: ProcMacroId,
}
impl ProcMacroExpander {
pub fn new(krate: CrateId) -> ProcMacroExpander {
ProcMacroExpander { krate }
pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
ProcMacroExpander { krate, proc_macro_id }
}
pub fn expand(
&self,
db: &dyn AstDatabase,
id: LazyMacroId,
_tt: &tt::Subtree,
_id: LazyMacroId,
tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
let loc: MacroCallLoc = db.lookup_intern_macro(id);
let name = match loc.kind {
MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError),
MacroCallKind::Attr(_, name) => name,
};
let krate_graph = db.crate_graph();
let proc_macro = krate_graph[self.krate]
.proc_macro
.get(self.proc_macro_id.0 as usize)
.clone()
.ok_or_else(|| mbe::ExpandError::ConversionError)?;
log::debug!("Proc-macro-expanding name = {}", name);
// Return nothing for now
return Ok(tt::Subtree::default());
proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
}
}

View File

@ -213,6 +213,7 @@ impl Analysis {
cfg_options,
Env::default(),
Default::default(),
Default::default(),
);
change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text));
change.set_crate_graph(crate_graph);

View File

@ -103,6 +103,7 @@ impl MockAnalysis {
cfg_options,
Env::default(),
Default::default(),
Default::default(),
));
} else if path.ends_with("/lib.rs") {
let crate_name = path.parent().unwrap().file_name().unwrap();
@ -113,6 +114,7 @@ impl MockAnalysis {
cfg_options,
Env::default(),
Default::default(),
Default::default(),
);
if let Some(root_crate) = root_crate {
crate_graph

View File

@ -137,6 +137,7 @@ mod tests {
CfgOptions::default(),
Env::default(),
Default::default(),
Default::default(),
);
let mut change = AnalysisChange::new();
change.set_crate_graph(crate_graph);

View File

@ -28,6 +28,13 @@ pub enum ExpandError {
BindingError(String),
ConversionError,
InvalidRepeat,
ProcMacroError(tt::ExpansionError),
}
impl From<tt::ExpansionError> for ExpandError {
fn from(it: tt::ExpansionError) -> Self {
ExpandError::ProcMacroError(it)
}
}
pub use crate::syntax_bridge::{

View File

@ -0,0 +1,12 @@
[package]
edition = "2018"
name = "ra_proc_macro"
version = "0.1.0"
authors = ["rust-analyzer developers"]
publish = false
[lib]
doctest = false
[dependencies]
ra_tt = { path = "../ra_tt" }

View File

@ -0,0 +1,59 @@
//! Client-side Proc-Macro crate
//!
//! We separate proc-macro expanding logic to an extern program to allow
//! different implementations (e.g. wasm or dylib loading). And this crate
//! is used to provide basic infrastructure for communication between two
//! processes: Client (RA itself), Server (the external program)
use ra_tt::{SmolStr, Subtree};
use std::{
path::{Path, PathBuf},
sync::Arc,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcMacroProcessExpander {
process: Arc<ProcMacroProcessSrv>,
name: SmolStr,
}
impl ra_tt::TokenExpander for ProcMacroProcessExpander {
fn expand(
&self,
_subtree: &Subtree,
_attr: Option<&Subtree>,
) -> Result<Subtree, ra_tt::ExpansionError> {
// FIXME: do nothing for now
Ok(Subtree::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProcMacroProcessSrv {
path: PathBuf,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProcMacroClient {
Process { process: Arc<ProcMacroProcessSrv> },
Dummy,
}
impl ProcMacroClient {
pub fn extern_process(process_path: &Path) -> ProcMacroClient {
let process = ProcMacroProcessSrv { path: process_path.into() };
ProcMacroClient::Process { process: Arc::new(process) }
}
pub fn dummy() -> ProcMacroClient {
ProcMacroClient::Dummy
}
pub fn by_dylib_path(
&self,
_dylib_path: &Path,
) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
// FIXME: return empty for now
vec![]
}
}

View File

@ -17,6 +17,7 @@ ra_arena = { path = "../ra_arena" }
ra_db = { path = "../ra_db" }
ra_cfg = { path = "../ra_cfg" }
ra_cargo_watch = { path = "../ra_cargo_watch" }
ra_proc_macro = { path = "../ra_proc_macro" }
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.48"

View File

@ -83,6 +83,7 @@ pub struct PackageData {
pub edition: Edition,
pub features: Vec<String>,
pub out_dir: Option<PathBuf>,
pub proc_macro_dylib_path: Option<PathBuf>,
}
#[derive(Debug, Clone)]
@ -158,8 +159,11 @@ impl CargoWorkspace {
})?;
let mut out_dir_by_id = FxHashMap::default();
let mut proc_macro_dylib_paths = FxHashMap::default();
if cargo_features.load_out_dirs_from_check {
out_dir_by_id = load_out_dirs(cargo_toml, cargo_features);
let resources = load_extern_resources(cargo_toml, cargo_features);
out_dir_by_id = resources.out_dirs;
proc_macro_dylib_paths = resources.proc_dylib_paths;
}
let mut pkg_by_id = FxHashMap::default();
@ -183,6 +187,7 @@ impl CargoWorkspace {
dependencies: Vec::new(),
features: Vec::new(),
out_dir: out_dir_by_id.get(&id).cloned(),
proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
});
let pkg_data = &mut packages[pkg];
pkg_by_id.insert(id, pkg);
@ -246,10 +251,13 @@ impl CargoWorkspace {
}
}
pub fn load_out_dirs(
cargo_toml: &Path,
cargo_features: &CargoFeatures,
) -> FxHashMap<PackageId, PathBuf> {
#[derive(Debug, Clone, Default)]
pub struct ExternResources {
out_dirs: FxHashMap<PackageId, PathBuf>,
proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
}
pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources {
let mut args: Vec<String> = vec![
"check".to_string(),
"--message-format=json".to_string(),
@ -267,14 +275,21 @@ pub fn load_out_dirs(
args.extend(cargo_features.features.iter().cloned());
}
let mut acc = FxHashMap::default();
let mut acc = ExternResources::default();
let res = run_cargo(&args, cargo_toml.parent(), &mut |message| {
match message {
Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
acc.insert(package_id, out_dir);
acc.out_dirs.insert(package_id, out_dir);
}
Message::CompilerArtifact(_) => (),
Message::CompilerArtifact(message) => {
if message.target.kind.contains(&"proc-macro".to_string()) {
let package_id = message.package_id;
if let Some(filename) = message.filenames.get(0) {
acc.proc_dylib_paths.insert(package_id, filename.clone());
}
}
}
Message::CompilerMessage(_) => (),
Message::Unknown => (),
}

View File

@ -23,6 +23,7 @@ pub struct Crate {
pub(crate) atom_cfgs: FxHashSet<String>,
pub(crate) key_value_cfgs: FxHashMap<String, String>,
pub(crate) out_dir: Option<PathBuf>,
pub(crate) proc_macro_dylib_path: Option<PathBuf>,
}
#[derive(Clone, Copy, Debug, Deserialize)]

View File

@ -23,6 +23,7 @@ pub use crate::{
json_project::JsonProject,
sysroot::Sysroot,
};
pub use ra_proc_macro::ProcMacroClient;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct CargoTomlNotFoundError {
@ -173,6 +174,29 @@ impl ProjectWorkspace {
}
}
pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
match self {
ProjectWorkspace::Json { project } => {
let mut proc_macro_dylib_paths = Vec::with_capacity(project.crates.len());
for krate in &project.crates {
if let Some(out_dir) = &krate.proc_macro_dylib_path {
proc_macro_dylib_paths.push(out_dir.to_path_buf());
}
}
proc_macro_dylib_paths
}
ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => {
let mut proc_macro_dylib_paths = Vec::with_capacity(cargo.packages().len());
for pkg in cargo.packages() {
if let Some(dylib_path) = &cargo[pkg].proc_macro_dylib_path {
proc_macro_dylib_paths.push(dylib_path.to_path_buf());
}
}
proc_macro_dylib_paths
}
}
}
pub fn n_packages(&self) -> usize {
match self {
ProjectWorkspace::Json { project } => project.crates.len(),
@ -186,6 +210,7 @@ impl ProjectWorkspace {
&self,
default_cfg_options: &CfgOptions,
extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
proc_macro_client: &ProcMacroClient,
load: &mut dyn FnMut(&Path) -> Option<FileId>,
) -> CrateGraph {
let mut crate_graph = CrateGraph::default();
@ -219,7 +244,10 @@ impl ProjectWorkspace {
extern_source.set_extern_path(&out_dir, extern_source_id);
}
}
let proc_macro = krate
.proc_macro_dylib_path
.clone()
.map(|it| proc_macro_client.by_dylib_path(&it));
// FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
crates.insert(
crate_id,
@ -231,6 +259,7 @@ impl ProjectWorkspace {
cfg_options,
env,
extern_source,
proc_macro.unwrap_or_default(),
),
);
}
@ -270,6 +299,8 @@ impl ProjectWorkspace {
let env = Env::default();
let extern_source = ExternSource::default();
let proc_macro = vec![];
let crate_id = crate_graph.add_crate_root(
file_id,
Edition::Edition2018,
@ -280,6 +311,7 @@ impl ProjectWorkspace {
cfg_options,
env,
extern_source,
proc_macro,
);
sysroot_crates.insert(krate, crate_id);
}
@ -327,6 +359,12 @@ impl ProjectWorkspace {
extern_source.set_extern_path(&out_dir, extern_source_id);
}
}
let proc_macro = cargo[pkg]
.proc_macro_dylib_path
.as_ref()
.map(|it| proc_macro_client.by_dylib_path(&it))
.unwrap_or_default();
let crate_id = crate_graph.add_crate_root(
file_id,
edition,
@ -334,6 +372,7 @@ impl ProjectWorkspace {
cfg_options,
env,
extern_source,
proc_macro.clone(),
);
if cargo[tgt].kind == TargetKind::Lib {
lib_tgt = Some((crate_id, cargo[tgt].name.clone()));

View File

@ -14,9 +14,12 @@ macro_rules! impl_froms {
}
}
use std::fmt;
use std::{
fmt::{self, Debug},
panic::RefUnwindSafe,
};
use smol_str::SmolStr;
pub use smol_str::SmolStr;
/// Represents identity of the token.
///
@ -184,3 +187,11 @@ impl Subtree {
}
pub mod buffer;
#[derive(Debug, PartialEq, Eq)]
pub enum ExpansionError {}
pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
-> Result<Subtree, ExpansionError>;
}

View File

@ -7,7 +7,9 @@ use anyhow::Result;
use crossbeam_channel::{unbounded, Receiver};
use ra_db::{ExternSourceId, FileId, SourceRootId};
use ra_ide::{AnalysisChange, AnalysisHost};
use ra_project_model::{get_rustc_cfg_options, CargoFeatures, PackageRoot, ProjectWorkspace};
use ra_project_model::{
get_rustc_cfg_options, CargoFeatures, PackageRoot, ProcMacroClient, ProjectWorkspace,
};
use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
use rustc_hash::{FxHashMap, FxHashSet};
@ -67,7 +69,9 @@ pub(crate) fn load_cargo(
(source_root_id, project_root)
})
.collect::<FxHashMap<_, _>>();
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs);
let proc_macro_client = ProcMacroClient::dummy();
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
Ok((host, source_roots))
}
@ -77,6 +81,7 @@ pub(crate) fn load(
vfs: &mut Vfs,
receiver: Receiver<VfsTask>,
extern_dirs: FxHashSet<PathBuf>,
proc_macro_client: &ProcMacroClient,
) -> AnalysisHost {
let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
let mut host = AnalysisHost::new(lru_cap);
@ -143,12 +148,16 @@ pub(crate) fn load(
opts
};
let crate_graph =
ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut |path: &Path| {
let crate_graph = ws.to_crate_graph(
&default_cfg_options,
&extern_source_roots,
proc_macro_client,
&mut |path: &Path| {
let vfs_file = vfs.load(path);
log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
vfs_file.map(vfs_file_to_id)
});
},
);
log::debug!("crate graph: {:?}", crate_graph);
analysis_change.set_crate_graph(crate_graph);

View File

@ -16,7 +16,7 @@ use ra_ide::{
Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData,
SourceRootId,
};
use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
use relative_path::RelativePathBuf;
@ -150,9 +150,19 @@ impl WorldState {
vfs_file.map(|f| FileId(f.0))
};
let proc_macro_client =
ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv"));
workspaces
.iter()
.map(|ws| ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut load))
.map(|ws| {
ws.to_crate_graph(
&default_cfg_options,
&extern_source_roots,
&proc_macro_client,
&mut load,
)
})
.for_each(|graph| {
crate_graph.extend(graph);
});