]> git.lizzy.rs Git - rust.git/blobdiff - crates/project_model/src/workspace.rs
Don't load auxiliary crates outside the workspace
[rust.git] / crates / project_model / src / workspace.rs
index d911209b075b6818ad9cd525cc17d39fabc5adc4..96522bf070a4ccc1dddef77d689bb985d5d0b423 100644 (file)
@@ -2,10 +2,13 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
-use std::{collections::VecDeque, convert::TryFrom, fmt, fs, process::Command};
+use std::{collections::VecDeque, fmt, fs, process::Command};
 
 use anyhow::{format_err, Context, Result};
-use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
+use base_db::{
+    CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
+    FileId, ProcMacro,
+};
 use cfg::{CfgDiff, CfgOptions};
 use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
     TargetKind, WorkspaceBuildScripts,
 };
 
-pub type CfgOverrides = FxHashMap<String, CfgDiff>;
+/// A set of cfg-overrides per crate.
+///
+/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
+/// without having to first obtain a list of all crates.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum CfgOverrides {
+    /// A single global set of overrides matching all crates.
+    Wildcard(CfgDiff),
+    /// A set of overrides matching specific crates.
+    Selective(FxHashMap<String, CfgDiff>),
+}
+
+impl Default for CfgOverrides {
+    fn default() -> Self {
+        Self::Selective(FxHashMap::default())
+    }
+}
+
+impl CfgOverrides {
+    pub fn len(&self) -> usize {
+        match self {
+            CfgOverrides::Wildcard(_) => 1,
+            CfgOverrides::Selective(hash_map) => hash_map.len(),
+        }
+    }
+}
 
 /// `PackageRoot` describes a package root folder.
 /// Which may be an external dependency, or a member of
 /// the current workspace.
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct PackageRoot {
-    /// Is a member of the current workspace
-    pub is_member: bool,
+    /// Is from the local filesystem and may be edited
+    pub is_local: bool,
     pub include: Vec<AbsPathBuf>,
     pub exclude: Vec<AbsPathBuf>,
 }
@@ -133,14 +161,19 @@ pub fn load(
                     cmd
                 })?;
 
-                let meta = CargoWorkspace::fetch_metadata(&cargo_toml, config, progress)
-                    .with_context(|| {
-                        format!(
-                            "Failed to read Cargo metadata from Cargo.toml file {}, {}",
-                            cargo_toml.display(),
-                            cargo_version
-                        )
-                    })?;
+                let meta = CargoWorkspace::fetch_metadata(
+                    &cargo_toml,
+                    cargo_toml.parent(),
+                    config,
+                    progress,
+                )
+                .with_context(|| {
+                    format!(
+                        "Failed to read Cargo metadata from Cargo.toml file {}, {}",
+                        cargo_toml.display(),
+                        cargo_version
+                    )
+                })?;
                 let cargo = CargoWorkspace::new(meta);
 
                 let sysroot = if config.no_sysroot {
@@ -162,10 +195,15 @@ pub fn load(
 
                 let rustc = match rustc_dir {
                     Some(rustc_dir) => Some({
-                        let meta = CargoWorkspace::fetch_metadata(&rustc_dir, config, progress)
-                            .with_context(|| {
-                                format!("Failed to read Cargo metadata for Rust sources")
-                            })?;
+                        let meta = CargoWorkspace::fetch_metadata(
+                            &rustc_dir,
+                            cargo_toml.parent(),
+                            config,
+                            progress,
+                        )
+                        .with_context(|| {
+                            "Failed to read Cargo metadata for Rust sources".to_string()
+                        })?;
                         CargoWorkspace::new(meta)
                     }),
                     None => None,
@@ -243,7 +281,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
             ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
                 .crates()
                 .map(|(_, krate)| PackageRoot {
-                    is_member: krate.is_workspace_member,
+                    is_local: krate.is_workspace_member,
                     include: krate.include.clone(),
                     exclude: krate.exclude.clone(),
                 })
@@ -251,7 +289,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                 .into_iter()
                 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
                     sysroot.crates().map(move |krate| PackageRoot {
-                        is_member: false,
+                        is_local: false,
                         include: vec![sysroot[krate].root.parent().to_path_buf()],
                         exclude: Vec::new(),
                     })
@@ -268,7 +306,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                 cargo
                     .packages()
                     .map(|pkg| {
-                        let is_member = cargo[pkg].is_member;
+                        let is_local = cargo[pkg].is_local;
                         let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
 
                         let mut include = vec![pkg_root.clone()];
@@ -294,23 +332,23 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                         include.extend(extra_targets);
 
                         let mut exclude = vec![pkg_root.join(".git")];
-                        if is_member {
+                        if is_local {
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
                             exclude.push(pkg_root.join("examples"));
                             exclude.push(pkg_root.join("benches"));
                         }
-                        PackageRoot { is_member, include, exclude }
+                        PackageRoot { is_local, include, exclude }
                     })
-                    .chain(sysroot.into_iter().map(|sysroot| PackageRoot {
-                        is_member: false,
+                    .chain(sysroot.iter().map(|sysroot| PackageRoot {
+                        is_local: false,
                         include: vec![sysroot.root().to_path_buf()],
                         exclude: Vec::new(),
                     }))
-                    .chain(rustc.into_iter().flat_map(|rustc| {
+                    .chain(rustc.iter().flat_map(|rustc| {
                         rustc.packages().map(move |krate| PackageRoot {
-                            is_member: false,
+                            is_local: false,
                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
                             exclude: Vec::new(),
                         })
@@ -318,14 +356,14 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                     .collect()
             }
             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
-                .into_iter()
+                .iter()
                 .map(|detached_file| PackageRoot {
-                    is_member: true,
+                    is_local: true,
                     include: vec![detached_file.clone()],
                     exclude: Vec::new(),
                 })
                 .chain(sysroot.crates().map(|krate| PackageRoot {
-                    is_member: false,
+                    is_local: false,
                     include: vec![sysroot[krate].root.parent().to_path_buf()],
                     exclude: Vec::new(),
                 }))
@@ -349,10 +387,14 @@ pub fn n_packages(&self) -> usize {
 
     pub fn to_crate_graph(
         &self,
-        load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
+        dummy_replace: &FxHashMap<Box<str>, Box<[Box<str>]>>,
+        load_proc_macro: &mut dyn FnMut(&AbsPath, &[Box<str>]) -> Vec<ProcMacro>,
         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     ) -> CrateGraph {
         let _p = profile::span("ProjectWorkspace::to_crate_graph");
+        let load_proc_macro = &mut |crate_name: &_, path: &_| {
+            load_proc_macro(path, dummy_replace.get(crate_name).map(|it| &**it).unwrap_or_default())
+        };
 
         let mut crate_graph = match self {
             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
@@ -384,9 +426,9 @@ pub fn to_crate_graph(
             }
         };
         if crate_graph.patch_cfg_if() {
-            log::debug!("Patched std to depend on cfg-if")
+            tracing::debug!("Patched std to depend on cfg-if")
         } else {
-            log::debug!("Did not patch std to depend on cfg-if")
+            tracing::debug!("Did not patch std to depend on cfg-if")
         }
         crate_graph
     }
@@ -394,7 +436,7 @@ pub fn to_crate_graph(
 
 fn project_json_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
-    load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     project: &ProjectJson,
     sysroot: &Option<Sysroot>,
@@ -414,7 +456,12 @@ fn project_json_to_crate_graph(
         })
         .map(|(crate_id, krate, file_id)| {
             let env = krate.env.clone().into_iter().collect();
-            let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
+            let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| {
+                load_proc_macro(
+                    krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
+                    &it,
+                )
+            });
 
             let target_cfgs = match krate.target.as_deref() {
                 Some(target) => {
@@ -431,10 +478,16 @@ fn project_json_to_crate_graph(
                     file_id,
                     krate.edition,
                     krate.display_name.clone(),
+                    krate.version.clone(),
                     cfg_options.clone(),
                     cfg_options,
                     env,
                     proc_macro.unwrap_or_default(),
+                    if krate.display_name.is_some() {
+                        CrateOrigin::CratesIo { repo: krate.repository.clone() }
+                    } else {
+                        CrateOrigin::Unknown
+                    },
                 ),
             )
         })
@@ -443,9 +496,7 @@ fn project_json_to_crate_graph(
     for (from, krate) in project.crates() {
         if let Some(&from) = crates.get(&from) {
             if let Some((public_deps, libproc_macro)) = &sysroot_deps {
-                for (name, to) in public_deps.iter() {
-                    add_dep(&mut crate_graph, from, name.clone(), *to)
-                }
+                public_deps.add(from, &mut crate_graph);
                 if krate.is_proc_macro {
                     if let Some(proc_macro) = libproc_macro {
                         add_dep(
@@ -471,7 +522,7 @@ fn project_json_to_crate_graph(
 fn cargo_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
     override_cfg: &CfgOverrides,
-    load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     cargo: &CargoWorkspace,
     build_scripts: &WorkspaceBuildScripts,
@@ -482,7 +533,7 @@ fn cargo_to_crate_graph(
     let mut crate_graph = CrateGraph::default();
     let (public_deps, libproc_macro) = match sysroot {
         Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
-        None => (Vec::new(), None),
+        None => (SysrootPublicDeps::default(), None),
     };
 
     let mut cfg_options = CfgOptions::default();
@@ -501,7 +552,13 @@ fn cargo_to_crate_graph(
     for pkg in cargo.packages() {
         let mut cfg_options = &cfg_options;
         let mut replaced_cfg_options;
-        if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
+
+        let overrides = match override_cfg {
+            CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
+            CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
+        };
+
+        if let Some(overrides) = overrides {
             // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
             // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
             // working on rust-lang/rust as that's the only time it appears outside sysroot).
@@ -517,13 +574,22 @@ fn cargo_to_crate_graph(
         has_private |= cargo[pkg].metadata.rustc_private;
         let mut lib_tgt = None;
         for &tgt in cargo[pkg].targets.iter() {
+            if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
+                // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
+                // add any targets except the library target, since those will not work correctly if
+                // they use dev-dependencies.
+                // In fact, they can break quite badly if multiple client workspaces get merged:
+                // https://github.com/rust-analyzer/rust-analyzer/issues/11300
+                continue;
+            }
+
             if let Some(file_id) = load(&cargo[tgt].root) {
                 let crate_id = add_target_crate_root(
                     &mut crate_graph,
                     &cargo[pkg],
                     build_scripts.outputs.get(pkg),
-                    &cfg_options,
-                    load_proc_macro,
+                    cfg_options,
+                    &mut |path| load_proc_macro(&cargo[tgt].name, path),
                     file_id,
                     &cargo[tgt].name,
                 );
@@ -532,11 +598,12 @@ fn cargo_to_crate_graph(
                     pkg_to_lib_crate.insert(pkg, crate_id);
                 }
                 if let Some(proc_macro) = libproc_macro {
-                    add_dep(
+                    add_dep_with_prelude(
                         &mut crate_graph,
                         crate_id,
                         CrateName::new("proc_macro").unwrap(),
                         proc_macro,
+                        cargo[tgt].is_proc_macro,
                     );
                 }
 
@@ -546,6 +613,9 @@ fn cargo_to_crate_graph(
 
         // Set deps to the core, std and to the lib target of the current package
         for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
+            // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
+            public_deps.add(*from, &mut crate_graph);
+
             if let Some((to, name)) = lib_tgt.clone() {
                 if to != *from && *kind != TargetKind::BuildScript {
                     // (build script can not depend on its library target)
@@ -557,9 +627,6 @@ fn cargo_to_crate_graph(
                     add_dep(&mut crate_graph, *from, name, to);
                 }
             }
-            for (name, krate) in public_deps.iter() {
-                add_dep(&mut crate_graph, *from, name.clone(), *krate);
-            }
         }
     }
 
@@ -623,7 +690,7 @@ fn detached_files_to_crate_graph(
         let file_id = match load(detached_file) {
             Some(file_id) => file_id,
             None => {
-                log::error!("Failed to load detached file {:?}", detached_file);
+                tracing::error!("Failed to load detached file {:?}", detached_file);
                 continue;
             }
         };
@@ -635,15 +702,15 @@ fn detached_files_to_crate_graph(
             file_id,
             Edition::CURRENT,
             display_name,
+            None,
             cfg_options.clone(),
             cfg_options.clone(),
             Env::default(),
             Vec::new(),
+            CrateOrigin::Unknown,
         );
 
-        for (name, krate) in public_deps.iter() {
-            add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
-        }
+        public_deps.add(detached_file_crate, &mut crate_graph);
     }
     crate_graph
 }
@@ -653,9 +720,9 @@ fn handle_rustc_crates(
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     crate_graph: &mut CrateGraph,
     cfg_options: &CfgOptions,
-    load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
+    load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
-    public_deps: &[(CrateName, CrateId)],
+    public_deps: &SysrootPublicDeps,
     cargo: &CargoWorkspace,
     pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
 ) {
@@ -689,15 +756,13 @@ fn handle_rustc_crates(
                         &rustc_workspace[pkg],
                         None,
                         cfg_options,
-                        load_proc_macro,
+                        &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
                         file_id,
                         &rustc_workspace[tgt].name,
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
                     // Add dependencies on core / std / alloc for this crate
-                    for (name, krate) in public_deps.iter() {
-                        add_dep(crate_graph, crate_id, name.clone(), *krate);
-                    }
+                    public_deps.add(crate_id, crate_graph);
                     rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
                 }
             }
@@ -783,18 +848,31 @@ fn add_target_crate_root(
             .iter()
             .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
     );
-
-    let crate_id = crate_graph.add_crate_root(
+    crate_graph.add_crate_root(
         file_id,
         edition,
         Some(display_name),
+        Some(pkg.version.to_string()),
         cfg_options,
         potential_cfg_options,
         env,
         proc_macro,
-    );
+        CrateOrigin::CratesIo { repo: pkg.repository.clone() },
+    )
+}
+
+#[derive(Default)]
+struct SysrootPublicDeps {
+    deps: Vec<(CrateName, CrateId, bool)>,
+}
 
-    crate_id
+impl SysrootPublicDeps {
+    /// Makes `from` depend on the public sysroot crates.
+    fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
+        for (name, krate, prelude) in &self.deps {
+            add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
+        }
+    }
 }
 
 fn sysroot_to_crate_graph(
@@ -802,7 +880,7 @@ fn sysroot_to_crate_graph(
     sysroot: &Sysroot,
     rustc_cfg: Vec<CfgFlag>,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
+) -> (SysrootPublicDeps, Option<CrateId>) {
     let _p = profile::span("sysroot_to_crate_graph");
     let mut cfg_options = CfgOptions::default();
     cfg_options.extend(rustc_cfg);
@@ -818,10 +896,12 @@ fn sysroot_to_crate_graph(
                 file_id,
                 Edition::CURRENT,
                 Some(display_name),
+                None,
                 cfg_options.clone(),
                 cfg_options.clone(),
                 env,
                 proc_macro,
+                CrateOrigin::Lang,
             );
             Some((krate, crate_id))
         })
@@ -836,18 +916,36 @@ fn sysroot_to_crate_graph(
         }
     }
 
-    let public_deps = sysroot
-        .public_deps()
-        .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
-        .collect::<Vec<_>>();
+    let public_deps = SysrootPublicDeps {
+        deps: sysroot
+            .public_deps()
+            .map(|(name, idx, prelude)| {
+                (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
+            })
+            .collect::<Vec<_>>(),
+    };
 
     let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
     (public_deps, libproc_macro)
 }
 
 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)
+    add_dep_inner(graph, from, Dependency::new(name, to))
+}
+
+fn add_dep_with_prelude(
+    graph: &mut CrateGraph,
+    from: CrateId,
+    name: CrateName,
+    to: CrateId,
+    prelude: bool,
+) {
+    add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
+}
+
+fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
+    if let Err(err) = graph.add_dep(from, dep) {
+        tracing::error!("{}", err)
     }
 }
 
@@ -862,27 +960,27 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) {
     // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
 
     let manifest_dir = package.manifest.parent();
-    env.set("CARGO_MANIFEST_DIR".into(), manifest_dir.as_os_str().to_string_lossy().into_owned());
+    env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
 
     // Not always right, but works for common cases.
-    env.set("CARGO".into(), "cargo".into());
+    env.set("CARGO", "cargo".into());
 
-    env.set("CARGO_PKG_VERSION".into(), package.version.to_string());
-    env.set("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string());
-    env.set("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string());
-    env.set("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string());
-    env.set("CARGO_PKG_VERSION_PRE".into(), package.version.pre.to_string());
+    env.set("CARGO_PKG_VERSION", package.version.to_string());
+    env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
+    env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
+    env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
+    env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
 
-    env.set("CARGO_PKG_AUTHORS".into(), String::new());
+    env.set("CARGO_PKG_AUTHORS", String::new());
 
-    env.set("CARGO_PKG_NAME".into(), package.name.clone());
+    env.set("CARGO_PKG_NAME", package.name.clone());
     // FIXME: This isn't really correct (a package can have many crates with different names), but
     // it's better than leaving the variable unset.
-    env.set("CARGO_CRATE_NAME".into(), CrateName::normalize_dashes(&package.name).to_string());
-    env.set("CARGO_PKG_DESCRIPTION".into(), String::new());
-    env.set("CARGO_PKG_HOMEPAGE".into(), String::new());
-    env.set("CARGO_PKG_REPOSITORY".into(), String::new());
-    env.set("CARGO_PKG_LICENSE".into(), String::new());
+    env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
+    env.set("CARGO_PKG_DESCRIPTION", String::new());
+    env.set("CARGO_PKG_HOMEPAGE", String::new());
+    env.set("CARGO_PKG_REPOSITORY", String::new());
+    env.set("CARGO_PKG_LICENSE", String::new());
 
-    env.set("CARGO_PKG_LICENSE_FILE".into(), String::new());
+    env.set("CARGO_PKG_LICENSE_FILE", String::new());
 }