diff options
Diffstat (limited to 'crates/project_model')
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 23 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 138 |
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}; | |||
9 | use la_arena::{Arena, Idx}; | 9 | use la_arena::{Arena, Idx}; |
10 | use paths::{AbsPath, AbsPathBuf}; | 10 | use paths::{AbsPath, AbsPathBuf}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
12 | use serde::Deserialize; | ||
13 | use serde_json::from_value; | ||
12 | 14 | ||
13 | use crate::build_data::BuildDataConfig; | 15 | use crate::build_data::BuildDataConfig; |
14 | use crate::utf8_stdout; | 16 | use 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)] | ||
114 | pub 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 | ||
175 | struct PackageMetadata { | ||
176 | #[serde(rename = "rust-analyzer")] | ||
177 | rust_analyzer: Option<RustAnalyzerPackageMetaData>, | ||
178 | } | ||
179 | |||
164 | impl CargoWorkspace { | 180 | impl 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 | ||
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( |