]> 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 5dc2e35fdab86f32f0ec5494beb3e6119c6dd17c..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};
@@ -158,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 {
@@ -187,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,
@@ -328,12 +341,12 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                         }
                         PackageRoot { is_local, include, exclude }
                     })
-                    .chain(sysroot.into_iter().map(|sysroot| PackageRoot {
+                    .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_local: false,
                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
@@ -343,7 +356,7 @@ pub fn to_roots(&self) -> Vec<PackageRoot> {
                     .collect()
             }
             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
-                .into_iter()
+                .iter()
                 .map(|detached_file| PackageRoot {
                     is_local: true,
                     include: vec![detached_file.clone()],
@@ -374,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(
@@ -419,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>,
@@ -439,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) => {
@@ -456,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
+                    },
                 ),
             )
         })
@@ -468,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(
@@ -496,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,
@@ -507,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();
@@ -548,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,
                 );
@@ -563,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,
                     );
                 }
 
@@ -577,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)
@@ -588,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);
-            }
         }
     }
 
@@ -666,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
 }
@@ -684,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)>>,
 ) {
@@ -720,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);
                 }
             }
@@ -814,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() },
+    )
+}
 
-    crate_id
+#[derive(Default)]
+struct SysrootPublicDeps {
+    deps: Vec<(CrateName, CrateId, bool)>,
+}
+
+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(
@@ -833,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);
@@ -849,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))
         })
@@ -867,17 +916,35 @@ 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) {
+    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)
     }
 }
@@ -893,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());
 }