]> git.lizzy.rs Git - rust.git/blob - crates/project_model/src/workspace.rs
Deal with todos
[rust.git] / crates / project_model / src / workspace.rs
1 //! Handles lowering of build-system specific workspace information (`cargo
2 //! metadata` or `rust-project.json`) into representation stored in the salsa
3 //! database -- `CrateGraph`.
4
5 use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6
7 use anyhow::{format_err, Context, Result};
8 use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9 use cargo_workspace::DepKind;
10 use cfg::CfgOptions;
11 use paths::{AbsPath, AbsPathBuf};
12 use proc_macro_api::ProcMacroClient;
13 use rustc_hash::{FxHashMap, FxHashSet};
14
15 use crate::{
16     build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
17     cargo_workspace,
18     cfg_flag::CfgFlag,
19     rustc_cfg,
20     sysroot::SysrootCrate,
21     utf8_stdout, BuildDataCollector, CargoConfig, CargoWorkspace, ProjectJson, ProjectManifest,
22     Sysroot, TargetKind,
23 };
24
25 /// `PackageRoot` describes a package root folder.
26 /// Which may be an external dependency, or a member of
27 /// the current workspace.
28 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
29 pub struct PackageRoot {
30     /// Is a member of the current workspace
31     pub is_member: bool,
32     pub include: Vec<AbsPathBuf>,
33     pub exclude: Vec<AbsPathBuf>,
34 }
35
36 #[derive(Clone, Eq, PartialEq)]
37 pub enum ProjectWorkspace {
38     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
39     Cargo {
40         cargo: CargoWorkspace,
41         sysroot: Sysroot,
42         rustc: Option<CargoWorkspace>,
43         /// Holds cfg flags for the current target. We get those by running
44         /// `rustc --print cfg`.
45         ///
46         /// FIXME: make this a per-crate map, as, eg, build.rs might have a
47         /// different target.
48         rustc_cfg: Vec<CfgFlag>,
49     },
50     /// Project workspace was manually specified using a `rust-project.json` file.
51     Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
52     /// Project with a set of disjoint files, not belonging to any particular workspace.
53     /// Backed by basic sysroot crates for basic completion and highlighting.
54     DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
55 }
56
57 impl fmt::Debug for ProjectWorkspace {
58     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59         // Make sure this isn't too verbose.
60         match self {
61             ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
62                 .debug_struct("Cargo")
63                 .field("root", &cargo.workspace_root().file_name())
64                 .field("n_packages", &cargo.packages().len())
65                 .field("n_sysroot_crates", &sysroot.crates().len())
66                 .field(
67                     "n_rustc_compiler_crates",
68                     &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
69                 )
70                 .field("n_rustc_cfg", &rustc_cfg.len())
71                 .finish(),
72             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
73                 let mut debug_struct = f.debug_struct("Json");
74                 debug_struct.field("n_crates", &project.n_crates());
75                 if let Some(sysroot) = sysroot {
76                     debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
77                 }
78                 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
79                 debug_struct.finish()
80             }
81             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
82                 .debug_struct("DetachedFiles")
83                 .field("n_files", &files.len())
84                 .field("n_sysroot_crates", &sysroot.crates().len())
85                 .field("n_rustc_cfg", &rustc_cfg.len())
86                 .finish(),
87         }
88     }
89 }
90
91 impl ProjectWorkspace {
92     pub fn load(
93         manifest: ProjectManifest,
94         config: &CargoConfig,
95         progress: &dyn Fn(String),
96     ) -> Result<ProjectWorkspace> {
97         let res = match manifest {
98             ProjectManifest::ProjectJson(project_json) => {
99                 let file = fs::read_to_string(&project_json).with_context(|| {
100                     format!("Failed to read json file {}", project_json.display())
101                 })?;
102                 let data = serde_json::from_str(&file).with_context(|| {
103                     format!("Failed to deserialize json file {}", project_json.display())
104                 })?;
105                 let project_location = project_json.parent().unwrap().to_path_buf();
106                 let project_json = ProjectJson::new(&project_location, data);
107                 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
108             }
109             ProjectManifest::CargoToml(cargo_toml) => {
110                 let cargo_version = utf8_stdout({
111                     let mut cmd = Command::new(toolchain::cargo());
112                     cmd.arg("--version");
113                     cmd
114                 })?;
115
116                 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
117                     .with_context(|| {
118                         format!(
119                             "Failed to read Cargo metadata from Cargo.toml file {}, {}",
120                             cargo_toml.display(),
121                             cargo_version
122                         )
123                     })?;
124
125                 let sysroot = if config.no_sysroot {
126                     Sysroot::default()
127                 } else {
128                     Sysroot::discover(&cargo_toml).with_context(|| {
129                         format!(
130                             "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
131                             cargo_toml.display()
132                         )
133                     })?
134                 };
135
136                 let rustc_dir = if let Some(rustc_source) = &config.rustc_source {
137                     use cargo_workspace::RustcSource;
138                     match rustc_source {
139                         RustcSource::Path(path) => Some(path.clone()),
140                         RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml),
141                     }
142                 } else {
143                     None
144                 };
145
146                 let rustc = if let Some(rustc_dir) = rustc_dir {
147                     Some(
148                         CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
149                             .with_context(|| {
150                                 format!("Failed to read Cargo metadata for Rust sources")
151                             })?,
152                     )
153                 } else {
154                     None
155                 };
156
157                 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
158                 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
159             }
160         };
161
162         Ok(res)
163     }
164
165     pub fn load_inline(
166         project_json: ProjectJson,
167         target: Option<&str>,
168     ) -> Result<ProjectWorkspace> {
169         let sysroot = match &project_json.sysroot_src {
170             Some(path) => Some(Sysroot::load(path)?),
171             None => None,
172         };
173         let rustc_cfg = rustc_cfg::get(None, target);
174         Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
175     }
176
177     pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
178         let sysroot = Sysroot::discover(
179             &detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
180         )?;
181         let rustc_cfg = rustc_cfg::get(None, None);
182         Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
183     }
184
185     /// Returns the roots for the current `ProjectWorkspace`
186     /// The return type contains the path and whether or not
187     /// the root is a member of the current workspace
188     pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot> {
189         match self {
190             ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
191                 .crates()
192                 .map(|(_, krate)| PackageRoot {
193                     is_member: krate.is_workspace_member,
194                     include: krate.include.clone(),
195                     exclude: krate.exclude.clone(),
196                 })
197                 .collect::<FxHashSet<_>>()
198                 .into_iter()
199                 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
200                     sysroot.crates().map(move |krate| PackageRoot {
201                         is_member: false,
202                         include: vec![sysroot[krate].root_dir().to_path_buf()],
203                         exclude: Vec::new(),
204                     })
205                 }))
206                 .collect::<Vec<_>>(),
207             ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo
208                 .packages()
209                 .map(|pkg| {
210                     let is_member = cargo[pkg].is_member;
211                     let pkg_root = cargo[pkg].root().to_path_buf();
212
213                     let mut include = vec![pkg_root.clone()];
214                     include.extend(
215                         build_data
216                             .and_then(|it| it.get(cargo.workspace_root()))
217                             .and_then(|map| map.get(&cargo[pkg].id))
218                             .and_then(|it| it.out_dir.clone()),
219                     );
220
221                     let mut exclude = vec![pkg_root.join(".git")];
222                     if is_member {
223                         exclude.push(pkg_root.join("target"));
224                     } else {
225                         exclude.push(pkg_root.join("tests"));
226                         exclude.push(pkg_root.join("examples"));
227                         exclude.push(pkg_root.join("benches"));
228                     }
229                     PackageRoot { is_member, include, exclude }
230                 })
231                 .chain(sysroot.crates().map(|krate| PackageRoot {
232                     is_member: false,
233                     include: vec![sysroot[krate].root_dir().to_path_buf()],
234                     exclude: Vec::new(),
235                 }))
236                 .chain(rustc.into_iter().flat_map(|rustc| {
237                     rustc.packages().map(move |krate| PackageRoot {
238                         is_member: false,
239                         include: vec![rustc[krate].root().to_path_buf()],
240                         exclude: Vec::new(),
241                     })
242                 }))
243                 .collect(),
244             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
245                 .into_iter()
246                 .map(|detached_file| PackageRoot {
247                     is_member: true,
248                     include: vec![detached_file.clone()],
249                     exclude: Vec::new(),
250                 })
251                 .chain(sysroot.crates().map(|krate| PackageRoot {
252                     is_member: false,
253                     include: vec![sysroot[krate].root_dir().to_path_buf()],
254                     exclude: Vec::new(),
255                 }))
256                 .collect(),
257         }
258     }
259
260     pub fn n_packages(&self) -> usize {
261         match self {
262             ProjectWorkspace::Json { project, .. } => project.n_crates(),
263             ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
264                 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
265                 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
266             }
267             ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
268                 sysroot.crates().len() + files.len()
269             }
270         }
271     }
272
273     pub fn to_crate_graph(
274         &self,
275         build_data: Option<&BuildDataResult>,
276         proc_macro_client: Option<&ProcMacroClient>,
277         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
278     ) -> CrateGraph {
279         let _p = profile::span("ProjectWorkspace::to_crate_graph");
280         let proc_macro_loader = |path: &Path| match proc_macro_client {
281             Some(client) => client.by_dylib_path(path),
282             None => Vec::new(),
283         };
284
285         let mut crate_graph = match self {
286             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
287                 rustc_cfg.clone(),
288                 &proc_macro_loader,
289                 load,
290                 project,
291                 sysroot,
292             ),
293             ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph(
294                 rustc_cfg.clone(),
295                 &proc_macro_loader,
296                 load,
297                 cargo,
298                 build_data.and_then(|it| it.get(cargo.workspace_root())),
299                 sysroot,
300                 rustc,
301                 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())),
302             ),
303             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
304                 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
305             }
306         };
307         if crate_graph.patch_cfg_if() {
308             log::debug!("Patched std to depend on cfg-if")
309         } else {
310             log::debug!("Did not patch std to depend on cfg-if")
311         }
312         crate_graph
313     }
314
315     pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
316         match self {
317             ProjectWorkspace::Cargo { cargo, .. } => {
318                 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
319             }
320             _ => {}
321         }
322     }
323 }
324
325 fn project_json_to_crate_graph(
326     rustc_cfg: Vec<CfgFlag>,
327     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
328     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
329     project: &ProjectJson,
330     sysroot: &Option<Sysroot>,
331 ) -> CrateGraph {
332     let mut crate_graph = CrateGraph::default();
333     let sysroot_deps = sysroot
334         .as_ref()
335         .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
336
337     let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
338     let crates: FxHashMap<CrateId, CrateId> = project
339         .crates()
340         .filter_map(|(crate_id, krate)| {
341             let file_path = &krate.root_module;
342             let file_id = load(&file_path)?;
343             Some((crate_id, krate, file_id))
344         })
345         .map(|(crate_id, krate, file_id)| {
346             let env = krate.env.clone().into_iter().collect();
347             let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
348
349             let target_cfgs = match krate.target.as_deref() {
350                 Some(target) => {
351                     cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
352                 }
353                 None => &rustc_cfg,
354             };
355
356             let mut cfg_options = CfgOptions::default();
357             cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
358             (
359                 crate_id,
360                 crate_graph.add_crate_root(
361                     file_id,
362                     krate.edition,
363                     krate.display_name.clone(),
364                     cfg_options,
365                     env,
366                     proc_macro.unwrap_or_default(),
367                 ),
368             )
369         })
370         .collect();
371
372     for (from, krate) in project.crates() {
373         if let Some(&from) = crates.get(&from) {
374             if let Some((public_deps, _proc_macro)) = &sysroot_deps {
375                 for (name, to) in public_deps.iter() {
376                     add_dep(&mut crate_graph, from, name.clone(), *to)
377                 }
378             }
379
380             for dep in &krate.deps {
381                 if let Some(&to) = crates.get(&dep.crate_id) {
382                     add_dep(&mut crate_graph, from, dep.name.clone(), to)
383                 }
384             }
385         }
386     }
387     crate_graph
388 }
389
390 fn cargo_to_crate_graph(
391     rustc_cfg: Vec<CfgFlag>,
392     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
393     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
394     cargo: &CargoWorkspace,
395     build_data_map: Option<&WorkspaceBuildData>,
396     sysroot: &Sysroot,
397     rustc: &Option<CargoWorkspace>,
398     rustc_build_data_map: Option<&WorkspaceBuildData>,
399 ) -> CrateGraph {
400     let _p = profile::span("cargo_to_crate_graph");
401     let mut crate_graph = CrateGraph::default();
402     let (public_deps, libproc_macro) =
403         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
404
405     let mut cfg_options = CfgOptions::default();
406     cfg_options.extend(rustc_cfg);
407
408     let mut pkg_to_lib_crate = FxHashMap::default();
409
410     // Add test cfg for non-sysroot crates
411     cfg_options.insert_atom("test".into());
412     cfg_options.insert_atom("debug_assertions".into());
413
414     let mut pkg_crates = FxHashMap::default();
415     // Does any crate signal to rust-analyzer that they need the rustc_private crates?
416     let mut has_private = false;
417     // Next, create crates for each package, target pair
418     for pkg in cargo.packages() {
419         has_private |= cargo[pkg].metadata.rustc_private;
420         let mut lib_tgt = None;
421         for &tgt in cargo[pkg].targets.iter() {
422             if let Some(file_id) = load(&cargo[tgt].root) {
423                 let crate_id = add_target_crate_root(
424                     &mut crate_graph,
425                     &cargo[pkg],
426                     build_data_map.and_then(|it| it.get(&cargo[pkg].id)),
427                     &cfg_options,
428                     proc_macro_loader,
429                     file_id,
430                     &cargo[tgt].name,
431                 );
432                 if cargo[tgt].kind == TargetKind::Lib {
433                     lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
434                     pkg_to_lib_crate.insert(pkg, crate_id);
435                 }
436                 if cargo[tgt].is_proc_macro {
437                     if let Some(proc_macro) = libproc_macro {
438                         add_dep(
439                             &mut crate_graph,
440                             crate_id,
441                             CrateName::new("proc_macro").unwrap(),
442                             proc_macro,
443                         );
444                     }
445                 }
446
447                 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
448             }
449         }
450
451         // Set deps to the core, std and to the lib target of the current package
452         for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
453             if let Some((to, name)) = lib_tgt.clone() {
454                 if to != *from && *kind != TargetKind::BuildScript {
455                     // (build script can not depend on its library target)
456
457                     // For root projects with dashes in their name,
458                     // cargo metadata does not do any normalization,
459                     // so we do it ourselves currently
460                     let name = CrateName::normalize_dashes(&name);
461                     add_dep(&mut crate_graph, *from, name, to);
462                 }
463             }
464             for (name, krate) in public_deps.iter() {
465                 add_dep(&mut crate_graph, *from, name.clone(), *krate);
466             }
467         }
468     }
469
470     // Now add a dep edge from all targets of upstream to the lib
471     // target of downstream.
472     for pkg in cargo.packages() {
473         for dep in cargo[pkg].dependencies.iter() {
474             let name = CrateName::new(&dep.name).unwrap();
475             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
476                 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
477                     if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
478                         // Only build scripts may depend on build dependencies.
479                         continue;
480                     }
481                     if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
482                         // Build scripts may only depend on build dependencies.
483                         continue;
484                     }
485
486                     add_dep(&mut crate_graph, *from, name.clone(), to)
487                 }
488             }
489         }
490     }
491
492     if has_private {
493         // If the user provided a path to rustc sources, we add all the rustc_private crates
494         // and create dependencies on them for the crates which opt-in to that
495         if let Some(rustc_workspace) = rustc {
496             handle_rustc_crates(
497                 rustc_workspace,
498                 load,
499                 &mut crate_graph,
500                 rustc_build_data_map,
501                 &cfg_options,
502                 proc_macro_loader,
503                 &mut pkg_to_lib_crate,
504                 &public_deps,
505                 cargo,
506                 &pkg_crates,
507             );
508         }
509     }
510     crate_graph
511 }
512
513 fn detached_files_to_crate_graph(
514     rustc_cfg: Vec<CfgFlag>,
515     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
516     detached_files: &[AbsPathBuf],
517     sysroot: &Sysroot,
518 ) -> CrateGraph {
519     let _p = profile::span("detached_files_to_crate_graph");
520     let mut crate_graph = CrateGraph::default();
521     let (public_deps, _libproc_macro) =
522         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
523
524     let mut cfg_options = CfgOptions::default();
525     cfg_options.extend(rustc_cfg);
526
527     for detached_file in detached_files {
528         let file_id = match load(&detached_file) {
529             Some(file_id) => file_id,
530             None => {
531                 log::error!("Failed to load detached file {:?}", detached_file);
532                 continue;
533             }
534         };
535         let display_name = detached_file
536             .file_stem()
537             .and_then(|os_str| os_str.to_str())
538             .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
539         let detached_file_crate = crate_graph.add_crate_root(
540             file_id,
541             Edition::Edition2018,
542             display_name,
543             cfg_options.clone(),
544             Env::default(),
545             Vec::new(),
546         );
547
548         for (name, krate) in public_deps.iter() {
549             add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
550         }
551     }
552     crate_graph
553 }
554
555 fn handle_rustc_crates(
556     rustc_workspace: &CargoWorkspace,
557     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
558     crate_graph: &mut CrateGraph,
559     rustc_build_data_map: Option<&WorkspaceBuildData>,
560     cfg_options: &CfgOptions,
561     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
562     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
563     public_deps: &[(CrateName, CrateId)],
564     cargo: &CargoWorkspace,
565     pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
566 ) {
567     let mut rustc_pkg_crates = FxHashMap::default();
568     // The root package of the rustc-dev component is rustc_driver, so we match that
569     let root_pkg =
570         rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
571     // The rustc workspace might be incomplete (such as if rustc-dev is not
572     // installed for the current toolchain) and `rustcSource` is set to discover.
573     if let Some(root_pkg) = root_pkg {
574         // Iterate through every crate in the dependency subtree of rustc_driver using BFS
575         let mut queue = VecDeque::new();
576         queue.push_back(root_pkg);
577         while let Some(pkg) = queue.pop_front() {
578             // Don't duplicate packages if they are dependended on a diamond pattern
579             // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
580             // which is not ideal
581             if rustc_pkg_crates.contains_key(&pkg) {
582                 continue;
583             }
584             for dep in &rustc_workspace[pkg].dependencies {
585                 queue.push_back(dep.pkg);
586             }
587             for &tgt in rustc_workspace[pkg].targets.iter() {
588                 if rustc_workspace[tgt].kind != TargetKind::Lib {
589                     continue;
590                 }
591                 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
592                     let crate_id = add_target_crate_root(
593                         crate_graph,
594                         &rustc_workspace[pkg],
595                         rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
596                         &cfg_options,
597                         proc_macro_loader,
598                         file_id,
599                         &rustc_workspace[tgt].name,
600                     );
601                     pkg_to_lib_crate.insert(pkg, crate_id);
602                     // Add dependencies on core / std / alloc for this crate
603                     for (name, krate) in public_deps.iter() {
604                         add_dep(crate_graph, crate_id, name.clone(), *krate);
605                     }
606                     rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
607                 }
608             }
609         }
610     }
611     // Now add a dep edge from all targets of upstream to the lib
612     // target of downstream.
613     for pkg in rustc_pkg_crates.keys().copied() {
614         for dep in rustc_workspace[pkg].dependencies.iter() {
615             let name = CrateName::new(&dep.name).unwrap();
616             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
617                 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
618                     add_dep(crate_graph, from, name.clone(), to);
619                 }
620             }
621         }
622     }
623     // Add a dependency on the rustc_private crates for all targets of each package
624     // which opts in
625     for dep in rustc_workspace.packages() {
626         let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
627
628         if let Some(&to) = pkg_to_lib_crate.get(&dep) {
629             for pkg in cargo.packages() {
630                 let package = &cargo[pkg];
631                 if !package.metadata.rustc_private {
632                     continue;
633                 }
634                 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
635                     // Avoid creating duplicate dependencies
636                     // This avoids the situation where `from` depends on e.g. `arrayvec`, but
637                     // `rust_analyzer` thinks that it should use the one from the `rustcSource`
638                     // instead of the one from `crates.io`
639                     if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
640                         add_dep(crate_graph, *from, name.clone(), to);
641                     }
642                 }
643             }
644         }
645     }
646 }
647
648 fn add_target_crate_root(
649     crate_graph: &mut CrateGraph,
650     pkg: &cargo_workspace::PackageData,
651     build_data: Option<&PackageBuildData>,
652     cfg_options: &CfgOptions,
653     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
654     file_id: FileId,
655     cargo_name: &str,
656 ) -> CrateId {
657     let edition = pkg.edition;
658     let cfg_options = {
659         let mut opts = cfg_options.clone();
660         for feature in pkg.active_features.iter() {
661             opts.insert_key_value("feature".into(), feature.into());
662         }
663         if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
664             opts.extend(cfgs.iter().cloned());
665         }
666         opts
667     };
668
669     let mut env = Env::default();
670     if let Some(envs) = build_data.map(|it| &it.envs) {
671         for (k, v) in envs {
672             env.set(k, v.clone());
673         }
674     }
675
676     let proc_macro = build_data
677         .as_ref()
678         .and_then(|it| it.proc_macro_dylib_path.as_ref())
679         .map(|it| proc_macro_loader(&it))
680         .unwrap_or_default();
681
682     let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
683     let crate_id = crate_graph.add_crate_root(
684         file_id,
685         edition,
686         Some(display_name),
687         cfg_options,
688         env,
689         proc_macro,
690     );
691
692     crate_id
693 }
694
695 fn sysroot_to_crate_graph(
696     crate_graph: &mut CrateGraph,
697     sysroot: &Sysroot,
698     rustc_cfg: Vec<CfgFlag>,
699     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
700 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
701     let _p = profile::span("sysroot_to_crate_graph");
702     let mut cfg_options = CfgOptions::default();
703     cfg_options.extend(rustc_cfg);
704     let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
705         .crates()
706         .filter_map(|krate| {
707             let file_id = load(&sysroot[krate].root)?;
708
709             let env = Env::default();
710             let proc_macro = vec![];
711             let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
712             let crate_id = crate_graph.add_crate_root(
713                 file_id,
714                 Edition::Edition2018,
715                 Some(display_name),
716                 cfg_options.clone(),
717                 env,
718                 proc_macro,
719             );
720             Some((krate, crate_id))
721         })
722         .collect();
723
724     for from in sysroot.crates() {
725         for &to in sysroot[from].deps.iter() {
726             let name = CrateName::new(&sysroot[to].name).unwrap();
727             if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
728                 add_dep(crate_graph, from, name, to);
729             }
730         }
731     }
732
733     let public_deps = sysroot
734         .public_deps()
735         .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx]))
736         .collect::<Vec<_>>();
737
738     let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
739     (public_deps, libproc_macro)
740 }
741
742 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
743     if let Err(err) = graph.add_dep(from, name, to) {
744         log::error!("{}", err)
745     }
746 }