aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/project_model/src/cargo_workspace.rs3
-rw-r--r--crates/project_model/src/lib.rs271
-rw-r--r--crates/rust-analyzer/src/config.rs16
-rw-r--r--crates/rust-analyzer/src/reload.rs4
-rw-r--r--editors/code/package.json8
5 files changed, 233 insertions, 69 deletions
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index d5f6a4025..608a031d4 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -64,6 +64,9 @@ pub struct CargoConfig {
64 64
65 /// rustc target 65 /// rustc target
66 pub target: Option<String>, 66 pub target: Option<String>,
67
68 /// rustc private crate source
69 pub rustc_source: Option<AbsPathBuf>,
67} 70}
68 71
69pub type Package = Idx<PackageData>; 72pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index e92cfea59..4531b1928 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -9,6 +9,7 @@ use std::{
9 fmt, 9 fmt,
10 fs::{self, read_dir, ReadDir}, 10 fs::{self, read_dir, ReadDir},
11 io, 11 io,
12 path::Component,
12 process::Command, 13 process::Command,
13}; 14};
14 15
@@ -31,7 +32,7 @@ pub use proc_macro_api::ProcMacroClient;
31#[derive(Clone, Eq, PartialEq)] 32#[derive(Clone, Eq, PartialEq)]
32pub enum ProjectWorkspace { 33pub enum ProjectWorkspace {
33 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 34 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
34 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 35 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> },
35 /// Project workspace was manually specified using a `rust-project.json` file. 36 /// Project workspace was manually specified using a `rust-project.json` file.
36 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 37 Json { project: ProjectJson, sysroot: Option<Sysroot> },
37} 38}
@@ -39,10 +40,14 @@ pub enum ProjectWorkspace {
39impl fmt::Debug for ProjectWorkspace { 40impl fmt::Debug for ProjectWorkspace {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self { 42 match self {
42 ProjectWorkspace::Cargo { cargo, sysroot } => f 43 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f
43 .debug_struct("Cargo") 44 .debug_struct("Cargo")
44 .field("n_packages", &cargo.packages().len()) 45 .field("n_packages", &cargo.packages().len())
45 .field("n_sysroot_crates", &sysroot.crates().len()) 46 .field("n_sysroot_crates", &sysroot.crates().len())
47 .field(
48 "n_rustc_compiler_crates",
49 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
50 )
46 .finish(), 51 .finish(),
47 ProjectWorkspace::Json { project, sysroot } => { 52 ProjectWorkspace::Json { project, sysroot } => {
48 let mut debug_struct = f.debug_struct("Json"); 53 let mut debug_struct = f.debug_struct("Json");
@@ -200,7 +205,19 @@ impl ProjectWorkspace {
200 } else { 205 } else {
201 Sysroot::default() 206 Sysroot::default()
202 }; 207 };
203 ProjectWorkspace::Cargo { cargo, sysroot } 208
209 let rustc = if let Some(rustc_dir) = &cargo_config.rustc_source {
210 Some(
211 CargoWorkspace::from_cargo_metadata(&rustc_dir, cargo_config)
212 .with_context(|| {
213 format!("Failed to read Cargo metadata for Rust sources")
214 })?,
215 )
216 } else {
217 None
218 };
219
220 ProjectWorkspace::Cargo { cargo, sysroot, rustc }
204 } 221 }
205 }; 222 };
206 223
@@ -238,31 +255,43 @@ impl ProjectWorkspace {
238 }) 255 })
239 })) 256 }))
240 .collect::<Vec<_>>(), 257 .collect::<Vec<_>>(),
241 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 258 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
242 .packages() 259 let roots = cargo
243 .map(|pkg| { 260 .packages()
244 let is_member = cargo[pkg].is_member; 261 .map(|pkg| {
245 let pkg_root = cargo[pkg].root().to_path_buf(); 262 let is_member = cargo[pkg].is_member;
246 263 let pkg_root = cargo[pkg].root().to_path_buf();
247 let mut include = vec![pkg_root.clone()]; 264
248 include.extend(cargo[pkg].out_dir.clone()); 265 let mut include = vec![pkg_root.clone()];
249 266 include.extend(cargo[pkg].out_dir.clone());
250 let mut exclude = vec![pkg_root.join(".git")]; 267
251 if is_member { 268 let mut exclude = vec![pkg_root.join(".git")];
252 exclude.push(pkg_root.join("target")); 269 if is_member {
253 } else { 270 exclude.push(pkg_root.join("target"));
254 exclude.push(pkg_root.join("tests")); 271 } else {
255 exclude.push(pkg_root.join("examples")); 272 exclude.push(pkg_root.join("tests"));
256 exclude.push(pkg_root.join("benches")); 273 exclude.push(pkg_root.join("examples"));
257 } 274 exclude.push(pkg_root.join("benches"));
258 PackageRoot { is_member, include, exclude } 275 }
259 }) 276 PackageRoot { is_member, include, exclude }
260 .chain(sysroot.crates().map(|krate| PackageRoot { 277 })
261 is_member: false, 278 .chain(sysroot.crates().map(|krate| PackageRoot {
262 include: vec![sysroot[krate].root_dir().to_path_buf()], 279 is_member: false,
263 exclude: Vec::new(), 280 include: vec![sysroot[krate].root_dir().to_path_buf()],
264 })) 281 exclude: Vec::new(),
265 .collect(), 282 }));
283 if let Some(rustc_packages) = rustc {
284 roots
285 .chain(rustc_packages.packages().map(|krate| PackageRoot {
286 is_member: false,
287 include: vec![rustc_packages[krate].root().to_path_buf()],
288 exclude: Vec::new(),
289 }))
290 .collect()
291 } else {
292 roots.collect()
293 }
294 }
266 } 295 }
267 } 296 }
268 297
@@ -273,7 +302,7 @@ impl ProjectWorkspace {
273 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref()) 302 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
274 .cloned() 303 .cloned()
275 .collect(), 304 .collect(),
276 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo 305 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot, rustc: _rustc_crates } => cargo
277 .packages() 306 .packages()
278 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref()) 307 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
279 .cloned() 308 .cloned()
@@ -284,8 +313,9 @@ impl ProjectWorkspace {
284 pub fn n_packages(&self) -> usize { 313 pub fn n_packages(&self) -> usize {
285 match self { 314 match self {
286 ProjectWorkspace::Json { project, .. } => project.n_crates(), 315 ProjectWorkspace::Json { project, .. } => project.n_crates(),
287 ProjectWorkspace::Cargo { cargo, sysroot } => { 316 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
288 cargo.packages().len() + sysroot.crates().len() 317 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
318 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
289 } 319 }
290 } 320 }
291 } 321 }
@@ -365,7 +395,7 @@ impl ProjectWorkspace {
365 } 395 }
366 } 396 }
367 } 397 }
368 ProjectWorkspace::Cargo { cargo, sysroot } => { 398 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
369 let (public_deps, libproc_macro) = 399 let (public_deps, libproc_macro) =
370 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 400 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
371 401
@@ -373,50 +403,25 @@ impl ProjectWorkspace {
373 cfg_options.extend(get_rustc_cfg_options(target)); 403 cfg_options.extend(get_rustc_cfg_options(target));
374 404
375 let mut pkg_to_lib_crate = FxHashMap::default(); 405 let mut pkg_to_lib_crate = FxHashMap::default();
376 let mut pkg_crates = FxHashMap::default();
377 406
378 // Add test cfg for non-sysroot crates 407 // Add test cfg for non-sysroot crates
379 cfg_options.insert_atom("test".into()); 408 cfg_options.insert_atom("test".into());
380 cfg_options.insert_atom("debug_assertions".into()); 409 cfg_options.insert_atom("debug_assertions".into());
381 410
411 let mut pkg_crates = FxHashMap::default();
412
382 // Next, create crates for each package, target pair 413 // Next, create crates for each package, target pair
383 for pkg in cargo.packages() { 414 for pkg in cargo.packages() {
384 let mut lib_tgt = None; 415 let mut lib_tgt = None;
385 for &tgt in cargo[pkg].targets.iter() { 416 for &tgt in cargo[pkg].targets.iter() {
386 let root = cargo[tgt].root.as_path(); 417 if let Some(crate_id) = add_target_crate_root(
387 if let Some(file_id) = load(root) { 418 &mut crate_graph,
388 let edition = cargo[pkg].edition; 419 &cargo[pkg],
389 let cfg_options = { 420 &cargo[tgt],
390 let mut opts = cfg_options.clone(); 421 &cfg_options,
391 for feature in cargo[pkg].features.iter() { 422 proc_macro_client,
392 opts.insert_key_value("feature".into(), feature.into()); 423 load,
393 } 424 ) {
394 opts.extend(cargo[pkg].cfgs.iter().cloned());
395 opts
396 };
397 let mut env = Env::default();
398 if let Some(out_dir) = &cargo[pkg].out_dir {
399 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
400 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
401 env.set("OUT_DIR", out_dir);
402 }
403 }
404 let proc_macro = cargo[pkg]
405 .proc_macro_dylib_path
406 .as_ref()
407 .map(|it| proc_macro_client.by_dylib_path(&it))
408 .unwrap_or_default();
409
410 let display_name =
411 CrateDisplayName::from_canonical_name(cargo[pkg].name.clone());
412 let crate_id = crate_graph.add_crate_root(
413 file_id,
414 edition,
415 Some(display_name),
416 cfg_options,
417 env,
418 proc_macro.clone(),
419 );
420 if cargo[tgt].kind == TargetKind::Lib { 425 if cargo[tgt].kind == TargetKind::Lib {
421 lib_tgt = Some((crate_id, cargo[tgt].name.clone())); 426 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
422 pkg_to_lib_crate.insert(pkg, crate_id); 427 pkg_to_lib_crate.insert(pkg, crate_id);
@@ -484,6 +489,92 @@ impl ProjectWorkspace {
484 } 489 }
485 } 490 }
486 } 491 }
492
493 let mut rustc_pkg_crates = FxHashMap::default();
494
495 // If the user provided a path to rustc sources, we add all the rustc_private crates
496 // and create dependencies on them for the crates in the current workspace
497 if let Some(rustc_workspace) = rustc {
498 for pkg in rustc_workspace.packages() {
499 for &tgt in rustc_workspace[pkg].targets.iter() {
500 if rustc_workspace[tgt].kind != TargetKind::Lib {
501 continue;
502 }
503 // Exclude alloc / core / std
504 if rustc_workspace[tgt]
505 .root
506 .components()
507 .any(|c| c == Component::Normal("library".as_ref()))
508 {
509 continue;
510 }
511
512 if let Some(crate_id) = add_target_crate_root(
513 &mut crate_graph,
514 &rustc_workspace[pkg],
515 &rustc_workspace[tgt],
516 &cfg_options,
517 proc_macro_client,
518 load,
519 ) {
520 pkg_to_lib_crate.insert(pkg, crate_id);
521 // Add dependencies on the core / std / alloc for rustc
522 for (name, krate) in public_deps.iter() {
523 if let Err(_) =
524 crate_graph.add_dep(crate_id, name.clone(), *krate)
525 {
526 log::error!(
527 "cyclic dependency on {} for {}",
528 name,
529 &cargo[pkg].name
530 )
531 }
532 }
533 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
534 }
535 }
536 }
537 // Now add a dep edge from all targets of upstream to the lib
538 // target of downstream.
539 for pkg in rustc_workspace.packages() {
540 for dep in rustc_workspace[pkg].dependencies.iter() {
541 let name = CrateName::new(&dep.name).unwrap();
542 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
543 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
544 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
545 log::error!(
546 "cyclic dependency {} -> {}",
547 &rustc_workspace[pkg].name,
548 &rustc_workspace[dep.pkg].name
549 )
550 }
551 }
552 }
553 }
554 }
555
556 // Add dependencies for all the crates of the current workspace to rustc_private libraries
557 for dep in rustc_workspace.packages() {
558 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
559
560 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
561 for pkg in cargo.packages() {
562 if !cargo[pkg].is_member {
563 continue;
564 }
565 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
566 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
567 log::error!(
568 "cyclic dependency {} -> {}",
569 &cargo[pkg].name,
570 &rustc_workspace[dep].name
571 )
572 }
573 }
574 }
575 }
576 }
577 }
487 } 578 }
488 } 579 }
489 if crate_graph.patch_cfg_if() { 580 if crate_graph.patch_cfg_if() {
@@ -537,6 +628,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
537 Ok(stdout.trim().to_string()) 628 Ok(stdout.trim().to_string())
538} 629}
539 630
631fn add_target_crate_root(
632 crate_graph: &mut CrateGraph,
633 pkg: &cargo_workspace::PackageData,
634 tgt: &cargo_workspace::TargetData,
635 cfg_options: &CfgOptions,
636 proc_macro_client: &ProcMacroClient,
637 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
638) -> Option<CrateId> {
639 let root = tgt.root.as_path();
640 if let Some(file_id) = load(root) {
641 let edition = pkg.edition;
642 let cfg_options = {
643 let mut opts = cfg_options.clone();
644 for feature in pkg.features.iter() {
645 opts.insert_key_value("feature".into(), feature.into());
646 }
647 opts.extend(pkg.cfgs.iter().cloned());
648 opts
649 };
650 let mut env = Env::default();
651 if let Some(out_dir) = &pkg.out_dir {
652 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
653 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
654 env.set("OUT_DIR", out_dir);
655 }
656 }
657 let proc_macro = pkg
658 .proc_macro_dylib_path
659 .as_ref()
660 .map(|it| proc_macro_client.by_dylib_path(&it))
661 .unwrap_or_default();
662
663 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
664 let crate_id = crate_graph.add_crate_root(
665 file_id,
666 edition,
667 Some(display_name),
668 cfg_options,
669 env,
670 proc_macro.clone(),
671 );
672
673 return Some(crate_id);
674 }
675 None
676}
540fn sysroot_to_crate_graph( 677fn sysroot_to_crate_graph(
541 crate_graph: &mut CrateGraph, 678 crate_graph: &mut CrateGraph,
542 sysroot: &Sysroot, 679 sysroot: &Sysroot,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index b4c738272..74a021dbf 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,7 +7,7 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use std::{ffi::OsString, path::PathBuf}; 10use std::{convert::TryFrom, ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
@@ -227,12 +227,25 @@ impl Config {
227 self.notifications = 227 self.notifications =
228 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound }; 228 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
229 self.cargo_autoreload = data.cargo_autoreload; 229 self.cargo_autoreload = data.cargo_autoreload;
230
231 let rustc_source = if let Some(rustc_source) = data.rustcSource {
232 let rustpath: PathBuf = rustc_source.into();
233 AbsPathBuf::try_from(rustpath)
234 .map_err(|_| {
235 log::error!("rustc source directory must be an absolute path");
236 })
237 .ok()
238 } else {
239 None
240 };
241
230 self.cargo = CargoConfig { 242 self.cargo = CargoConfig {
231 no_default_features: data.cargo_noDefaultFeatures, 243 no_default_features: data.cargo_noDefaultFeatures,
232 all_features: data.cargo_allFeatures, 244 all_features: data.cargo_allFeatures,
233 features: data.cargo_features.clone(), 245 features: data.cargo_features.clone(),
234 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, 246 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
235 target: data.cargo_target.clone(), 247 target: data.cargo_target.clone(),
248 rustc_source: rustc_source,
236 }; 249 };
237 self.runnables = RunnablesConfig { 250 self.runnables = RunnablesConfig {
238 override_cargo: data.runnables_overrideCargo, 251 override_cargo: data.runnables_overrideCargo,
@@ -535,5 +548,6 @@ config_data! {
535 rustfmt_overrideCommand: Option<Vec<String>> = None, 548 rustfmt_overrideCommand: Option<Vec<String>> = None,
536 549
537 withSysroot: bool = true, 550 withSysroot: bool = true,
551 rustcSource : Option<String> = None,
538 } 552 }
539} 553}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 0eabd51bd..11c8d0e5f 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -246,7 +246,9 @@ impl GlobalState {
246 .iter() 246 .iter()
247 .enumerate() 247 .enumerate()
248 .filter_map(|(id, w)| match w { 248 .filter_map(|(id, w)| match w {
249 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())), 249 ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => {
250 Some((id, cargo.workspace_root()))
251 }
250 ProjectWorkspace::Json { project, .. } => { 252 ProjectWorkspace::Json { project, .. } => {
251 // Enable flychecks for json projects if a custom flycheck command was supplied 253 // Enable flychecks for json projects if a custom flycheck command was supplied
252 // in the workspace configuration. 254 // in the workspace configuration.
diff --git a/editors/code/package.json b/editors/code/package.json
index eccafccdd..6db78a99a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -687,6 +687,14 @@
687 }, 687 },
688 "default": [], 688 "default": [],
689 "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'" 689 "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'"
690 },
691 "rust-analyzer.rustcSource": {
692 "type": [
693 "null",
694 "string"
695 ],
696 "default": null,
697 "description": "Path to the rust compiler sources, for usage in rustc_private projects."
690 } 698 }
691 } 699 }
692 }, 700 },