|
2 | 2 | //! metadata` or `rust-project.json`) into representation stored in the salsa
|
3 | 3 | //! database -- `CrateGraph`.
|
4 | 4 |
|
5 |
| -use std::{ |
6 |
| - fmt, fs, |
7 |
| - path::{Component, Path}, |
8 |
| - process::Command, |
9 |
| -}; |
| 5 | +use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; |
10 | 6 |
|
11 | 7 | use anyhow::{Context, Result};
|
12 | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
|
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace {
|
60 | 56 | match self {
|
61 | 57 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
|
62 | 58 | .debug_struct("Cargo")
|
| 59 | + .field("root", &cargo.workspace_root().file_name()) |
63 | 60 | .field("n_packages", &cargo.packages().len())
|
64 | 61 | .field("n_sysroot_crates", &sysroot.crates().len())
|
65 | 62 | .field(
|
@@ -279,11 +276,8 @@ impl ProjectWorkspace {
|
279 | 276 |
|
280 | 277 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
|
281 | 278 | match self {
|
282 |
| - ProjectWorkspace::Cargo { cargo, rustc, .. } => { |
| 279 | + ProjectWorkspace::Cargo { cargo, .. } => { |
283 | 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 | 374 | cfg_options.insert_atom("debug_assertions".into());
|
381 | 375 |
|
382 | 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 | 379 | // Next, create crates for each package, target pair
|
385 | 380 | for pkg in cargo.packages() {
|
| 381 | + has_private |= cargo[pkg].metadata.rustc_private; |
386 | 382 | let mut lib_tgt = None;
|
387 | 383 | for &tgt in cargo[pkg].targets.iter() {
|
388 | 384 | if let Some(file_id) = load(&cargo[tgt].root) {
|
@@ -443,73 +439,117 @@ 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 |
449 |
| - // and create dependencies on them for the crates in the current workspace |
450 |
| - if let Some(rustc_workspace) = rustc { |
451 |
| - for pkg in rustc_workspace.packages() { |
| 463 | +fn handle_rustc_crates( |
| 464 | + rustc_workspace: &CargoWorkspace, |
| 465 | + load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
| 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 | 495 | for &tgt in rustc_workspace[pkg].targets.iter() {
|
453 | 496 | if rustc_workspace[tgt].kind != TargetKind::Lib {
|
454 | 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 | 499 | if let Some(file_id) = load(&rustc_workspace[tgt].root) {
|
466 | 500 | let crate_id = add_target_crate_root(
|
467 |
| - &mut crate_graph, |
| 501 | + crate_graph, |
468 | 502 | &rustc_workspace[pkg],
|
469 | 503 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
|
470 | 504 | &cfg_options,
|
471 | 505 | proc_macro_loader,
|
472 | 506 | file_id,
|
473 | 507 | );
|
474 | 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 | 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 | 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 |
484 |
| - // target of downstream. |
485 |
| - for pkg in rustc_workspace.packages() { |
486 |
| - for dep in rustc_workspace[pkg].dependencies.iter() { |
487 |
| - let name = CrateName::new(&dep.name).unwrap(); |
488 |
| - if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
489 |
| - for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { |
490 |
| - add_dep(&mut crate_graph, from, name.clone(), to); |
491 |
| - } |
| 517 | + } |
| 518 | + // Now add a dep edge from all targets of upstream to the lib |
| 519 | + // target of downstream. |
| 520 | + for pkg in rustc_pkg_crates.keys().copied() { |
| 521 | + for dep in rustc_workspace[pkg].dependencies.iter() { |
| 522 | + let name = CrateName::new(&dep.name).unwrap(); |
| 523 | + if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
| 524 | + for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { |
| 525 | + add_dep(crate_graph, from, name.clone(), to); |
492 | 526 | }
|
493 | 527 | }
|
494 | 528 | }
|
495 |
| - |
496 |
| - // Add dependencies for all the crates of the current workspace to rustc_private libraries |
497 |
| - for dep in rustc_workspace.packages() { |
498 |
| - let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); |
499 |
| - |
500 |
| - if let Some(&to) = pkg_to_lib_crate.get(&dep) { |
501 |
| - for pkg in cargo.packages() { |
502 |
| - if !cargo[pkg].is_member { |
503 |
| - continue; |
504 |
| - } |
505 |
| - for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
506 |
| - add_dep(&mut crate_graph, from, name.clone(), to); |
| 529 | + } |
| 530 | + // Add a dependency on the rustc_private crates for all targets of each package |
| 531 | + // which opts in |
| 532 | + for dep in rustc_workspace.packages() { |
| 533 | + let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); |
| 534 | + |
| 535 | + if let Some(&to) = pkg_to_lib_crate.get(&dep) { |
| 536 | + for pkg in cargo.packages() { |
| 537 | + let package = &cargo[pkg]; |
| 538 | + if !package.metadata.rustc_private { |
| 539 | + continue; |
| 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 | 555 | fn add_target_crate_root(
|
|
0 commit comments