diff --git a/clippy.toml b/clippy.toml index 8032c775ab0a..1046cb3d56bc 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,3 +3,7 @@ disallowed-types = [ { path = "std::collections::HashSet", reason = "use FxHashSet" }, { path = "std::collections::hash_map::RandomState", reason = "use BuildHasherDefault"} ] + +disallowed-methods = [ + { path = "std::process::Command::new", reason = "use `toolchain::command` instead as it forces the choice of a working directory" }, +] diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index f40d508f755a..2649d88ac2c2 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; -use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot}; +use project_model::{toolchain_info::QueryConfig, Sysroot}; use rustc_hash::FxHashMap; use syntax::ToSmolStr; use test_fixture::WithFixture; @@ -17,8 +17,8 @@ use crate::{ mod closure; fn current_machine_data_layout() -> String { - project_model::target_data_layout::get( - RustcDataLayoutConfig::Rustc(&Sysroot::empty()), + project_model::toolchain_info::target_data_layout::get( + QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), ) diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 10a73edd51c5..f642db6a71ef 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -253,6 +253,7 @@ fn _format( let &crate_id = db.relevant_crates(file_id).iter().next()?; let edition = db.crate_graph()[crate_id].edition; + #[allow(clippy::disallowed_methods)] let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path()); cmd.arg("--edition"); cmd.arg(edition.to_string()); diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 4045e25fdf11..4d62efdd6b19 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -202,6 +202,7 @@ fn mk_child( env: impl IntoIterator, impl AsRef)>, null_stderr: bool, ) -> io::Result { + #[allow(clippy::disallowed_methods)] let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs index 9a17cfc9f360..07a10aaae578 100644 --- a/crates/proc-macro-srv/build.rs +++ b/crates/proc-macro-srv/build.rs @@ -7,6 +7,7 @@ fn main() { println!("cargo::rustc-check-cfg=cfg(rust_analyzer)"); let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set"); + #[allow(clippy::disallowed_methods)] let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run"); let version_string = std::str::from_utf8(&output.stdout[..]) .expect("rustc --version output must be UTF-8") diff --git a/crates/proc-macro-srv/proc-macro-test/build.rs b/crates/proc-macro-srv/proc-macro-test/build.rs index ff2f5d186391..d3d58a6df011 100644 --- a/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/crates/proc-macro-srv/proc-macro-test/build.rs @@ -7,6 +7,8 @@ //! a specific rustup toolchain: this allows testing against older ABIs (e.g. //! 1.58) and future ABIs (stage1, nightly) +#![allow(clippy::disallowed_methods)] + use std::{ env, path::{Path, PathBuf}, diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs index 524323b97363..b0939229f93e 100644 --- a/crates/project-model/src/build_dependencies.rs +++ b/crates/project-model/src/build_dependencies.rs @@ -172,19 +172,18 @@ impl WorkspaceBuildScripts { } let res = (|| { let target_libdir = (|| { - let mut cargo_config = sysroot.tool(Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo, current_dir); cargo_config.envs(extra_env); cargo_config - .current_dir(current_dir) .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) .env("RUSTC_BOOTSTRAP", "1"); - if let Ok(it) = utf8_stdout(cargo_config) { + if let Ok(it) = utf8_stdout(&mut cargo_config) { return Ok(it); } - let mut cmd = sysroot.tool(Tool::Rustc); + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); cmd.envs(extra_env); cmd.args(["--print", "target-libdir"]); - utf8_stdout(cmd) + utf8_stdout(&mut cmd) })()?; let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir)) @@ -390,12 +389,12 @@ impl WorkspaceBuildScripts { ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { - let mut cmd = Command::new(program); + let mut cmd = toolchain::command(program, current_dir); cmd.args(args); cmd } _ => { - let mut cmd = sysroot.tool(Tool::Cargo); + let mut cmd = sysroot.tool(Tool::Cargo, current_dir); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&config.extra_args); @@ -448,7 +447,6 @@ impl WorkspaceBuildScripts { } }; - cmd.current_dir(current_dir); cmd.envs(&config.extra_env); if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 836879c75bff..4ffe3a6265cc 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -294,7 +294,7 @@ impl CargoWorkspace { no_deps: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option)> { - let cargo = sysroot.tool(Tool::Cargo); + let cargo = sysroot.tool(Tool::Cargo, current_dir); let mut meta = MetadataCommand::new(); meta.cargo_path(cargo.get_program()); cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default())); diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs index ff9d2035f60a..b4714b764afa 100644 --- a/crates/project-model/src/env.rs +++ b/crates/project-model/src/env.rs @@ -74,10 +74,9 @@ pub(crate) fn cargo_config_env( extra_env: &FxHashMap, sysroot: &Sysroot, ) -> FxHashMap { - let mut cargo_config = sysroot.tool(Tool::Cargo); + let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent()); cargo_config.envs(extra_env); cargo_config - .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); if manifest.is_rust_manifest() { @@ -85,7 +84,7 @@ pub(crate) fn cargo_config_env( } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); - utf8_stdout(cargo_config) + utf8_stdout(&mut cargo_config) .map(parse_output_cargo_config_env) .inspect(|env| { tracing::debug!("Discovered cargo config env: {:?}", env); diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 9a024f6b962e..1913db11fa9b 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -15,15 +15,31 @@ //! procedural macros). //! * Lowering of concrete model to a [`base_db::CrateGraph`] +pub mod project_json; +pub mod toolchain_info { + pub mod rustc_cfg; + pub mod target_data_layout; + pub mod target_triple; + + use std::path::Path; + + use crate::{ManifestPath, Sysroot}; + + #[derive(Copy, Clone)] + pub enum QueryConfig<'a> { + /// Directly invoke `rustc` to query the desired information. + Rustc(&'a Sysroot, &'a Path), + /// Attempt to use cargo to query the desired information, honoring cargo configurations. + /// If this fails, falls back to invoking `rustc` directly. + Cargo(&'a Sysroot, &'a ManifestPath), + } +} + mod build_dependencies; mod cargo_workspace; mod env; mod manifest_path; -pub mod project_json; -mod rustc_cfg; mod sysroot; -pub mod target_data_layout; -mod target_triple; mod workspace; #[cfg(test)] @@ -182,7 +198,7 @@ impl fmt::Display for ProjectManifest { } } -fn utf8_stdout(mut cmd: Command) -> anyhow::Result { +fn utf8_stdout(cmd: &mut Command) -> anyhow::Result { let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?; if !output.status.success() { match String::from_utf8(output.stderr) { diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs deleted file mode 100644 index bc1f0e6fbf26..000000000000 --- a/crates/project-model/src/rustc_cfg.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Runs `rustc --print cfg` to get built-in cfg flags. - -use anyhow::Context; -use cfg::CfgAtom; -use intern::Symbol; -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print cfg` is discovered and invoked. -pub(crate) enum RustcCfgConfig<'a> { - /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub(crate) fn get( - target: Option<&str>, - extra_env: &FxHashMap, - config: RustcCfgConfig<'_>, -) -> Vec { - let _p = tracing::info_span!("rustc_cfg::get").entered(); - let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1); - - // Some nightly-only cfgs, which are required for stdlib - res.push(CfgAtom::Flag(Symbol::intern("target_thread_local"))); - for key in ["target_has_atomic", "target_has_atomic_load_store"] { - for ty in ["8", "16", "32", "64", "cas", "ptr"] { - res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) }); - } - res.push(CfgAtom::Flag(Symbol::intern(key))); - } - - let rustc_cfgs = get_rust_cfgs(target, extra_env, config); - - let rustc_cfgs = match rustc_cfgs { - Ok(cfgs) => cfgs, - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); - return res; - } - }; - - let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::, _>>(); - - match rustc_cfgs { - Ok(rustc_cfgs) => { - tracing::debug!(?rustc_cfgs, "rustc cfgs found"); - res.extend(rustc_cfgs); - } - Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs") - } - } - - res -} - -fn get_rust_cfgs( - target: Option<&str>, - extra_env: &FxHashMap, - config: RustcCfgConfig<'_>, -) -> anyhow::Result { - let sysroot = match config { - RustcCfgConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - match utf8_stdout(cmd) { - Ok(it) => return Ok(it), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcCfgConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = sysroot.tool(Tool::Rustc); - cmd.envs(extra_env); - cmd.args(["--print", "cfg", "-O"]); - if let Some(target) = target { - cmd.args(["--target", target]); - } - - utf8_stdout(cmd).context("unable to fetch cfgs via `rustc --print cfg -O`") -} diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index d8186a23f76b..b0fdd3fa413c 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -4,7 +4,7 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, ops, process::Command}; +use std::{env, fs, ops, path::Path, process::Command}; use anyhow::{format_err, Result}; use base_db::CrateName; @@ -170,7 +170,7 @@ impl Sysroot { } /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists. - pub fn tool(&self, tool: Tool) -> Command { + pub fn tool(&self, tool: Tool, current_dir: impl AsRef) -> Command { match self.root() { Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder @@ -179,15 +179,15 @@ impl Sysroot { if let Some(path) = probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) { - return Command::new(path); + return toolchain::command(path, current_dir); } } - let mut cmd = Command::new(tool.prefer_proxy()); + let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir); cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root)); cmd } - _ => Command::new(tool.path()), + _ => toolchain::command(tool.path(), current_dir), } } @@ -436,11 +436,11 @@ fn discover_sysroot_dir( current_dir: &AbsPath, extra_env: &FxHashMap, ) -> Result { - let mut rustc = Command::new(Tool::Rustc.path()); + let mut rustc = toolchain::command(Tool::Rustc.path(), current_dir); rustc.envs(extra_env); rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); - let stdout = utf8_stdout(rustc)?; + let stdout = utf8_stdout(&mut rustc)?; Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } @@ -468,11 +468,11 @@ fn discover_sysroot_src_dir_or_add_component( ) -> Result { discover_sysroot_src_dir(sysroot_path) .or_else(|| { - let mut rustup = Command::new(Tool::Rustup.prefer_proxy()); + let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir); rustup.envs(extra_env); - rustup.current_dir(current_dir).args(["component", "add", "rust-src"]); + rustup.args(["component", "add", "rust-src"]); tracing::info!("adding rust-src component by {:?}", rustup); - utf8_stdout(rustup).ok()?; + utf8_stdout(&mut rustup).ok()?; get_rust_src(sysroot_path) }) .ok_or_else(|| { diff --git a/crates/project-model/src/target_data_layout.rs b/crates/project-model/src/target_data_layout.rs deleted file mode 100644 index 8a8a2d32558b..000000000000 --- a/crates/project-model/src/target_data_layout.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Runs `rustc --print target-spec-json` to get the target_data_layout. - -use rustc_hash::FxHashMap; -use toolchain::Tool; - -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -/// Determines how `rustc --print target-spec-json` is discovered and invoked. -pub enum RustcDataLayoutConfig<'a> { - /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::rustc`]. - Rustc(&'a Sysroot), - /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via - /// [`toolchain::cargo`]. - Cargo(&'a Sysroot, &'a ManifestPath), -} - -pub fn get( - config: RustcDataLayoutConfig<'_>, - target: Option<&str>, - extra_env: &FxHashMap, -) -> anyhow::Result { - let process = |output: String| { - (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() - .ok_or_else(|| { - anyhow::format_err!("could not fetch target-spec-json from command output") - }) - }; - let sysroot = match config { - RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => { - let mut cmd = sysroot.tool(Tool::Cargo); - cmd.envs(extra_env); - cmd.current_dir(cargo_toml.parent()) - .args([ - "rustc", - "-Z", - "unstable-options", - "--print", - "target-spec-json", - "--", - "-Z", - "unstable-options", - ]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - match utf8_stdout(cmd) { - Ok(output) => return process(output), - Err(e) => { - tracing::warn!("failed to run `cargo rustc --print target-spec-json`, falling back to invoking rustc directly: {e}"); - sysroot - } - } - } - RustcDataLayoutConfig::Rustc(sysroot) => sysroot, - }; - - let mut cmd = Sysroot::tool(sysroot, Tool::Rustc); - cmd.envs(extra_env) - .args(["-Z", "unstable-options", "--print", "target-spec-json"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cmd.args(["--target", target]); - } - process(utf8_stdout(cmd)?) -} diff --git a/crates/project-model/src/toolchain_info/rustc_cfg.rs b/crates/project-model/src/toolchain_info/rustc_cfg.rs new file mode 100644 index 000000000000..12e674a6c4f0 --- /dev/null +++ b/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -0,0 +1,78 @@ +//! Get the built-in cfg flags for the to be compile platform. + +use anyhow::Context; +use cfg::CfgAtom; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout}; + +/// Uses `rustc --print cfg` to fetch the builtin cfgs. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> Vec { + let _p = tracing::info_span!("rustc_cfg::get").entered(); + + let rustc_cfgs = rustc_print_cfg(target, extra_env, config); + let rustc_cfgs = match rustc_cfgs { + Ok(cfgs) => cfgs, + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs"); + return vec![]; + } + }; + + let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::, _>>(); + match rustc_cfgs { + Ok(rustc_cfgs) => { + tracing::debug!(?rustc_cfgs, "rustc cfgs found"); + rustc_cfgs + } + Err(e) => { + tracing::error!(?e, "failed to parse rustc cfgs"); + vec![] + } + } +} + +fn rustc_print_cfg( + target: Option<&str>, + extra_env: &FxHashMap, + config: QueryConfig<'_>, +) -> anyhow::Result { + const RUSTC_ARGS: [&str; 3] = ["--print", "cfg", "-O"]; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + + match utf8_stdout(&mut cmd) { + Ok(it) => return Ok(it), + Err(e) => { + tracing::warn!( + %e, + "failed to run `{cmd:?}`, falling back to invoking rustc directly" + ); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + + utf8_stdout(&mut cmd).with_context(|| format!("unable to fetch cfgs via `{cmd:?}`")) +} diff --git a/crates/project-model/src/toolchain_info/target_data_layout.rs b/crates/project-model/src/toolchain_info/target_data_layout.rs new file mode 100644 index 000000000000..9986c661311e --- /dev/null +++ b/crates/project-model/src/toolchain_info/target_data_layout.rs @@ -0,0 +1,57 @@ +//! Runs `rustc --print target-spec-json` to get the target_data_layout. + +use anyhow::Context; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{toolchain_info::QueryConfig, utf8_stdout, Sysroot}; + +/// Uses `rustc --print target-spec-json`. +pub fn get( + config: QueryConfig<'_>, + target: Option<&str>, + extra_env: &FxHashMap, +) -> anyhow::Result { + const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; + let process = |output: String| { + (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() + .ok_or_else(|| { + anyhow::format_err!("could not parse target-spec-json from command output") + }) + }; + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([ + "--", + "-Z", + "unstable-options", + ]); + if let Some(target) = target { + cmd.args(["--target", target]); + } + match utf8_stdout(&mut cmd) { + Ok(output) => return process(output), + Err(e) => { + tracing::warn!(%e, "failed to run `{cmd:?}`, falling back to invoking rustc directly"); + (sysroot, cargo_toml.parent().as_ref()) + } + } + } + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), + }; + + let mut cmd = Sysroot::tool(sysroot, Tool::Rustc, current_dir); + cmd.envs(extra_env) + .env("RUSTC_BOOTSTRAP", "1") + .args(["-Z", "unstable-options"]) + .args(RUSTC_ARGS); + if let Some(target) = target { + cmd.args(["--target", target]); + } + utf8_stdout(&mut cmd) + .with_context(|| format!("unable to fetch target-data-layout via `{cmd:?}`")) + .and_then(process) +} diff --git a/crates/project-model/src/target_triple.rs b/crates/project-model/src/toolchain_info/target_triple.rs similarity index 53% rename from crates/project-model/src/target_triple.rs rename to crates/project-model/src/toolchain_info/target_triple.rs index 4a32212097d2..163884e5e8ba 100644 --- a/crates/project-model/src/target_triple.rs +++ b/crates/project-model/src/toolchain_info/target_triple.rs @@ -1,46 +1,46 @@ -//! Runs `rustc --print -vV` to get the host target. +//! Functionality to discover the current build target(s). +use std::path::Path; + use anyhow::Context; use rustc_hash::FxHashMap; use toolchain::Tool; -use crate::{utf8_stdout, ManifestPath, Sysroot}; - -pub(super) enum TargetTipleConfig<'a> { - #[expect(dead_code)] - Rustc(&'a Sysroot), - Cargo(&'a Sysroot, &'a ManifestPath), -} +use crate::{toolchain_info::QueryConfig, utf8_stdout, ManifestPath, Sysroot}; -pub(super) fn get( - config: TargetTipleConfig<'_>, +/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s). +/// For rustc, runs `rustc --print -vV` to get the host target. +pub fn get( + config: QueryConfig<'_>, target: Option<&str>, extra_env: &FxHashMap, ) -> anyhow::Result> { + let _p = tracing::info_span!("target_triple::get").entered(); if let Some(target) = target { return Ok(vec![target.to_owned()]); } - let sysroot = match config { - TargetTipleConfig::Cargo(sysroot, cargo_toml) => { + let (sysroot, current_dir) = match config { + QueryConfig::Cargo(sysroot, cargo_toml) => { match cargo_config_build_target(cargo_toml, extra_env, sysroot) { Some(it) => return Ok(it), - None => sysroot, + None => (sysroot, cargo_toml.parent().as_ref()), } } - TargetTipleConfig::Rustc(sysroot) => sysroot, + QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir), }; - rustc_discover_host_triple(extra_env, sysroot).map(|it| vec![it]) + rustc_discover_host_triple(extra_env, sysroot, current_dir).map(|it| vec![it]) } fn rustc_discover_host_triple( extra_env: &FxHashMap, sysroot: &Sysroot, + current_dir: &Path, ) -> anyhow::Result { - let mut rustc = sysroot.tool(Tool::Rustc); - rustc.envs(extra_env); - rustc.arg("-vV"); - tracing::debug!("Discovering host platform by {:?}", rustc); - let stdout = utf8_stdout(rustc).context("Failed to discover host platform")?; + let mut cmd = sysroot.tool(Tool::Rustc, current_dir); + cmd.envs(extra_env); + cmd.arg("-vV"); + let stdout = utf8_stdout(&mut cmd) + .with_context(|| format!("unable to discover host platform via `{cmd:?}`"))?; let field = "host: "; let target = stdout.lines().find_map(|l| l.strip_prefix(field)); if let Some(target) = target { @@ -56,20 +56,18 @@ fn cargo_config_build_target( extra_env: &FxHashMap, sysroot: &Sysroot, ) -> Option> { - let mut cargo_config = sysroot.tool(Tool::Cargo); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["-Z", "unstable-options", "config", "get", "build.target"]) - .env("RUSTC_BOOTSTRAP", "1"); + let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1"); + cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]); // if successful we receive `build.target = "target-triple"` // or `build.target = ["", ..]` - tracing::debug!("Discovering cargo config target by {:?}", cargo_config); // this might be `error: config value `build.target` is not set` in which case we // don't wanna log the error - utf8_stdout(cargo_config).and_then(parse_output_cargo_config_build_target).ok() + utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok() } +// Parses `"build.target = [target-triple, target-triple, ...]"` or `"build.target = "target-triple"` fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result> { let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index d747a8086b46..233f94203e79 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -25,10 +25,8 @@ use crate::{ cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, - rustc_cfg::{self, RustcCfgConfig}, sysroot::{SysrootCrate, SysrootMode}, - target_data_layout::{self, RustcDataLayoutConfig}, - target_triple::{self, TargetTipleConfig}, + toolchain_info::{rustc_cfg, target_data_layout, target_triple, QueryConfig}, utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; @@ -177,10 +175,10 @@ fn get_toolchain_version( extra_env: &FxHashMap, prefix: &str, ) -> Result, anyhow::Error> { - let cargo_version = utf8_stdout({ - let mut cmd = Sysroot::tool(sysroot, tool); + let cargo_version = utf8_stdout(&mut { + let mut cmd = Sysroot::tool(sysroot, tool, current_dir); cmd.envs(extra_env); - cmd.arg("--version").current_dir(current_dir); + cmd.arg("--version"); cmd }) .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?; @@ -221,155 +219,152 @@ impl ProjectWorkspace { ProjectWorkspace::load_detached_file(rust_file, config)? } ProjectManifest::CargoToml(cargo_toml) => { - // FIXME: Split sysroot discovery from sysroot loading, as to load the sysroot we - // want to pass the analysis target, but to discover the target we need to know the - // sysroot location so we know which cargo to use - let sysroot = match (&config.sysroot, &config.sysroot_src) { - (Some(RustLibSource::Discover), None) => Sysroot::discover( - cargo_toml.parent(), - &config.extra_env, - &config.sysroot_query_metadata, - ), - (Some(RustLibSource::Discover), Some(sysroot_src)) => { - Sysroot::discover_with_src_override( - cargo_toml.parent(), - &config.extra_env, - sysroot_src.clone(), - &config.sysroot_query_metadata, - ) - } - (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir( - path.clone(), - &config.sysroot_query_metadata, - ), - (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( - Some(sysroot.clone()), - Some(sysroot_src.clone()), - &config.sysroot_query_metadata, - ), - (None, _) => Sysroot::empty(), - }; - tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); - - let rustc_dir = match &config.rustc_source { - Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) - .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), - Some(RustLibSource::Discover) => { - sysroot.discover_rustc_src().ok_or_else(|| { - Some("Failed to discover rustc source for sysroot.".to_owned()) - }) - } - None => Err(None), - }; - let targets = target_triple::get( - TargetTipleConfig::Cargo(&sysroot, cargo_toml), - config.target.as_deref(), + ProjectWorkspace::load_cargo(cargo_toml, config, progress)? + } + }; + + Ok(res) + } + + fn load_cargo( + cargo_toml: &ManifestPath, + config: &CargoConfig, + progress: &dyn Fn(String), + ) -> Result { + // FIXME: Split sysroot discovery from sysroot loading, as to load the sysroot we + // want to pass the analysis target, but to discover the target we need to know the + // sysroot location so we know which cargo to use + let sysroot = match (&config.sysroot, &config.sysroot_src) { + (Some(RustLibSource::Discover), None) => Sysroot::discover( + cargo_toml.parent(), + &config.extra_env, + &config.sysroot_query_metadata, + ), + (Some(RustLibSource::Discover), Some(sysroot_src)) => { + Sysroot::discover_with_src_override( + cargo_toml.parent(), &config.extra_env, + sysroot_src.clone(), + &config.sysroot_query_metadata, ) - .unwrap_or_default(); - let rustc = rustc_dir.and_then(|rustc_dir| { - info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); - match CargoWorkspace::fetch_metadata( - &rustc_dir, + } + (Some(RustLibSource::Path(path)), None) => { + Sysroot::discover_sysroot_src_dir(path.clone(), &config.sysroot_query_metadata) + } + (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load( + Some(sysroot.clone()), + Some(sysroot_src.clone()), + &config.sysroot_query_metadata, + ), + (None, _) => Sysroot::empty(), + }; + tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot"); + let rustc_dir = match &config.rustc_source { + Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) + .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), + Some(RustLibSource::Discover) => sysroot + .discover_rustc_src() + .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())), + None => Err(None), + }; + let targets = target_triple::get( + QueryConfig::Cargo(&sysroot, cargo_toml), + config.target.as_deref(), + &config.extra_env, + ) + .unwrap_or_default(); + let rustc = rustc_dir.and_then(|rustc_dir| { + info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source"); + match CargoWorkspace::fetch_metadata( + &rustc_dir, + cargo_toml.parent(), + &CargoMetadataConfig { + features: crate::CargoFeatures::default(), + targets: targets.clone(), + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) { + Ok((meta, _error)) => { + let workspace = CargoWorkspace::new(meta, cargo_toml.clone()); + let buildscripts = WorkspaceBuildScripts::rustc_crates( + &workspace, cargo_toml.parent(), - &CargoMetadataConfig { - features: crate::CargoFeatures::default(), - targets: targets.clone(), - extra_args: config.extra_args.clone(), - extra_env: config.extra_env.clone(), - }, + &config.extra_env, &sysroot, - false, - progress, - ) { - Ok((meta, _error)) => { - let workspace = CargoWorkspace::new(meta, cargo_toml.clone()); - let buildscripts = WorkspaceBuildScripts::rustc_crates( - &workspace, - cargo_toml.parent(), - &config.extra_env, - &sysroot - ); - Ok(Box::new((workspace, buildscripts))) - } - Err(e) => { - tracing::error!( - %e, - "Failed to read Cargo metadata from rustc source at {rustc_dir}", - ); - Err(Some(format!( - "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" - ))) - } - } - }); - - let toolchain = get_toolchain_version( - cargo_toml.parent(), - &sysroot, - Tool::Cargo, - &config.extra_env, - "cargo ", - )?; - let rustc_cfg = rustc_cfg::get( - targets.first().map(Deref::deref), - &config.extra_env, - RustcCfgConfig::Cargo(&sysroot, cargo_toml), - ); - - let cfg_overrides = config.cfg_overrides.clone(); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml), - targets.first().map(Deref::deref), - &config.extra_env, - ); - if let Err(e) = &data_layout { - tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); + ); + Ok(Box::new((workspace, buildscripts))) } - - let (meta, error) = CargoWorkspace::fetch_metadata( - cargo_toml, - cargo_toml.parent(), - &CargoMetadataConfig { - features: config.features.clone(), - targets, - extra_args: config.extra_args.clone(), - extra_env: config.extra_env.clone(), - }, - &sysroot, - false, - progress, - ) - .with_context(|| { - format!( - "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", - ) - })?; - let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); - - let cargo_config_extra_env = - cargo_config_env(cargo_toml, &config.extra_env, &sysroot); - ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo, - build_scripts: WorkspaceBuildScripts::default(), - rustc, - cargo_config_extra_env, - error: error.map(Arc::new), - set_test: config.set_test, - }, - sysroot, - rustc_cfg, - cfg_overrides, - toolchain, - target_layout: data_layout - .map(Arc::from) - .map_err(|it| Arc::from(it.to_string())), + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {rustc_dir}", + ); + Err(Some(format!( + "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}" + ))) } } - }; - - Ok(res) + }); + let toolchain = get_toolchain_version( + cargo_toml.parent(), + &sysroot, + Tool::Cargo, + &config.extra_env, + "cargo ", + )?; + let rustc_cfg = rustc_cfg::get( + QueryConfig::Cargo(&sysroot, cargo_toml), + targets.first().map(Deref::deref), + &config.extra_env, + ); + let cfg_overrides = config.cfg_overrides.clone(); + let data_layout = target_data_layout::get( + QueryConfig::Cargo(&sysroot, cargo_toml), + targets.first().map(Deref::deref), + &config.extra_env, + ); + if let Err(e) = &data_layout { + tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace"); + } + let (meta, error) = CargoWorkspace::fetch_metadata( + cargo_toml, + cargo_toml.parent(), + &CargoMetadataConfig { + features: config.features.clone(), + targets, + extra_args: config.extra_args.clone(), + extra_env: config.extra_env.clone(), + }, + &sysroot, + false, + progress, + ) + .with_context(|| { + format!( + "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", + ) + })?; + let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); + let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot); + Ok(ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo, + build_scripts: WorkspaceBuildScripts::default(), + rustc, + cargo_config_extra_env, + error: error.map(Arc::new), + set_test: config.set_test, + }, + sysroot, + rustc_cfg, + cfg_overrides, + toolchain, + target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + }) } pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace { @@ -378,8 +373,7 @@ impl ProjectWorkspace { project_json.sysroot_src.clone(), &config.sysroot_query_metadata, ); - let cfg_config = RustcCfgConfig::Rustc(&sysroot); - let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot); + let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref()); let toolchain = match get_toolchain_version( project_json.path(), &sysroot, @@ -395,8 +389,8 @@ impl ProjectWorkspace { }; let target = config.target.as_deref(); - let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config); - let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env); + let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env); + let data_layout = target_data_layout::get(query_config, target, &config.extra_env); ProjectWorkspace { kind: ProjectWorkspaceKind::Json(project_json), sysroot, @@ -432,17 +426,14 @@ impl ProjectWorkspace { }; let targets = target_triple::get( - TargetTipleConfig::Cargo(&sysroot, detached_file), + QueryConfig::Cargo(&sysroot, detached_file), config.target.as_deref(), &config.extra_env, ) .unwrap_or_default(); - let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot)); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), - None, - &config.extra_env, - ); + let query_config = QueryConfig::Rustc(&sysroot, dir.as_ref()); + let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); + let data_layout = target_data_layout::get(query_config, None, &config.extra_env); let cargo_script = CargoWorkspace::fetch_metadata( detached_file, @@ -954,7 +945,11 @@ fn project_json_to_crate_graph( let target_cfgs = match target.as_deref() { Some(target) => cfg_cache.entry(target).or_insert_with(|| { - rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot)) + rustc_cfg::get( + QueryConfig::Rustc(sysroot, project.project_root().as_ref()), + Some(target), + extra_env, + ) }), None => &rustc_cfg, }; diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs index 72b741de00ee..0fd381d61221 100644 --- a/crates/rust-analyzer/build.rs +++ b/crates/rust-analyzer/build.rs @@ -32,6 +32,7 @@ fn set_rerun() { } fn set_commit_info() { + #[allow(clippy::disallowed_methods)] let output = match Command::new("git") .arg("log") .arg("-1") diff --git a/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/crates/rust-analyzer/src/bin/rustc_wrapper.rs index 684b3f52afc8..b9fcd2e18709 100644 --- a/crates/rust-analyzer/src/bin/rustc_wrapper.rs +++ b/crates/rust-analyzer/src/bin/rustc_wrapper.rs @@ -46,6 +46,7 @@ fn run_rustc_skipping_cargo_checking( } fn run_rustc(rustc_executable: OsString, args: Vec) -> io::Result { + #[allow(clippy::disallowed_methods)] let mut child = Command::new(rustc_executable) .args(args) .stdin(Stdio::inherit()) diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index db792ade574d..dabc71b1b992 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -10,10 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use itertools::Either; use paths::Utf8PathBuf; use profile::StopWatch; -use project_model::target_data_layout::RustcDataLayoutConfig; +use project_model::toolchain_info::{target_data_layout, QueryConfig}; use project_model::{ - target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, - RustLibSource, Sysroot, SysrootQueryMetadata, + CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot, + SysrootQueryMetadata, }; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -80,7 +80,7 @@ impl Tester { &SysrootQueryMetadata::CargoMetadata(Default::default()), ); let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(&sysroot), + QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), None, &cargo_config.extra_env, ); diff --git a/crates/rust-analyzer/src/discover.rs b/crates/rust-analyzer/src/discover.rs index 96b164228efe..0c111319bb41 100644 --- a/crates/rust-analyzer/src/discover.rs +++ b/crates/rust-analyzer/src/discover.rs @@ -1,6 +1,6 @@ //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery //! via a custom discover command. -use std::{io, process::Command}; +use std::{io, path::Path}; use crossbeam_channel::Sender; use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf}; @@ -43,7 +43,11 @@ impl DiscoverCommand { } /// Spawn the command inside [Discover] and report progress, if any. - pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result { + pub(crate) fn spawn( + &self, + discover_arg: DiscoverArgument, + current_dir: &Path, + ) -> io::Result { let command = &self.command[0]; let args = &self.command[1..]; @@ -58,7 +62,7 @@ impl DiscoverCommand { }) .collect(); - let mut cmd = Command::new(command); + let mut cmd = toolchain::command(command, current_dir); cmd.args(args); Ok(DiscoverHandle { diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index c7bb275c5f10..16ed674406db 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -444,12 +444,11 @@ impl FlycheckActor { ) -> Option { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root); if let Some(sysroot_root) = &self.sysroot_root { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } cmd.arg(command); - cmd.current_dir(&*self.root); match package { Some(pkg) => cmd.arg("-p").arg(pkg), @@ -486,18 +485,15 @@ impl FlycheckActor { Some(cmd) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { - let mut cmd = Command::new(command); - cmd.envs(extra_env); - - match invocation_strategy { - InvocationStrategy::Once => { - cmd.current_dir(&*self.root); - } + let root = match invocation_strategy { + InvocationStrategy::Once => &*self.root, InvocationStrategy::PerWorkspace => { - // FIXME: cmd.current_dir(&affected_workspace); - cmd.current_dir(&*self.root); + // FIXME: &affected_workspace + &*self.root } - } + }; + let mut cmd = toolchain::command(command, root); + cmd.envs(extra_env); // If the custom command has a $saved_file placeholder, and // we're saving a file, replace the placeholder in the arguments. diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 8f2bf80ea26d..d2ed43e882fd 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1,12 +1,7 @@ //! This module is responsible for implementing handlers for Language Server //! Protocol. This module specifically handles requests. -use std::{ - fs, - io::Write as _, - ops::Not, - process::{self, Stdio}, -}; +use std::{fs, io::Write as _, ops::Not, process::Stdio}; use anyhow::Context; @@ -2243,10 +2238,31 @@ fn run_rustfmt( let line_index = snap.file_line_index(file_id)?; let source_root_id = snap.analysis.source_root_id(file_id).ok(); + // try to chdir to the file so we can respect `rustfmt.toml` + // FIXME: use `rustfmt --config-path` once + // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed + let current_dir = match text_document.uri.to_file_path() { + Ok(mut path) => { + // pop off file name + if path.pop() && path.is_dir() { + path + } else { + std::env::current_dir()? + } + } + Err(_) => { + tracing::error!( + text_document = ?text_document.uri, + "Unable to get path, rustfmt.toml might be ignored" + ); + std::env::current_dir()? + } + }; + let mut command = match snap.config.rustfmt(source_root_id) { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN - let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); + let mut cmd = toolchain::command(toolchain::Tool::Rustfmt.path(), current_dir); cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); @@ -2300,9 +2316,9 @@ fn run_rustfmt( } else { cmd }; - process::Command::new(cmd_path) + toolchain::command(cmd_path, current_dir) } - _ => process::Command::new(cmd), + _ => toolchain::command(cmd, current_dir), }; cmd.envs(snap.config.extra_env(source_root_id)); @@ -2313,24 +2329,6 @@ fn run_rustfmt( tracing::debug!(?command, "created format command"); - // try to chdir to the file so we can respect `rustfmt.toml` - // FIXME: use `rustfmt --config-path` once - // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed - match text_document.uri.to_file_path() { - Ok(mut path) => { - // pop off file name - if path.pop() && path.is_dir() { - command.current_dir(path); - } - } - Err(_) => { - tracing::error!( - text_document = ?text_document.uri, - "Unable to get path, rustfmt.toml might be ignored" - ); - } - } - let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index d97d96d54a01..97657b926583 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -744,7 +744,8 @@ impl GlobalState { DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = discover.spawn(arg).unwrap(); + let handle = + discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap(); self.discover_handle = Some(handle); } } diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs index 2fd52547336e..503b3ee43a12 100644 --- a/crates/rust-analyzer/src/test_runner.rs +++ b/crates/rust-analyzer/src/test_runner.rs @@ -1,8 +1,6 @@ //! This module provides the functionality needed to run `cargo test` in a background //! thread and report the result of each test in a channel. -use std::process::Command; - use crossbeam_channel::Sender; use paths::AbsPath; use serde::Deserialize as _; @@ -78,7 +76,7 @@ impl CargoTestHandle { test_target: TestTarget, sender: Sender, ) -> std::io::Result { - let mut cmd = Command::new(Tool::Cargo.path()); + let mut cmd = toolchain::command(Tool::Cargo.path(), root); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs index a0603e35a09f..33578e056ee6 100644 --- a/crates/toolchain/src/lib.rs +++ b/crates/toolchain/src/lib.rs @@ -1,6 +1,12 @@ //! Discovery of `cargo` & `rustc` executables. -use std::{env, iter, path::PathBuf}; +use std::{ + env, + ffi::OsStr, + iter, + path::{Path, PathBuf}, + process::Command, +}; use camino::{Utf8Path, Utf8PathBuf}; @@ -65,6 +71,14 @@ impl Tool { } } +pub fn command(cmd: impl AsRef, working_directory: impl AsRef) -> Command { + // we are `toolchain::command`` + #[allow(clippy::disallowed_methods)] + let mut cmd = Command::new(cmd); + cmd.current_dir(working_directory); + cmd +} + fn invoke(list: &[fn(&str) -> Option], executable: &str) -> Utf8PathBuf { list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into()) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 5c312da1dd73..1e723b90a5ea 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -9,7 +9,12 @@ //! `.cargo/config`. #![warn(rust_2018_idioms, unused_lifetimes)] -#![allow(clippy::print_stderr, clippy::print_stdout)] +#![allow( + clippy::print_stderr, + clippy::print_stdout, + clippy::disallowed_methods, + clippy::disallowed_types +)] mod flags;