aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/project_model/src/workspace.rs170
1 files changed, 98 insertions, 72 deletions
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index b969420cf..b2ca7a4d2 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -376,6 +376,7 @@ fn cargo_to_crate_graph(
376 cfg_options.insert_atom("debug_assertions".into()); 376 cfg_options.insert_atom("debug_assertions".into());
377 377
378 let mut pkg_crates = FxHashMap::default(); 378 let mut pkg_crates = FxHashMap::default();
379 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
379 let mut has_private = false; 380 let mut has_private = false;
380 // Next, create crates for each package, target pair 381 // Next, create crates for each package, target pair
381 for pkg in cargo.packages() { 382 for pkg in cargo.packages() {
@@ -440,92 +441,117 @@ fn cargo_to_crate_graph(
440 } 441 }
441 } 442 }
442 443
443 let mut rustc_pkg_crates = FxHashMap::default();
444
445 if has_private { 444 if has_private {
446 // If the user provided a path to rustc sources, we add all the rustc_private crates 445 // If the user provided a path to rustc sources, we add all the rustc_private crates
447 // and create dependencies on them for the crates which opt-in to that 446 // and create dependencies on them for the crates which opt-in to that
448 if let Some(rustc_workspace) = rustc { 447 if let Some(rustc_workspace) = rustc {
449 // rustc-dev crates start from 'rustc_driver' 448 handle_rustc_crates(
450 // We want to collect all crates which are transitive dependencies of rustc_driver 449 rustc_workspace,
451 if let Some(root_pkg) = rustc_workspace 450 load,
452 .packages() 451 &mut crate_graph,
453 .find(|package| rustc_workspace[*package].name == "rustc_driver") 452 rustc_build_data_map,
454 { 453 &cfg_options,
455 let mut queue = VecDeque::new(); 454 proc_macro_loader,
456 queue.push_back(root_pkg); 455 &mut pkg_to_lib_crate,
457 while let Some(pkg) = queue.pop_front() { 456 &public_deps,
458 // Don't duplicate packages 457 cargo,
459 if rustc_pkg_crates.contains_key(&pkg) { 458 &pkg_crates,
460 continue; 459 );
461 } 460 }
462 for dep in &rustc_workspace[pkg].dependencies { 461 }
463 queue.push_back(dep.pkg); 462 crate_graph
464 } 463}
465 for &tgt in rustc_workspace[pkg].targets.iter() { 464
466 if rustc_workspace[tgt].kind != TargetKind::Lib { 465fn handle_rustc_crates(
467 continue; 466 rustc_workspace: &CargoWorkspace,
468 } 467 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
469 if let Some(file_id) = load(&rustc_workspace[tgt].root) { 468 crate_graph: &mut CrateGraph,
470 let crate_id = add_target_crate_root( 469 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>,
471 &mut crate_graph, 470 cfg_options: &CfgOptions,
472 &rustc_workspace[pkg], 471 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
473 rustc_build_data_map 472 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
474 .and_then(|it| it.get(&rustc_workspace[pkg].id)), 473 public_deps: &[(CrateName, CrateId)],
475 &cfg_options, 474 cargo: &CargoWorkspace,
476 proc_macro_loader, 475 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>,
477 file_id, 476) {
478 ); 477 let mut rustc_pkg_crates = FxHashMap::default();
479 pkg_to_lib_crate.insert(pkg, crate_id); 478 // The root package of the rustc-dev component is rustc_driver, so we match that
480 // Add dependencies on the core / std / alloc for rustc 479 let root_pkg =
481 for (name, krate) in public_deps.iter() { 480 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
482 add_dep(&mut crate_graph, crate_id, name.clone(), *krate); 481 // The rustc workspace might be incomplete (such as if rustc-dev is not installed for the current toolchain)
483 } 482 // and `rustcSource` is set to discover.
484 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); 483 if let Some(root_pkg) = root_pkg {
485 } 484 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
485 let mut queue = VecDeque::new();
486 queue.push_back(root_pkg);
487 while let Some(pkg) = queue.pop_front() {
488 // Don't duplicate packages if they are dependended on a diamond pattern
489 // N.B. if this line is ommitted, we try and analyse either 48_000 or 480_000 crates
490 // neither of which makes
491 if rustc_pkg_crates.contains_key(&pkg) {
492 continue;
493 }
494 for dep in &rustc_workspace[pkg].dependencies {
495 queue.push_back(dep.pkg);
496 }
497 for &tgt in rustc_workspace[pkg].targets.iter() {
498 if rustc_workspace[tgt].kind != TargetKind::Lib {
499 continue;
500 }
501 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
502 let crate_id = add_target_crate_root(
503 crate_graph,
504 &rustc_workspace[pkg],
505 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
506 &cfg_options,
507 proc_macro_loader,
508 file_id,
509 );
510 pkg_to_lib_crate.insert(pkg, crate_id);
511 // Add dependencies on core / std / alloc for this crate
512 for (name, krate) in public_deps.iter() {
513 add_dep(crate_graph, crate_id, name.clone(), *krate);
486 } 514 }
515 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
487 } 516 }
488 } 517 }
489 // Now add a dep edge from all targets of upstream to the lib 518 }
490 // target of downstream. 519 }
491 for pkg in rustc_pkg_crates.keys().copied() { 520 // Now add a dep edge from all targets of upstream to the lib
492 for dep in rustc_workspace[pkg].dependencies.iter() { 521 // target of downstream.
493 let name = CrateName::new(&dep.name).unwrap(); 522 for pkg in rustc_pkg_crates.keys().copied() {
494 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 523 for dep in rustc_workspace[pkg].dependencies.iter() {
495 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 524 let name = CrateName::new(&dep.name).unwrap();
496 add_dep(&mut crate_graph, from, name.clone(), to); 525 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
497 } 526 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
498 } 527 add_dep(crate_graph, from, name.clone(), to);
499 } 528 }
500 } 529 }
501 530 }
502 // Add dependencies for all crates which opt in to rustc_private libraries 531 }
503 for dep in rustc_workspace.packages() { 532 // Add a dependency on the rustc_private crates for all targets of each package
504 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 533 // which opts in
505 534 for dep in rustc_workspace.packages() {
506 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 535 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
507 for pkg in cargo.packages() { 536
508 let package = &cargo[pkg]; 537 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
509 if !package.metadata.rustc_private { 538 for pkg in cargo.packages() {
510 continue; 539 let package = &cargo[pkg];
511 } 540 if !package.metadata.rustc_private {
512 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 541 continue;
513 // Avoid creating duplicate dependencies 542 }
514 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { 543 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
515 add_dep(&mut crate_graph, from, name.clone(), to); 544 // Avoid creating duplicate dependencies
516 } else { 545 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
517 eprintln!( 546 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
518 "Skipped {} for {:?}", 547 // instead of the one from `crates.io`
519 &name, &crate_graph[from].display_name 548 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) {
520 ); 549 add_dep(crate_graph, from, name.clone(), to);
521 }
522 }
523 } 550 }
524 } 551 }
525 } 552 }
526 } 553 }
527 } 554 }
528 crate_graph
529} 555}
530 556
531fn add_target_crate_root( 557fn add_target_crate_root(