From d0b6ed4441469acfb6bc6555d78abf12637b6cf4 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Wed, 18 Mar 2020 20:56:46 +0800 Subject: Add ProcMacroClient --- crates/ra_db/Cargo.toml | 1 + crates/ra_db/src/fixture.rs | 4 ++ crates/ra_db/src/input.rs | 15 +++++ crates/ra_db/src/lib.rs | 3 +- crates/ra_hir_def/src/nameres/collector.rs | 18 ++++-- crates/ra_hir_expand/src/proc_macro.rs | 30 +++++----- crates/ra_ide/src/lib.rs | 1 + crates/ra_ide/src/mock_analysis.rs | 2 + crates/ra_ide/src/parent_module.rs | 1 + crates/ra_proc_macro/src/lib.rs | 81 ++++++++++++++++++++++++-- crates/ra_project_model/Cargo.toml | 1 + crates/ra_project_model/src/cargo_workspace.rs | 31 +++++++--- crates/ra_project_model/src/json_project.rs | 1 + crates/ra_project_model/src/lib.rs | 41 ++++++++++++- crates/rust-analyzer/src/cli/load_cargo.rs | 19 ++++-- crates/rust-analyzer/src/world.rs | 14 ++++- 16 files changed, 220 insertions(+), 43 deletions(-) (limited to 'crates') diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 878c22ba9..82fd842a6 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -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_proc_macro = { path = "../ra_proc_macro" } test_utils = { path = "../test_utils" } diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 3464f43df..9d992886e 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -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 Option, + pub proc_macro: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -166,6 +171,7 @@ impl CrateGraph { cfg_options: CfgOptions, env: Env, extern_source: ExternSource, + proc_macro: Vec, ) -> CrateId { let data = CrateData { root_file_id: file_id, @@ -174,6 +180,7 @@ impl CrateGraph { cfg_options, env, extern_source, + proc_macro, dependencies: Vec::new(), }; let crate_id = CrateId(self.arena.len() as u32); @@ -345,6 +352,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -353,6 +361,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -361,6 +370,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 +387,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -385,6 +396,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate3 = graph.add_crate_root( FileId(3u32), @@ -393,6 +405,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 +421,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); let crate2 = graph.add_crate_root( FileId(2u32), @@ -416,6 +430,7 @@ mod tests { CfgOptions::default(), Env::default(), Default::default(), + Default::default(), ); assert!(graph .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index bac24e218..5829ae465 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -12,9 +12,10 @@ pub use crate::{ cancellation::Canceled, input::{ CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, - FileId, SourceRoot, SourceRootId, + FileId, ProcMacroId, SourceRoot, SourceRootId, }, }; +pub use ra_proc_macro::ProcMacro; pub use relative_path::{RelativePath, RelativePathBuf}; pub use salsa; diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 9c125f32f..9b46431cb 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -11,8 +11,8 @@ use hir_expand::{ HirFileId, MacroCallId, MacroDefId, MacroDefKind, }; use ra_cfg::CfgOptions; -use ra_db::{CrateId, FileId}; -use ra_syntax::ast; +use ra_db::{CrateId, FileId, ProcMacroId}; +use ra_syntax::{ast, SmolStr}; 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: SmolStr::new(&it.name()), id: tt::TokenId::unspecified() }; + (name.as_name(), ProcMacroExpander::new(def_map.krate, ProcMacroId(idx))) + }) + .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() diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index a8dee2052..296325b05 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs @@ -1,33 +1,31 @@ //! 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 { - let loc: MacroCallLoc = db.lookup_intern_macro(id); - let name = match loc.kind { - MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError), - MacroCallKind::Attr(_, name) => name, - }; - - log::debug!("Proc-macro-expanding name = {}", name); - - // Return nothing for now - return Ok(tt::Subtree::default()); + let krate_graph = db.crate_graph(); + let proc_macro = krate_graph[self.krate] + .proc_macro + .get(self.proc_macro_id.0) + .clone() + .ok_or_else(|| mbe::ExpandError::ConversionError)?; + proc_macro.custom_derive(tt) } } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5ab06c6cf..e43414985 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -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); diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 2cf77a31f..2c13f206a 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -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 diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 76d130b9b..958b92bed 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -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); diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs index 31e1bb209..a7a84249f 100644 --- a/crates/ra_proc_macro/src/lib.rs +++ b/crates/ra_proc_macro/src/lib.rs @@ -1,7 +1,78 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +//! 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 for provide basic infra-structure for commnicate between two +//! process: Client (RA itself), Server (the external program) + +use ra_mbe::ExpandError; +use ra_tt::Subtree; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; + +trait ProcMacroExpander: std::fmt::Debug + Send + Sync + std::panic::RefUnwindSafe { + fn custom_derive(&self, subtree: &Subtree, derive_name: &str) -> Result; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProcMacroProcessExpander { + process_path: PathBuf, +} + +impl ProcMacroExpander for ProcMacroProcessExpander { + fn custom_derive( + &self, + _subtree: &Subtree, + _derive_name: &str, + ) -> Result { + // FIXME: do nothing for now + Ok(Subtree::default()) + } +} + +#[derive(Debug, Clone)] +pub struct ProcMacro { + expander: Arc>, + name: String, +} + +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) + } +} + +impl ProcMacro { + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn custom_derive(&self, subtree: &Subtree) -> Result { + self.expander.custom_derive(subtree, &self.name) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProcMacroClient { + Process { expander: Arc }, + Dummy, +} + +impl ProcMacroClient { + pub fn extern_process(process_path: &Path) -> ProcMacroClient { + let expander = ProcMacroProcessExpander { process_path: process_path.into() }; + ProcMacroClient::Process { expander: Arc::new(expander) } + } + + pub fn dummy() -> ProcMacroClient { + ProcMacroClient::Dummy + } + + pub fn by_dylib_path(&self, _dylib_path: &Path) -> Vec { + // FIXME: return empty for now + vec![] } } diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 22300548a..cdcdd63c9 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -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" diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index c7f9bd873..291594e2a 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -83,6 +83,7 @@ pub struct PackageData { pub edition: Edition, pub features: Vec, pub out_dir: Option, + pub proc_macro_dylib_path: Option, } #[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 { +#[derive(Debug, Clone, Default)] +pub struct ExternResources { + out_dirs: FxHashMap, + proc_dylib_paths: FxHashMap, +} + +pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { let mut args: Vec = 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 => (), } diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 336446e58..b030c8a6a 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -23,6 +23,7 @@ pub struct Crate { pub(crate) atom_cfgs: FxHashSet, pub(crate) key_value_cfgs: FxHashMap, pub(crate) out_dir: Option, + pub(crate) proc_macro_dylib_path: Option, } #[derive(Clone, Copy, Debug, Deserialize)] diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a3ef9acdc..444d3bb3f 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -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 { + 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, + proc_macro_client: &ProcMacroClient, load: &mut dyn FnMut(&Path) -> Option, ) -> 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())); diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 54e2fa1a7..832f04226 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -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::>(); - 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, extern_dirs: FxHashSet, + proc_macro_client: &ProcMacroClient, ) -> AnalysisHost { let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().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); diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index c4244fee2..de85bb017 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs @@ -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); }); -- cgit v1.2.3