From add87f54240a20e72136a403c166113af5173572 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 18 Jan 2021 14:52:12 +0300 Subject: [PATCH] Avoid blocking the main loop when editing Cargo.toml I've noticed a bunch of "main loop too long" warnings in console when typing in Cargo.toml. Profiling showed that the culprit is `rustc --print cfg` call. I moved it to the background project loading phase, where it belongs. This highlighted a problem: we generally use single `cfg`, while it really should be per crate. --- crates/project_model/src/lib.rs | 1 + crates/project_model/src/rustc_cfg.rs | 34 ++++++ crates/project_model/src/workspace.rs | 119 ++++++++++----------- crates/rust-analyzer/src/cli/load_cargo.rs | 2 +- crates/rust-analyzer/src/reload.rs | 15 ++- 5 files changed, 100 insertions(+), 71 deletions(-) create mode 100644 crates/project_model/src/rustc_cfg.rs diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index aabb7a47df..970a7e1402 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -5,6 +5,7 @@ mod cfg_flag; mod project_json; mod sysroot; mod workspace; +mod rustc_cfg; use std::{ fs::{read_dir, ReadDir}, diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs new file mode 100644 index 0000000000..4a7bd8ae35 --- /dev/null +++ b/crates/project_model/src/rustc_cfg.rs @@ -0,0 +1,34 @@ +//! Runs `rustc --print cfg` to get built-in cfg flags. + +use std::process::Command; + +use crate::{cfg_flag::CfgFlag, utf8_stdout}; + +pub(crate) fn get(target: Option<&str>) -> Vec { + let _p = profile::span("rustc_cfg::get"); + let mut res = Vec::new(); + + // Some nightly-only cfgs, which are required for stdlib + res.push(CfgFlag::Atom("target_thread_local".into())); + for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { + for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { + res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); + } + } + + let rustc_cfgs = { + let mut cmd = Command::new(toolchain::rustc()); + cmd.args(&["--print", "cfg", "-O"]); + if let Some(target) = target { + cmd.args(&["--target", target]); + } + utf8_stdout(cmd) + }; + + match rustc_cfgs { + Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), + Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), + } + + res +} diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index bd990594b0..8e0481ae97 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs @@ -16,7 +16,7 @@ use proc_macro_api::ProcMacroClient; use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig, + cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, }; @@ -34,15 +34,25 @@ pub struct PackageRoot { #[derive(Clone, Eq, PartialEq)] pub enum ProjectWorkspace { /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. - Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option }, + Cargo { + cargo: CargoWorkspace, + sysroot: Sysroot, + rustc: Option, + /// Holds cfg flags for the current target. We get those by running + /// `rustc --print cfg`. + /// + /// FIXME: make this a per-crate map, as, eg, build.rs might have a + /// different target. + rustc_cfg: Vec, + }, /// Project workspace was manually specified using a `rust-project.json` file. - Json { project: ProjectJson, sysroot: Option }, + Json { project: ProjectJson, sysroot: Option, rustc_cfg: Vec }, } impl fmt::Debug for ProjectWorkspace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f + ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f .debug_struct("Cargo") .field("n_packages", &cargo.packages().len()) .field("n_sysroot_crates", &sysroot.crates().len()) @@ -50,13 +60,15 @@ impl fmt::Debug for ProjectWorkspace { "n_rustc_compiler_crates", &rustc.as_ref().map_or(0, |rc| rc.packages().len()), ) + .field("rustc_cfg", rustc_cfg) .finish(), - ProjectWorkspace::Json { project, sysroot } => { + ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); if let Some(sysroot) = sysroot { debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); } + debug_struct.field("rustc_cfg", rustc_cfg); debug_struct.finish() } } @@ -79,7 +91,7 @@ impl ProjectWorkspace { })?; let project_location = project_json.parent().unwrap().to_path_buf(); let project_json = ProjectJson::new(&project_location, data); - ProjectWorkspace::load_inline(project_json)? + ProjectWorkspace::load_inline(project_json, config.target.as_deref())? } ProjectManifest::CargoToml(cargo_toml) => { let cargo_version = utf8_stdout({ @@ -117,21 +129,24 @@ impl ProjectWorkspace { } else { None }; - - ProjectWorkspace::Cargo { cargo, sysroot, rustc } + let rustc_cfg = rustc_cfg::get(config.target.as_deref()); + ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } } }; Ok(res) } - pub fn load_inline(project_json: ProjectJson) -> Result { + pub fn load_inline( + project_json: ProjectJson, + target: Option<&str>, + ) -> Result { let sysroot = match &project_json.sysroot_src { Some(path) => Some(Sysroot::load(path)?), None => None, }; - - Ok(ProjectWorkspace::Json { project: project_json, sysroot }) + let rustc_cfg = rustc_cfg::get(target); + Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) } /// Returns the roots for the current `ProjectWorkspace` @@ -139,7 +154,7 @@ impl ProjectWorkspace { /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { match self { - ProjectWorkspace::Json { project, sysroot } => project + ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project .crates() .map(|(_, krate)| PackageRoot { is_member: krate.is_workspace_member, @@ -156,7 +171,7 @@ impl ProjectWorkspace { }) })) .collect::>(), - ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo + ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo .packages() .map(|pkg| { let is_member = cargo[pkg].is_member; @@ -194,7 +209,7 @@ impl ProjectWorkspace { pub fn n_packages(&self) -> usize { match self { ProjectWorkspace::Json { project, .. } => project.n_crates(), - ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { + ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => { let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); cargo.packages().len() + sysroot.crates().len() + rustc_package_len } @@ -203,7 +218,6 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, - target: Option<&str>, proc_macro_client: Option<&ProcMacroClient>, load: &mut dyn FnMut(&AbsPath) -> Option, ) -> CrateGraph { @@ -214,12 +228,21 @@ impl ProjectWorkspace { }; let mut crate_graph = match self { - ProjectWorkspace::Json { project, sysroot } => { - project_json_to_crate_graph(target, &proc_macro_loader, load, project, sysroot) - } - ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { - cargo_to_crate_graph(target, &proc_macro_loader, load, cargo, sysroot, rustc) - } + ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph( + rustc_cfg.clone(), + &proc_macro_loader, + load, + project, + sysroot, + ), + ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph( + rustc_cfg.clone(), + &proc_macro_loader, + load, + cargo, + sysroot, + rustc, + ), }; if crate_graph.patch_cfg_if() { log::debug!("Patched std to depend on cfg-if") @@ -231,7 +254,7 @@ impl ProjectWorkspace { } fn project_json_to_crate_graph( - target: Option<&str>, + rustc_cfg: Vec, proc_macro_loader: &dyn Fn(&Path) -> Vec, load: &mut dyn FnMut(&AbsPath) -> Option, project: &ProjectJson, @@ -240,9 +263,9 @@ fn project_json_to_crate_graph( let mut crate_graph = CrateGraph::default(); let sysroot_deps = sysroot .as_ref() - .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); + .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load)); - let mut cfg_cache: FxHashMap, Vec> = FxHashMap::default(); + let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); let crates: FxHashMap = project .crates() .filter_map(|(crate_id, krate)| { @@ -254,9 +277,12 @@ fn project_json_to_crate_graph( let env = krate.env.clone().into_iter().collect(); let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it)); - let target = krate.target.as_deref().or(target); - let target_cfgs = - cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); + let target_cfgs = match krate.target.as_deref() { + Some(target) => { + cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target))) + } + None => &rustc_cfg, + }; let mut cfg_options = CfgOptions::default(); cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); @@ -293,7 +319,7 @@ fn project_json_to_crate_graph( } fn cargo_to_crate_graph( - target: Option<&str>, + rustc_cfg: Vec, proc_macro_loader: &dyn Fn(&Path) -> Vec, load: &mut dyn FnMut(&AbsPath) -> Option, cargo: &CargoWorkspace, @@ -303,10 +329,10 @@ fn cargo_to_crate_graph( let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, libproc_macro) = - sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); + sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load); let mut cfg_options = CfgOptions::default(); - cfg_options.extend(get_rustc_cfg_options(target)); + cfg_options.extend(rustc_cfg); let mut pkg_to_lib_crate = FxHashMap::default(); @@ -492,12 +518,12 @@ fn add_target_crate_root( fn sysroot_to_crate_graph( crate_graph: &mut CrateGraph, sysroot: &Sysroot, - target: Option<&str>, + rustc_cfg: Vec, load: &mut dyn FnMut(&AbsPath) -> Option, ) -> (Vec<(CrateName, CrateId)>, Option) { let _p = profile::span("sysroot_to_crate_graph"); let mut cfg_options = CfgOptions::default(); - cfg_options.extend(get_rustc_cfg_options(target)); + cfg_options.extend(rustc_cfg); let sysroot_crates: FxHashMap = sysroot .crates() .filter_map(|krate| { @@ -536,35 +562,6 @@ fn sysroot_to_crate_graph( (public_deps, libproc_macro) } -fn get_rustc_cfg_options(target: Option<&str>) -> Vec { - let _p = profile::span("get_rustc_cfg_options"); - let mut res = Vec::new(); - - // Some nightly-only cfgs, which are required for stdlib - res.push(CfgFlag::Atom("target_thread_local".into())); - for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { - for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { - res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); - } - } - - let rustc_cfgs = { - let mut cmd = Command::new(toolchain::rustc()); - cmd.args(&["--print", "cfg", "-O"]); - if let Some(target) = target { - cmd.args(&["--target", target]); - } - utf8_stdout(cmd) - }; - - match rustc_cfgs { - Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), - Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), - } - - res -} - fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { if let Err(err) = graph.add_dep(from, name, to) { log::error!("{}", err) diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 31a16ca464..16ccab7817 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -39,7 +39,7 @@ pub fn load_cargo( None }; - let crate_graph = ws.to_crate_graph(None, proc_macro_client.as_ref(), &mut |path: &AbsPath| { + let crate_graph = ws.to_crate_graph(proc_macro_client.as_ref(), &mut |path: &AbsPath| { let contents = loader.load_sync(path); let path = vfs::VfsPath::from(path.to_path_buf()); vfs.set_file_contents(path.clone(), contents); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 34e3cfe682..dabfb42415 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -135,7 +135,10 @@ impl GlobalState { ) } LinkedProject::InlineJsonProject(it) => { - project_model::ProjectWorkspace::load_inline(it.clone()) + project_model::ProjectWorkspace::load_inline( + it.clone(), + cargo_config.target.as_deref(), + ) } }) .collect::>(); @@ -253,11 +256,7 @@ impl GlobalState { res }; for ws in workspaces.iter() { - crate_graph.extend(ws.to_crate_graph( - self.config.cargo().target.as_deref(), - self.proc_macro_client.as_ref(), - &mut load, - )); + crate_graph.extend(ws.to_crate_graph(self.proc_macro_client.as_ref(), &mut load)); } crate_graph @@ -289,9 +288,7 @@ impl GlobalState { .iter() .enumerate() .filter_map(|(id, w)| match w { - ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => { - Some((id, cargo.workspace_root())) - } + ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())), ProjectWorkspace::Json { project, .. } => { // Enable flychecks for json projects if a custom flycheck command was supplied // in the workspace configuration.