aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/cargo_workspace.rs23
-rw-r--r--crates/project_model/src/workspace.rs138
2 files changed, 110 insertions, 51 deletions
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index f7241b711..bc6e20341 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -9,6 +9,8 @@ use cargo_metadata::{CargoOpt, MetadataCommand};
9use la_arena::{Arena, Idx}; 9use la_arena::{Arena, Idx};
10use paths::{AbsPath, AbsPathBuf}; 10use paths::{AbsPath, AbsPathBuf};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12use serde::Deserialize;
13use serde_json::from_value;
12 14
13use crate::build_data::BuildDataConfig; 15use crate::build_data::BuildDataConfig;
14use crate::utf8_stdout; 16use crate::utf8_stdout;
@@ -104,6 +106,13 @@ pub struct PackageData {
104 pub active_features: Vec<String>, 106 pub active_features: Vec<String>,
105 // String representation of package id 107 // String representation of package id
106 pub id: String, 108 pub id: String,
109 // The contents of [package.metadata.rust-analyzer]
110 pub metadata: RustAnalyzerPackageMetaData,
111}
112
113#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
114pub struct RustAnalyzerPackageMetaData {
115 pub rustc_private: bool,
107} 116}
108 117
109#[derive(Debug, Clone, Eq, PartialEq)] 118#[derive(Debug, Clone, Eq, PartialEq)]
@@ -161,6 +170,13 @@ impl PackageData {
161 } 170 }
162} 171}
163 172
173#[derive(Deserialize, Default)]
174// Deserialise helper for the cargo metadata
175struct PackageMetadata {
176 #[serde(rename = "rust-analyzer")]
177 rust_analyzer: Option<RustAnalyzerPackageMetaData>,
178}
179
164impl CargoWorkspace { 180impl CargoWorkspace {
165 pub fn from_cargo_metadata( 181 pub fn from_cargo_metadata(
166 cargo_toml: &AbsPath, 182 cargo_toml: &AbsPath,
@@ -244,8 +260,10 @@ impl CargoWorkspace {
244 260
245 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 261 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
246 for meta_pkg in &meta.packages { 262 for meta_pkg in &meta.packages {
247 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 263 let cargo_metadata::Package {
248 meta_pkg; 264 id, edition, name, manifest_path, version, metadata, ..
265 } = meta_pkg;
266 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
249 let is_member = ws_members.contains(&id); 267 let is_member = ws_members.contains(&id);
250 let edition = edition 268 let edition = edition
251 .parse::<Edition>() 269 .parse::<Edition>()
@@ -262,6 +280,7 @@ impl CargoWorkspace {
262 dependencies: Vec::new(), 280 dependencies: Vec::new(),
263 features: meta_pkg.features.clone().into_iter().collect(), 281 features: meta_pkg.features.clone().into_iter().collect(),
264 active_features: Vec::new(), 282 active_features: Vec::new(),
283 metadata: meta.rust_analyzer.unwrap_or_default(),
265 }); 284 });
266 let pkg_data = &mut packages[pkg]; 285 let pkg_data = &mut packages[pkg];
267 pkg_by_id.insert(id, pkg); 286 pkg_by_id.insert(id, pkg);
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 0220efdb4..1b53fcc30 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -2,11 +2,7 @@
2//! metadata` or `rust-project.json`) into representation stored in the salsa 2//! metadata` or `rust-project.json`) into representation stored in the salsa
3//! database -- `CrateGraph`. 3//! database -- `CrateGraph`.
4 4
5use std::{ 5use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6 fmt, fs,
7 path::{Component, Path},
8 process::Command,
9};
10 6
11use anyhow::{Context, Result}; 7use anyhow::{Context, Result};
12use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace {
60 match self { 56 match self {
61 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f 57 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
62 .debug_struct("Cargo") 58 .debug_struct("Cargo")
59 .field("root", &cargo.workspace_root().file_name())
63 .field("n_packages", &cargo.packages().len()) 60 .field("n_packages", &cargo.packages().len())
64 .field("n_sysroot_crates", &sysroot.crates().len()) 61 .field("n_sysroot_crates", &sysroot.crates().len())
65 .field( 62 .field(
@@ -279,11 +276,8 @@ impl ProjectWorkspace {
279 276
280 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { 277 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
281 match self { 278 match self {
282 ProjectWorkspace::Cargo { cargo, rustc, .. } => { 279 ProjectWorkspace::Cargo { cargo, .. } => {
283 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); 280 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
284 if let Some(rustc) = rustc {
285 collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone());
286 }
287 } 281 }
288 _ => {} 282 _ => {}
289 } 283 }
@@ -380,9 +374,11 @@ fn cargo_to_crate_graph(
380 cfg_options.insert_atom("debug_assertions".into()); 374 cfg_options.insert_atom("debug_assertions".into());
381 375
382 let mut pkg_crates = FxHashMap::default(); 376 let mut pkg_crates = FxHashMap::default();
383 377 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
378 let mut has_private = false;
384 // Next, create crates for each package, target pair 379 // Next, create crates for each package, target pair
385 for pkg in cargo.packages() { 380 for pkg in cargo.packages() {
381 has_private |= cargo[pkg].metadata.rustc_private;
386 let mut lib_tgt = None; 382 let mut lib_tgt = None;
387 for &tgt in cargo[pkg].targets.iter() { 383 for &tgt in cargo[pkg].targets.iter() {
388 if let Some(file_id) = load(&cargo[tgt].root) { 384 if let Some(file_id) = load(&cargo[tgt].root) {
@@ -443,28 +439,66 @@ fn cargo_to_crate_graph(
443 } 439 }
444 } 440 }
445 441
446 let mut rustc_pkg_crates = FxHashMap::default(); 442 if has_private {
443 // If the user provided a path to rustc sources, we add all the rustc_private crates
444 // and create dependencies on them for the crates which opt-in to that
445 if let Some(rustc_workspace) = rustc {
446 handle_rustc_crates(
447 rustc_workspace,
448 load,
449 &mut crate_graph,
450 rustc_build_data_map,
451 &cfg_options,
452 proc_macro_loader,
453 &mut pkg_to_lib_crate,
454 &public_deps,
455 cargo,
456 &pkg_crates,
457 );
458 }
459 }
460 crate_graph
461}
447 462
448 // If the user provided a path to rustc sources, we add all the rustc_private crates 463fn handle_rustc_crates(
449 // and create dependencies on them for the crates in the current workspace 464 rustc_workspace: &CargoWorkspace,
450 if let Some(rustc_workspace) = rustc { 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
451 for pkg in rustc_workspace.packages() { 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>,
468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
471 public_deps: &[(CrateName, CrateId)],
472 cargo: &CargoWorkspace,
473 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>,
474) {
475 let mut rustc_pkg_crates = FxHashMap::default();
476 // The root package of the rustc-dev component is rustc_driver, so we match that
477 let root_pkg =
478 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
479 // The rustc workspace might be incomplete (such as if rustc-dev is not
480 // installed for the current toolchain) and `rustcSource` is set to discover.
481 if let Some(root_pkg) = root_pkg {
482 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
483 let mut queue = VecDeque::new();
484 queue.push_back(root_pkg);
485 while let Some(pkg) = queue.pop_front() {
486 // Don't duplicate packages if they are dependended on a diamond pattern
487 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
488 // which is not ideal
489 if rustc_pkg_crates.contains_key(&pkg) {
490 continue;
491 }
492 for dep in &rustc_workspace[pkg].dependencies {
493 queue.push_back(dep.pkg);
494 }
452 for &tgt in rustc_workspace[pkg].targets.iter() { 495 for &tgt in rustc_workspace[pkg].targets.iter() {
453 if rustc_workspace[tgt].kind != TargetKind::Lib { 496 if rustc_workspace[tgt].kind != TargetKind::Lib {
454 continue; 497 continue;
455 } 498 }
456 // Exclude alloc / core / std
457 if rustc_workspace[tgt]
458 .root
459 .components()
460 .any(|c| c == Component::Normal("library".as_ref()))
461 {
462 continue;
463 }
464
465 if let Some(file_id) = load(&rustc_workspace[tgt].root) { 499 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
466 let crate_id = add_target_crate_root( 500 let crate_id = add_target_crate_root(
467 &mut crate_graph, 501 crate_graph,
468 &rustc_workspace[pkg], 502 &rustc_workspace[pkg],
469 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), 503 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
470 &cfg_options, 504 &cfg_options,
@@ -472,44 +506,50 @@ fn cargo_to_crate_graph(
472 file_id, 506 file_id,
473 ); 507 );
474 pkg_to_lib_crate.insert(pkg, crate_id); 508 pkg_to_lib_crate.insert(pkg, crate_id);
475 // Add dependencies on the core / std / alloc for rustc 509 // Add dependencies on core / std / alloc for this crate
476 for (name, krate) in public_deps.iter() { 510 for (name, krate) in public_deps.iter() {
477 add_dep(&mut crate_graph, crate_id, name.clone(), *krate); 511 add_dep(crate_graph, crate_id, name.clone(), *krate);
478 } 512 }
479 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); 513 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
480 } 514 }
481 } 515 }
482 } 516 }
483 // Now add a dep edge from all targets of upstream to the lib 517 }
484 // target of downstream. 518 // Now add a dep edge from all targets of upstream to the lib
485 for pkg in rustc_workspace.packages() { 519 // target of downstream.
486 for dep in rustc_workspace[pkg].dependencies.iter() { 520 for pkg in rustc_pkg_crates.keys().copied() {
487 let name = CrateName::new(&dep.name).unwrap(); 521 for dep in rustc_workspace[pkg].dependencies.iter() {
488 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 522 let name = CrateName::new(&dep.name).unwrap();
489 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 523 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
490 add_dep(&mut crate_graph, from, name.clone(), to); 524 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
491 } 525 add_dep(crate_graph, from, name.clone(), to);
492 } 526 }
493 } 527 }
494 } 528 }
495 529 }
496 // Add dependencies for all the crates of the current workspace to rustc_private libraries 530 // Add a dependency on the rustc_private crates for all targets of each package
497 for dep in rustc_workspace.packages() { 531 // which opts in
498 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 532 for dep in rustc_workspace.packages() {
499 533 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
500 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 534
501 for pkg in cargo.packages() { 535 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
502 if !cargo[pkg].is_member { 536 for pkg in cargo.packages() {
503 continue; 537 let package = &cargo[pkg];
504 } 538 if !package.metadata.rustc_private {
505 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 539 continue;
506 add_dep(&mut crate_graph, from, name.clone(), to); 540 }
541 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
542 // Avoid creating duplicate dependencies
543 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
544 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
545 // instead of the one from `crates.io`
546 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) {
547 add_dep(crate_graph, from, name.clone(), to);
507 } 548 }
508 } 549 }
509 } 550 }
510 } 551 }
511 } 552 }
512 crate_graph
513} 553}
514 554
515fn add_target_crate_root( 555fn add_target_crate_root(