diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 5dfaaf7742..6d5ca8321e 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -125,8 +125,10 @@ impl FlycheckHandle { config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, + manifest_path: Option, ) -> FlycheckHandle { - let actor = FlycheckActor::new(id, sender, config, sysroot_root, workspace_root); + let actor = + FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); let (sender, receiver) = unbounded::(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("Flycheck".to_owned()) @@ -205,6 +207,7 @@ struct FlycheckActor { id: usize, sender: Box, config: FlycheckConfig, + manifest_path: Option, /// Either the workspace root of the workspace we are flychecking, /// or the project root of the project. root: AbsPathBuf, @@ -233,6 +236,7 @@ impl FlycheckActor { config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, + manifest_path: Option, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { @@ -241,6 +245,7 @@ impl FlycheckActor { config, sysroot_root, root: workspace_root, + manifest_path, command_handle: None, command_receiver: None, } @@ -388,8 +393,13 @@ impl FlycheckActor { "--message-format=json" }); - cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml")); + if let Some(manifest_path) = &self.manifest_path { + cmd.arg("--manifest-path"); + cmd.arg(manifest_path); + if manifest_path.extension().map_or(false, |ext| ext == "rs") { + cmd.arg("-Zscript"); + } + } options.apply_on_command(&mut cmd); (cmd, options.extra_args.clone()) diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 31b0c8cdec..1232322ee3 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -333,9 +333,7 @@ fn load_crate_graph( vfs: &mut vfs::Vfs, receiver: &Receiver, ) -> RootDatabase { - let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; + let ProjectWorkspace { toolchain, target_layout, .. } = ws; let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); let mut db = RootDatabase::new(lru_cap); diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 3fdd59967b..4dc3b3f349 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -116,7 +116,7 @@ impl WorkspaceBuildScripts { } } - if manifest_path.extension().map_or(false, |ext| ext == "rs") { + if manifest_path.is_rust_manifest() { cmd.arg("-Zscript"); } diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 88ba5a0da9..9955f2687c 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -307,7 +307,7 @@ impl CargoWorkspace { ); } // The manifest is a rust file, so this means its a script manifest - if cargo_toml.extension().is_some_and(|ext| ext == "rs") { + if cargo_toml.is_rust_manifest() { // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should // opt into it themselves. other_options.push("-Zscript".to_owned()); diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs index 762e01c917..5520cdaff6 100644 --- a/crates/project-model/src/env.rs +++ b/crates/project-model/src/env.rs @@ -60,16 +60,19 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe } pub(crate) fn cargo_config_env( - cargo_toml: &ManifestPath, + manifest: &ManifestPath, extra_env: &FxHashMap, sysroot: Option<&Sysroot>, ) -> FxHashMap { let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); cargo_config.envs(extra_env); cargo_config - .current_dir(cargo_toml.parent()) + .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); + if manifest.is_rust_manifest() { + cargo_config.arg("-Zscript"); + } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default() diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 5428f061b7..181c07f46b 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -52,7 +52,7 @@ pub use crate::{ manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, - workspace::{FileLoader, PackageRoot, ProjectWorkspace}, + workspace::{FileLoader, PackageRoot, ProjectWorkspace, ProjectWorkspaceKind}, }; pub use cargo_metadata::Metadata; diff --git a/crates/project-model/src/manifest_path.rs b/crates/project-model/src/manifest_path.rs index 6929a75895..2331c0c36c 100644 --- a/crates/project-model/src/manifest_path.rs +++ b/crates/project-model/src/manifest_path.rs @@ -38,6 +38,10 @@ impl ManifestPath { pub fn canonicalize(&self) -> ! { (**self).canonicalize() } + + pub fn is_rust_manifest(&self) -> bool { + self.file.extension().map_or(false, |ext| ext == "rs") + } } impl fmt::Display for ManifestPath { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 41351d5dc0..1308015d13 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -10,8 +10,8 @@ use serde::de::DeserializeOwned; use triomphe::Arc; use crate::{ - CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, - Sysroot, WorkspaceBuildScripts, + workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, + ProjectJsonData, ProjectWorkspace, Sysroot, WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { @@ -26,16 +26,18 @@ fn load_cargo_with_overrides( let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), - sysroot: Err(None), - rustc: Err(None), - rustc_cfg: Vec::new(), + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, cfg_overrides, + sysroot: Err(None), + rustc_cfg: Vec::new(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; to_crate_graph(project_workspace) } @@ -48,16 +50,18 @@ fn load_cargo_with_fake_sysroot( let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot: Ok(get_fake_sysroot()), - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; project_workspace.to_crate_graph( &mut { @@ -74,8 +78,8 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); let sysroot = Ok(get_fake_sysroot()); - let project_workspace = ProjectWorkspace::Json { - project, + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Json(project), sysroot, rustc_cfg: Vec::new(), toolchain: None, @@ -284,16 +288,18 @@ fn smoke_test_real_sysroot_cargo() { ) .unwrap()); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot, - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; project_workspace.to_crate_graph( &mut { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 9f6e75171b..7f845f85ae 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -44,50 +44,39 @@ pub struct PackageRoot { } #[derive(Clone)] -pub enum ProjectWorkspace { +pub struct ProjectWorkspace { + pub kind: ProjectWorkspaceKind, + /// The sysroot loaded for this workspace. + pub sysroot: Result>, + /// 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. + pub rustc_cfg: Vec, + /// The toolchain version used by this workspace. + pub toolchain: Option, + /// The target data layout queried for workspace. + pub target_layout: TargetLayoutLoadResult, + /// A set of cfg overrides for this workspace. + pub cfg_overrides: CfgOverrides, +} + +#[derive(Clone)] +pub enum ProjectWorkspaceKind { /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. Cargo { /// The workspace as returned by `cargo metadata`. cargo: CargoWorkspace, /// The build script results for the workspace. build_scripts: WorkspaceBuildScripts, - /// The sysroot loaded for this workspace. - sysroot: Result>, /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been /// disabled or was otherwise not requested. rustc: Result, 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, - /// A set of cfg overrides for this workspace. - cfg_overrides: CfgOverrides, - /// The toolchain version used by this workspace. - toolchain: Option, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, /// Environment variables set in the `.cargo/config` file. cargo_config_extra_env: FxHashMap, }, /// Project workspace was manually specified using a `rust-project.json` file. - Json { - /// The loaded project json file. - project: ProjectJson, - /// The sysroot loaded for this workspace. - sysroot: Result>, - /// 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, - /// The toolchain version used by this workspace. - toolchain: Option, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, - /// A set of cfg overrides for this workspace. - cfg_overrides: CfgOverrides, - }, + Json(ProjectJson), // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning. // That's not the end user experience we should strive for. // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working. @@ -101,37 +90,22 @@ pub enum ProjectWorkspace { DetachedFile { /// The file in question. file: ManifestPath, - /// The sysroot loaded for this workspace. - sysroot: Result>, - /// 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, - /// The toolchain version used by this workspace. - toolchain: Option, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, - /// A set of cfg overrides for the files. - cfg_overrides: CfgOverrides, /// Is this file a cargo script file? - cargo_script: Option<(CargoWorkspace, WorkspaceBuildScripts)>, + cargo: Option<(CargoWorkspace, WorkspaceBuildScripts)>, + /// Environment variables set in the `.cargo/config` file. + cargo_config_extra_env: FxHashMap, }, } impl fmt::Debug for ProjectWorkspace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Make sure this isn't too verbose. - match self { - ProjectWorkspace::Cargo { + let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self; + match kind { + ProjectWorkspaceKind::Cargo { cargo, build_scripts: _, - sysroot, rustc, - rustc_cfg, - cfg_overrides, - toolchain, - target_layout, cargo_config_extra_env, } => f .debug_struct("Cargo") @@ -148,14 +122,7 @@ impl fmt::Debug for ProjectWorkspace { .field("data_layout", &target_layout) .field("cargo_config_extra_env", &cargo_config_extra_env) .finish(), - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg, - toolchain, - target_layout: data_layout, - cfg_overrides, - } => { + ProjectWorkspaceKind::Json(project) => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); if let Ok(sysroot) = sysroot { @@ -164,18 +131,14 @@ impl fmt::Debug for ProjectWorkspace { debug_struct .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) - .field("data_layout", &data_layout) + .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()); debug_struct.finish() } - ProjectWorkspace::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file, - sysroot, - rustc_cfg, - toolchain, - target_layout, - cfg_overrides, - cargo_script, + cargo: cargo_script, + cargo_config_extra_env, } => f .debug_struct("DetachedFiles") .field("file", &file) @@ -186,6 +149,7 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()) + .field("cargo_config_extra_env", &cargo_config_extra_env) .finish(), } } @@ -361,18 +325,20 @@ impl ProjectWorkspace { let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, sysroot_ref); - ProjectWorkspace::Cargo { - cargo, - build_scripts: WorkspaceBuildScripts::default(), + ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo, + build_scripts: WorkspaceBuildScripts::default(), + rustc, + cargo_config_extra_env, + }, sysroot, - rustc, rustc_cfg, cfg_overrides, toolchain, target_layout: data_layout .map(Arc::from) .map_err(|it| Arc::from(it.to_string())), - cargo_config_extra_env, } } }; @@ -425,8 +391,8 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get(target, extra_env, cfg_config); let data_layout = target_data_layout::get(data_layout_config, target, extra_env); - ProjectWorkspace::Json { - project: project_json, + ProjectWorkspace { + kind: ProjectWorkspaceKind::Json(project_json), sysroot, rustc_cfg, toolchain, @@ -484,14 +450,19 @@ impl ProjectWorkspace { ) }); - Ok(ProjectWorkspace::DetachedFile { - file: detached_file.to_owned(), + let cargo_config_extra_env = + cargo_config_env(detached_file, &config.extra_env, sysroot_ref); + Ok(ProjectWorkspace { + kind: ProjectWorkspaceKind::DetachedFile { + file: detached_file.to_owned(), + cargo: cargo_script, + cargo_config_extra_env, + }, sysroot, rustc_cfg, toolchain, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: config.cfg_overrides.clone(), - cargo_script, }) } @@ -508,27 +479,22 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> anyhow::Result { - match self { - ProjectWorkspace::DetachedFile { - cargo_script: Some((cargo, _)), - toolchain, - sysroot, - .. - } - | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => { + match &self.kind { + ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } + | ProjectWorkspaceKind::Cargo { cargo, .. } => { WorkspaceBuildScripts::run_for_workspace( config, cargo, progress, - toolchain, - sysroot.as_ref().ok(), + &self.toolchain, + self.sysroot.as_ref().ok(), ) .with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) }) } - ProjectWorkspace::DetachedFile { cargo_script: None, .. } - | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()), + ProjectWorkspaceKind::DetachedFile { cargo: None, .. } + | ProjectWorkspaceKind::Json { .. } => Ok(WorkspaceBuildScripts::default()), } } @@ -548,8 +514,8 @@ impl ProjectWorkspace { let cargo_ws: Vec<_> = workspaces .iter() - .filter_map(|it| match it { - ProjectWorkspace::Cargo { cargo, .. } => Some(cargo), + .filter_map(|it| match &it.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo), _ => None, }) .collect(); @@ -563,8 +529,8 @@ impl ProjectWorkspace { workspaces .iter() - .map(|it| match it { - ProjectWorkspace::Cargo { cargo, .. } => match outputs { + .map(|it| match &it.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => match outputs { Ok(outputs) => Ok(outputs.next().unwrap()), Err(e) => Err(e.clone()).with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) @@ -576,40 +542,33 @@ impl ProjectWorkspace { } pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) { - match self { - ProjectWorkspace::Cargo { build_scripts, .. } - | ProjectWorkspace::DetachedFile { cargo_script: Some((_, build_scripts)), .. } => { + match &mut self.kind { + ProjectWorkspaceKind::Cargo { build_scripts, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts)), .. } => { *build_scripts = bs } _ => assert_eq!(bs, WorkspaceBuildScripts::default()), } } - pub fn workspace_definition_path(&self) -> &AbsPath { - match self { - ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(), - ProjectWorkspace::Json { project, .. } => project.path(), - ProjectWorkspace::DetachedFile { file, .. } => file, + pub fn manifest_or_root(&self) -> &AbsPath { + match &self.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.manifest_path(), + ProjectWorkspaceKind::Json(project) => project.path(), + ProjectWorkspaceKind::DetachedFile { file, .. } => file, } } pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { - match self { - ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => { - sysroot.discover_proc_macro_srv() - } - ProjectWorkspace::DetachedFile { .. } => { - Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found")) - } - ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!( - "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - cargo.workspace_root() + match &self.sysroot { + Ok(sysroot) => sysroot.discover_proc_macro_srv(), + Err(None) => Err(anyhow::format_err!( + "cannot find proc-macro server, the workspace `{}` is missing a sysroot", + self.manifest_or_root() )), - ProjectWorkspace::Json { project, .. } => Err(anyhow::format_err!( - "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - project.path() + Err(Some(e)) => Err(anyhow::format_err!( + "cannot find proc-macro server, the workspace `{}` is missing a sysroot: {e}", + self.manifest_or_root() )), } } @@ -618,8 +577,8 @@ impl ProjectWorkspace { /// The return type contains the path and whether or not /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec { - let mk_sysroot = |sysroot: Result<_, _>| { - sysroot.into_iter().flat_map(move |sysroot: &Sysroot| { + let mk_sysroot = || { + self.sysroot.as_ref().into_iter().flat_map(move |sysroot: &Sysroot| { let mut r = match sysroot.mode() { SysrootMode::Workspace(ws) => ws .packages() @@ -653,15 +612,8 @@ impl ProjectWorkspace { r }) }; - match self { - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg: _, - toolchain: _, - target_layout: _, - cfg_overrides: _, - } => project + match &self.kind { + ProjectWorkspaceKind::Json(project) => project .crates() .map(|(_, krate)| PackageRoot { is_local: krate.is_workspace_member, @@ -670,17 +622,12 @@ impl ProjectWorkspace { }) .collect::>() .into_iter() - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .collect::>(), - ProjectWorkspace::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg: _, - cfg_overrides: _, build_scripts, - toolchain: _, - target_layout: _, cargo_config_extra_env: _, } => { cargo @@ -721,7 +668,7 @@ impl ProjectWorkspace { } PackageRoot { is_local, include, exclude } }) - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .chain(rustc.iter().map(|a| a.as_ref()).flat_map(|(rustc, _)| { rustc.packages().map(move |krate| PackageRoot { is_local: false, @@ -731,7 +678,7 @@ impl ProjectWorkspace { })) .collect() } - ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => { + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => { iter::once(PackageRoot { is_local: true, include: vec![file.as_ref().to_owned()], @@ -775,26 +722,26 @@ impl ProjectWorkspace { PackageRoot { is_local, include, exclude } }) })) - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .collect() } } } pub fn n_packages(&self) -> usize { - match self { - ProjectWorkspace::Json { project, sysroot, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + match &self.kind { + ProjectWorkspaceKind::Json(project) => { + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + project.n_crates() } - ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => { + ProjectWorkspaceKind::Cargo { cargo, rustc, .. } => { let rustc_package_len = rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(it, _)| it.packages().len()); - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); cargo.packages().len() + sysroot_package_len + rustc_package_len } - ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => { + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len()) } @@ -808,15 +755,9 @@ impl ProjectWorkspace { ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); - let ((mut crate_graph, proc_macros), sysroot) = match self { - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg, - toolchain: _, - target_layout: _, - cfg_overrides, - } => ( + let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self; + let ((mut crate_graph, proc_macros), sysroot) = match kind { + ProjectWorkspaceKind::Json(project) => ( project_json_to_crate_graph( rustc_cfg.clone(), load, @@ -827,15 +768,10 @@ impl ProjectWorkspace { ), sysroot, ), - ProjectWorkspace::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg, - cfg_overrides, build_scripts, - toolchain: _, - target_layout: _, cargo_config_extra_env: _, } => ( cargo_to_crate_graph( @@ -849,15 +785,7 @@ impl ProjectWorkspace { ), sysroot, ), - ProjectWorkspace::DetachedFile { - file, - sysroot, - rustc_cfg, - toolchain: _, - target_layout: _, - cfg_overrides, - cargo_script, - } => ( + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => ( if let Some((cargo, build_scripts)) = cargo_script { cargo_to_crate_graph( &mut |path| load(path), @@ -892,93 +820,60 @@ impl ProjectWorkspace { } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { - match (self, other) { + let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self; + let Self { + kind: o_kind, + sysroot: o_sysroot, + rustc_cfg: o_rustc_cfg, + toolchain: o_toolchain, + target_layout: o_target_layout, + cfg_overrides: o_cfg_overrides, + .. + } = other; + (match (kind, o_kind) { ( - Self::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg, - cfg_overrides, - toolchain, cargo_config_extra_env, build_scripts: _, - target_layout: _, }, - Self::Cargo { + ProjectWorkspaceKind::Cargo { cargo: o_cargo, - sysroot: o_sysroot, rustc: o_rustc, - rustc_cfg: o_rustc_cfg, - cfg_overrides: o_cfg_overrides, - toolchain: o_toolchain, cargo_config_extra_env: o_cargo_config_extra_env, build_scripts: _, - target_layout: _, }, ) => { cargo == o_cargo && rustc == o_rustc - && rustc_cfg == o_rustc_cfg - && cfg_overrides == o_cfg_overrides - && toolchain == o_toolchain - && sysroot == o_sysroot && cargo_config_extra_env == o_cargo_config_extra_env } - ( - Self::Json { - project, - sysroot, - rustc_cfg, - toolchain, - target_layout: _, - cfg_overrides, - }, - Self::Json { - project: o_project, - sysroot: o_sysroot, - rustc_cfg: o_rustc_cfg, - toolchain: o_toolchain, - target_layout: _, - cfg_overrides: o_cfg_overrides, - }, - ) => { + (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => { project == o_project - && rustc_cfg == o_rustc_cfg - && sysroot == o_sysroot - && toolchain == o_toolchain - && cfg_overrides == o_cfg_overrides } ( - Self::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file, - sysroot, - rustc_cfg, - cargo_script: Some((cargo_script, _)), - toolchain, - target_layout, - cfg_overrides, + cargo: Some((cargo_script, _)), + cargo_config_extra_env, }, - Self::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file: o_file, - sysroot: o_sysroot, - rustc_cfg: o_rustc_cfg, - cargo_script: Some((o_cargo_script, _)), - toolchain: o_toolchain, - target_layout: o_target_layout, - cfg_overrides: o_cfg_overrides, + cargo: Some((o_cargo_script, _)), + cargo_config_extra_env: o_cargo_config_extra_env, }, ) => { file == o_file - && sysroot == o_sysroot - && rustc_cfg == o_rustc_cfg - && toolchain == o_toolchain - && target_layout == o_target_layout - && cfg_overrides == o_cfg_overrides && cargo_script == o_cargo_script + && cargo_config_extra_env == o_cargo_config_extra_env } - _ => false, - } + _ => return false, + }) && sysroot == o_sysroot + && rustc_cfg == o_rustc_cfg + && toolchain == o_toolchain + && target_layout == o_target_layout + && cfg_overrides == o_cfg_overrides } /// Returns `true` if the project workspace is [`Json`]. @@ -986,7 +881,7 @@ impl ProjectWorkspace { /// [`Json`]: ProjectWorkspace::Json #[must_use] pub fn is_json(&self) -> bool { - matches!(self, Self::Json { .. }) + matches!(self.kind, ProjectWorkspaceKind::Json { .. }) } } diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index cd15d86aa6..85f964b1dd 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -11,7 +11,8 @@ use itertools::Either; use profile::StopWatch; use project_model::target_data_layout::RustcDataLayoutConfig; use project_model::{ - target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, RustLibSource, Sysroot, + target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, + RustLibSource, Sysroot, }; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; @@ -77,14 +78,17 @@ impl Tester { &cargo_config.extra_env, ); - let workspace = ProjectWorkspace::DetachedFile { - file: ManifestPath::try_from(tmp_file).unwrap(), + let workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::DetachedFile { + file: ManifestPath::try_from(tmp_file).unwrap(), + cargo: None, + cargo_config_extra_env: Default::default(), + }, sysroot, rustc_cfg: vec![], toolchain: None, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: Default::default(), - cargo_script: None, }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: false, diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index eaf3f511c4..1fcb5d4496 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -19,7 +19,8 @@ use parking_lot::{ }; use proc_macro_api::ProcMacroServer; use project_model::{ - CargoWorkspace, ManifestPath, ProjectWorkspace, Target, WorkspaceBuildScripts, + CargoWorkspace, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, Target, + WorkspaceBuildScripts, }; use rustc_hash::{FxHashMap, FxHashSet}; use triomphe::Arc; @@ -518,12 +519,13 @@ impl GlobalStateSnapshot { let file_id = self.analysis.crate_root(crate_id).ok()?; let path = self.vfs_read().file_path(file_id).clone(); let path = path.as_path()?; - self.workspaces.iter().find_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, .. } => { + self.workspaces.iter().find_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { cargo.target_by_root(path).map(|it| (cargo, it)) } - ProjectWorkspace::Json { .. } => None, - ProjectWorkspace::DetachedFile { .. } => None, + ProjectWorkspaceKind::Json { .. } => None, + ProjectWorkspaceKind::DetachedFile { .. } => None, }) } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 4b8c3d06ce..fee8e97273 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -289,17 +289,19 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // Find all workspaces that have at least one target containing the saved file let workspace_ids = world.workspaces.iter().enumerate().filter_map(|(idx, ws)| { - let package = match ws { - project_model::ProjectWorkspace::Cargo { cargo, .. } => { - cargo.packages().find_map(|pkg| { - let has_target_with_root = cargo[pkg] - .targets - .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo[pkg].name.clone()) - }) - } - project_model::ProjectWorkspace::Json { project, .. } => { + let package = match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _)), + .. + } => cargo.packages().find_map(|pkg| { + let has_target_with_root = cargo[pkg] + .targets + .iter() + .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); + has_target_with_root.then(|| cargo[pkg].name.clone()) + }), + project_model::ProjectWorkspaceKind::Json(project) => { if !project.crates().any(|(_, krate)| { crate_root_paths.contains(&krate.root_module.as_path()) }) { @@ -307,8 +309,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } None } - // FIXME - project_model::ProjectWorkspace::DetachedFile { .. } => return None, + project_model::ProjectWorkspaceKind::DetachedFile { .. } => return None, }; Some((idx, package)) }); diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 39cdef08f8..6afcc2c51f 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -27,7 +27,7 @@ use lsp_types::{ SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; use paths::Utf8PathBuf; -use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; +use project_model::{ManifestPath, ProjectWorkspaceKind, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; @@ -99,10 +99,7 @@ pub(crate) fn handle_analyzer_status( format_to!( buf, "Workspace root folders: {:?}", - snap.workspaces - .iter() - .map(|ws| ws.workspace_definition_path()) - .collect::>() + snap.workspaces.iter().map(|ws| ws.manifest_or_root()).collect::>() ); } buf.push_str("\nAnalysis:\n"); @@ -228,7 +225,7 @@ pub(crate) fn handle_run_test( }; let mut handles = vec![]; for ws in &*state.workspaces { - if let ProjectWorkspace::Cargo { cargo, .. } = ws { + if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind { let handle = flycheck::CargoTestHandle::new( test_path, state.config.cargo_test_options(), @@ -769,8 +766,11 @@ pub(crate) fn handle_parent_module( let links: Vec = snap .workspaces .iter() - .filter_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, .. } => cargo.parent_manifests(&manifest_path), + .filter_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { + cargo.parent_manifests(&manifest_path) + } _ => None, }) .flatten() @@ -1758,13 +1758,13 @@ pub(crate) fn handle_open_docs( let _p = tracing::span!(tracing::Level::INFO, "handle_open_docs").entered(); let position = from_proto::file_position(&snap, params)?; - let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, sysroot, .. } - | ProjectWorkspace::DetachedFile { cargo_script: Some((cargo, _)), sysroot, .. } => { - Some((cargo, sysroot.as_ref().ok())) + let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { + Some((cargo, ws.sysroot.as_ref().ok())) } - ProjectWorkspace::Json { .. } => None, - ProjectWorkspace::DetachedFile { .. } => None, + ProjectWorkspaceKind::Json { .. } => None, + ProjectWorkspaceKind::DetachedFile { .. } => None, }); let (cargo, sysroot) = match ws_and_sysroot { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 4cc174731c..f2b6e3ed03 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -25,7 +25,7 @@ use ide_db::{ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; -use project_model::{ManifestPath, ProjectWorkspace, WorkspaceBuildScripts}; +use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -151,9 +151,7 @@ impl GlobalState { } for ws in self.workspaces.iter() { - let (ProjectWorkspace::Cargo { sysroot, .. } - | ProjectWorkspace::Json { sysroot, .. } - | ProjectWorkspace::DetachedFile { sysroot, .. }) = ws; + let sysroot = ws.sysroot.as_ref(); match sysroot { Err(None) => (), Err(Some(e)) => { @@ -169,7 +167,7 @@ impl GlobalState { } } } - if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws { + if let ProjectWorkspaceKind::Cargo { rustc: Err(Some(e)), .. } = &ws.kind { status.health = lsp_ext::Health::Warning; message.push_str(e); message.push_str("\n\n"); @@ -501,20 +499,25 @@ impl GlobalState { None => ws.find_sysroot_proc_macro_srv()?, }; - let env = - match ws { - ProjectWorkspace::Cargo { cargo_config_extra_env, sysroot, .. } => { - cargo_config_extra_env - .iter() - .chain(self.config.extra_env()) - .map(|(a, b)| (a.clone(), b.clone())) - .chain(sysroot.as_ref().map(|it| { - ("RUSTUP_TOOLCHAIN".to_owned(), it.root().to_string()) - })) - .collect() - } - _ => Default::default(), - }; + let env = match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo_config_extra_env, .. } + | ProjectWorkspaceKind::DetachedFile { + cargo: Some(_), + cargo_config_extra_env, + .. + } => cargo_config_extra_env + .iter() + .chain(self.config.extra_env()) + .map(|(a, b)| (a.clone(), b.clone())) + .chain( + ws.sysroot + .as_ref() + .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), it.root().to_string())), + ) + .collect(), + + _ => Default::default(), + }; tracing::info!("Using proc-macro server at {path}"); ProcMacroServer::spawn(path.clone(), &env).map_err(|err| { @@ -561,8 +564,8 @@ impl GlobalState { self.detached_files = self .workspaces .iter() - .filter_map(|ws| match ws { - ProjectWorkspace::DetachedFile { file, .. } => Some(file.clone()), + .filter_map(|ws| match &ws.kind { + ProjectWorkspaceKind::DetachedFile { file, .. } => Some(file.clone()), _ => None, }) .collect(); @@ -666,33 +669,37 @@ impl GlobalState { config, None, self.config.root_path().clone(), + None, )], flycheck::InvocationStrategy::PerWorkspace => { self.workspaces .iter() .enumerate() - .filter_map(|(id, w)| match w { - ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some(( + .filter_map(|(id, ws)| { + Some(( id, - cargo.workspace_root(), - sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), - )), - ProjectWorkspace::Json { project, sysroot, .. } => { - // Enable flychecks for json projects if a custom flycheck command was supplied - // in the workspace configuration. - match config { - FlycheckConfig::CustomCommand { .. } => Some(( - id, - project.path(), - sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), - )), - _ => None, - } - } - // FIXME - ProjectWorkspace::DetachedFile { .. } => None, + match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _)), + .. + } => (cargo.workspace_root(), Some(cargo.manifest_path())), + ProjectWorkspaceKind::Json(project) => { + // Enable flychecks for json projects if a custom flycheck command was supplied + // in the workspace configuration. + match config { + FlycheckConfig::CustomCommand { .. } => { + (project.path(), None) + } + _ => return None, + } + } + ProjectWorkspaceKind::DetachedFile { .. } => return None, + }, + ws.sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), + )) }) - .map(|(id, root, sysroot_root)| { + .map(|(id, (root, manifest_path), sysroot_root)| { let sender = sender.clone(); FlycheckHandle::spawn( id, @@ -700,6 +707,7 @@ impl GlobalState { config.clone(), sysroot_root, root.to_path_buf(), + manifest_path.map(|it| it.to_path_buf()), ) }) .collect() @@ -729,9 +737,7 @@ pub fn ws_to_crate_graph( let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env); let num_layouts = layouts.len(); let num_toolchains = toolchains.len(); - let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; + let ProjectWorkspace { toolchain, target_layout, .. } = ws; let mapping = crate_graph.extend( other, diff --git a/crates/rust-analyzer/tests/crate_graph.rs b/crates/rust-analyzer/tests/crate_graph.rs index e833ea5e11..59b229cd06 100644 --- a/crates/rust-analyzer/tests/crate_graph.rs +++ b/crates/rust-analyzer/tests/crate_graph.rs @@ -1,7 +1,8 @@ use std::path::PathBuf; use project_model::{ - CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, Sysroot, WorkspaceBuildScripts, + CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, ProjectWorkspaceKind, Sysroot, + WorkspaceBuildScripts, }; use rust_analyzer::ws_to_crate_graph; use rustc_hash::FxHashMap; @@ -13,16 +14,18 @@ fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace { let manifest_path = ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot: Ok(get_fake_sysroot()), - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), } } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 2ca86bc50a..5a1397bbb0 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -150,7 +150,7 @@ use dependency2::Spam; ) .with_config(serde_json::json!({ "cargo": { "sysroot": null }, - "detachedFiles": ["/src/lib.rs"], + "linkedProjects": ["src/lib.rs"], })) .server() .wait_until_workspace_is_loaded();