diff options
Diffstat (limited to 'crates/project_model/src/workspace.rs')
-rw-r--r-- | crates/project_model/src/workspace.rs | 138 |
1 files changed, 89 insertions, 49 deletions
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 | ||
5 | use std::{ | 5 | use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; |
6 | fmt, fs, | ||
7 | path::{Component, Path}, | ||
8 | process::Command, | ||
9 | }; | ||
10 | 6 | ||
11 | use anyhow::{Context, Result}; | 7 | use anyhow::{Context, Result}; |
12 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use 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 | 463 | fn 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 | ||
515 | fn add_target_crate_root( | 555 | fn add_target_crate_root( |