//! 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};
/// 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>,
}
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 {
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,
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(),
})
.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(),
})
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()];
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(),
})
.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(),
}))
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(
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>,
})
.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) => {
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
+ },
),
)
})
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(
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,
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();
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,
);
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,
);
}
// 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)
add_dep(&mut crate_graph, *from, name, to);
}
}
- for (name, krate) in public_deps.iter() {
- add_dep(&mut crate_graph, *from, name.clone(), *krate);
- }
}
}
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
}
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)>>,
) {
&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);
}
}
.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(
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);
file_id,
Edition::CURRENT,
Some(display_name),
+ None,
cfg_options.clone(),
cfg_options.clone(),
env,
proc_macro,
+ CrateOrigin::Lang,
);
Some((krate, crate_id))
})
}
}
- 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)
}
}
// 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());
}