]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/project-model/src/workspace.rs
Add 'src/tools/rust-analyzer/' from commit '977e12a0bdc3e329af179ef3a9d466af9eb613bb'
[rust.git] / src / tools / rust-analyzer / 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, process::Command};
6
7 use anyhow::{format_err, Context, Result};
8 use base_db::{
9     CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
10     FileId, LangCrateOrigin, ProcMacroLoadResult,
11 };
12 use cfg::{CfgDiff, CfgOptions};
13 use paths::{AbsPath, AbsPathBuf};
14 use rustc_hash::{FxHashMap, FxHashSet};
15 use stdx::always;
16
17 use crate::{
18     build_scripts::BuildScriptOutput,
19     cargo_workspace::{DepKind, PackageData, RustcSource},
20     cfg_flag::CfgFlag,
21     rustc_cfg,
22     sysroot::SysrootCrate,
23     utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
24     TargetKind, WorkspaceBuildScripts,
25 };
26
27 /// A set of cfg-overrides per crate.
28 ///
29 /// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
30 /// without having to first obtain a list of all crates.
31 #[derive(Debug, Clone, Eq, PartialEq)]
32 pub enum CfgOverrides {
33     /// A single global set of overrides matching all crates.
34     Wildcard(CfgDiff),
35     /// A set of overrides matching specific crates.
36     Selective(FxHashMap<String, CfgDiff>),
37 }
38
39 impl Default for CfgOverrides {
40     fn default() -> Self {
41         Self::Selective(FxHashMap::default())
42     }
43 }
44
45 impl CfgOverrides {
46     pub fn len(&self) -> usize {
47         match self {
48             CfgOverrides::Wildcard(_) => 1,
49             CfgOverrides::Selective(hash_map) => hash_map.len(),
50         }
51     }
52 }
53
54 /// `PackageRoot` describes a package root folder.
55 /// Which may be an external dependency, or a member of
56 /// the current workspace.
57 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
58 pub struct PackageRoot {
59     /// Is from the local filesystem and may be edited
60     pub is_local: bool,
61     pub include: Vec<AbsPathBuf>,
62     pub exclude: Vec<AbsPathBuf>,
63 }
64
65 #[derive(Clone, Eq, PartialEq)]
66 pub enum ProjectWorkspace {
67     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
68     Cargo {
69         cargo: CargoWorkspace,
70         build_scripts: WorkspaceBuildScripts,
71         sysroot: Option<Sysroot>,
72         rustc: Option<CargoWorkspace>,
73         /// Holds cfg flags for the current target. We get those by running
74         /// `rustc --print cfg`.
75         ///
76         /// FIXME: make this a per-crate map, as, eg, build.rs might have a
77         /// different target.
78         rustc_cfg: Vec<CfgFlag>,
79         cfg_overrides: CfgOverrides,
80     },
81     /// Project workspace was manually specified using a `rust-project.json` file.
82     Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
83
84     // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
85     // That's not the end user experience we should strive for.
86     // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
87     // That needs some changes on the salsa-level though.
88     // In particular, we should split the unified CrateGraph (which currently has maximal durability) into proper crate graph, and a set of ad hoc roots (with minimal durability).
89     // Then, we need to hide the graph behind the queries such that most queries look only at the proper crate graph, and fall back to ad hoc roots only if there's no results.
90     // After this, we should be able to tweak the logic in reload.rs to add newly opened files, which don't belong to any existing crates, to the set of the detached files.
91     // //
92     /// Project with a set of disjoint files, not belonging to any particular workspace.
93     /// Backed by basic sysroot crates for basic completion and highlighting.
94     DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
95 }
96
97 impl fmt::Debug for ProjectWorkspace {
98     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99         // Make sure this isn't too verbose.
100         match self {
101             ProjectWorkspace::Cargo {
102                 cargo,
103                 build_scripts: _,
104                 sysroot,
105                 rustc,
106                 rustc_cfg,
107                 cfg_overrides,
108             } => f
109                 .debug_struct("Cargo")
110                 .field("root", &cargo.workspace_root().file_name())
111                 .field("n_packages", &cargo.packages().len())
112                 .field("sysroot", &sysroot.is_some())
113                 .field(
114                     "n_rustc_compiler_crates",
115                     &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
116                 )
117                 .field("n_rustc_cfg", &rustc_cfg.len())
118                 .field("n_cfg_overrides", &cfg_overrides.len())
119                 .finish(),
120             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
121                 let mut debug_struct = f.debug_struct("Json");
122                 debug_struct.field("n_crates", &project.n_crates());
123                 if let Some(sysroot) = sysroot {
124                     debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
125                 }
126                 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
127                 debug_struct.finish()
128             }
129             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
130                 .debug_struct("DetachedFiles")
131                 .field("n_files", &files.len())
132                 .field("n_sysroot_crates", &sysroot.crates().len())
133                 .field("n_rustc_cfg", &rustc_cfg.len())
134                 .finish(),
135         }
136     }
137 }
138
139 impl ProjectWorkspace {
140     pub fn load(
141         manifest: ProjectManifest,
142         config: &CargoConfig,
143         progress: &dyn Fn(String),
144     ) -> Result<ProjectWorkspace> {
145         let res = match manifest {
146             ProjectManifest::ProjectJson(project_json) => {
147                 let file = fs::read_to_string(&project_json).with_context(|| {
148                     format!("Failed to read json file {}", project_json.display())
149                 })?;
150                 let data = serde_json::from_str(&file).with_context(|| {
151                     format!("Failed to deserialize json file {}", project_json.display())
152                 })?;
153                 let project_location = project_json.parent().to_path_buf();
154                 let project_json = ProjectJson::new(&project_location, data);
155                 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
156             }
157             ProjectManifest::CargoToml(cargo_toml) => {
158                 let cargo_version = utf8_stdout({
159                     let mut cmd = Command::new(toolchain::cargo());
160                     cmd.arg("--version");
161                     cmd
162                 })?;
163
164                 let meta = CargoWorkspace::fetch_metadata(
165                     &cargo_toml,
166                     cargo_toml.parent(),
167                     config,
168                     progress,
169                 )
170                 .with_context(|| {
171                     format!(
172                         "Failed to read Cargo metadata from Cargo.toml file {}, {}",
173                         cargo_toml.display(),
174                         cargo_version
175                     )
176                 })?;
177                 let cargo = CargoWorkspace::new(meta);
178
179                 let sysroot = if config.no_sysroot {
180                     None
181                 } else {
182                     Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
183                         format!(
184                             "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
185                             cargo_toml.display()
186                         )
187                     })?)
188                 };
189
190                 let rustc_dir = match &config.rustc_source {
191                     Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
192                     Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
193                     None => None,
194                 };
195
196                 let rustc = match rustc_dir {
197                     Some(rustc_dir) => Some({
198                         let meta = CargoWorkspace::fetch_metadata(
199                             &rustc_dir,
200                             cargo_toml.parent(),
201                             config,
202                             progress,
203                         )
204                         .with_context(|| {
205                             "Failed to read Cargo metadata for Rust sources".to_string()
206                         })?;
207                         CargoWorkspace::new(meta)
208                     }),
209                     None => None,
210                 };
211
212                 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
213
214                 let cfg_overrides = config.cfg_overrides();
215                 ProjectWorkspace::Cargo {
216                     cargo,
217                     build_scripts: WorkspaceBuildScripts::default(),
218                     sysroot,
219                     rustc,
220                     rustc_cfg,
221                     cfg_overrides,
222                 }
223             }
224         };
225
226         Ok(res)
227     }
228
229     pub fn load_inline(
230         project_json: ProjectJson,
231         target: Option<&str>,
232     ) -> Result<ProjectWorkspace> {
233         let sysroot = match &project_json.sysroot_src {
234             Some(path) => Some(Sysroot::load(path.clone())?),
235             None => None,
236         };
237         let rustc_cfg = rustc_cfg::get(None, target);
238         Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
239     }
240
241     pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
242         let sysroot = Sysroot::discover(
243             detached_files
244                 .first()
245                 .and_then(|it| it.parent())
246                 .ok_or_else(|| format_err!("No detached files to load"))?,
247         )?;
248         let rustc_cfg = rustc_cfg::get(None, None);
249         Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
250     }
251
252     pub fn run_build_scripts(
253         &self,
254         config: &CargoConfig,
255         progress: &dyn Fn(String),
256     ) -> Result<WorkspaceBuildScripts> {
257         match self {
258             ProjectWorkspace::Cargo { cargo, .. } => {
259                 WorkspaceBuildScripts::run(config, cargo, progress).with_context(|| {
260                     format!("Failed to run build scripts for {}", &cargo.workspace_root().display())
261                 })
262             }
263             ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
264                 Ok(WorkspaceBuildScripts::default())
265             }
266         }
267     }
268
269     pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
270         match self {
271             ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
272             _ => {
273                 always!(bs == WorkspaceBuildScripts::default());
274             }
275         }
276     }
277
278     /// Returns the roots for the current `ProjectWorkspace`
279     /// The return type contains the path and whether or not
280     /// the root is a member of the current workspace
281     pub fn to_roots(&self) -> Vec<PackageRoot> {
282         match self {
283             ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
284                 .crates()
285                 .map(|(_, krate)| PackageRoot {
286                     is_local: krate.is_workspace_member,
287                     include: krate.include.clone(),
288                     exclude: krate.exclude.clone(),
289                 })
290                 .collect::<FxHashSet<_>>()
291                 .into_iter()
292                 .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
293                     sysroot.crates().map(move |krate| PackageRoot {
294                         is_local: false,
295                         include: vec![sysroot[krate].root.parent().to_path_buf()],
296                         exclude: Vec::new(),
297                     })
298                 }))
299                 .collect::<Vec<_>>(),
300             ProjectWorkspace::Cargo {
301                 cargo,
302                 sysroot,
303                 rustc,
304                 rustc_cfg: _,
305                 cfg_overrides: _,
306                 build_scripts,
307             } => {
308                 cargo
309                     .packages()
310                     .map(|pkg| {
311                         let is_local = cargo[pkg].is_local;
312                         let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
313
314                         let mut include = vec![pkg_root.clone()];
315                         let out_dir =
316                             build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
317                         include.extend(out_dir);
318
319                         // In case target's path is manually set in Cargo.toml to be
320                         // outside the package root, add its parent as an extra include.
321                         // An example of this situation would look like this:
322                         //
323                         // ```toml
324                         // [lib]
325                         // path = "../../src/lib.rs"
326                         // ```
327                         let extra_targets = cargo[pkg]
328                             .targets
329                             .iter()
330                             .filter(|&&tgt| cargo[tgt].kind == TargetKind::Lib)
331                             .filter_map(|&tgt| cargo[tgt].root.parent())
332                             .map(|tgt| tgt.normalize().to_path_buf())
333                             .filter(|path| !path.starts_with(&pkg_root));
334                         include.extend(extra_targets);
335
336                         let mut exclude = vec![pkg_root.join(".git")];
337                         if is_local {
338                             exclude.push(pkg_root.join("target"));
339                         } else {
340                             exclude.push(pkg_root.join("tests"));
341                             exclude.push(pkg_root.join("examples"));
342                             exclude.push(pkg_root.join("benches"));
343                         }
344                         PackageRoot { is_local, include, exclude }
345                     })
346                     .chain(sysroot.iter().map(|sysroot| PackageRoot {
347                         is_local: false,
348                         include: vec![sysroot.root().to_path_buf()],
349                         exclude: Vec::new(),
350                     }))
351                     .chain(rustc.iter().flat_map(|rustc| {
352                         rustc.packages().map(move |krate| PackageRoot {
353                             is_local: false,
354                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
355                             exclude: Vec::new(),
356                         })
357                     }))
358                     .collect()
359             }
360             ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
361                 .iter()
362                 .map(|detached_file| PackageRoot {
363                     is_local: true,
364                     include: vec![detached_file.clone()],
365                     exclude: Vec::new(),
366                 })
367                 .chain(sysroot.crates().map(|krate| PackageRoot {
368                     is_local: false,
369                     include: vec![sysroot[krate].root.parent().to_path_buf()],
370                     exclude: Vec::new(),
371                 }))
372                 .collect(),
373         }
374     }
375
376     pub fn n_packages(&self) -> usize {
377         match self {
378             ProjectWorkspace::Json { project, .. } => project.n_crates(),
379             ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
380                 let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
381                 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
382                 cargo.packages().len() + sysroot_package_len + rustc_package_len
383             }
384             ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
385                 sysroot.crates().len() + files.len()
386             }
387         }
388     }
389
390     pub fn to_crate_graph(
391         &self,
392         load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
393         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
394     ) -> CrateGraph {
395         let _p = profile::span("ProjectWorkspace::to_crate_graph");
396
397         let mut crate_graph = match self {
398             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
399                 rustc_cfg.clone(),
400                 load_proc_macro,
401                 load,
402                 project,
403                 sysroot,
404             ),
405             ProjectWorkspace::Cargo {
406                 cargo,
407                 sysroot,
408                 rustc,
409                 rustc_cfg,
410                 cfg_overrides,
411                 build_scripts,
412             } => cargo_to_crate_graph(
413                 rustc_cfg.clone(),
414                 cfg_overrides,
415                 load_proc_macro,
416                 load,
417                 cargo,
418                 build_scripts,
419                 sysroot.as_ref(),
420                 rustc,
421             ),
422             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
423                 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
424             }
425         };
426         if crate_graph.patch_cfg_if() {
427             tracing::debug!("Patched std to depend on cfg-if")
428         } else {
429             tracing::debug!("Did not patch std to depend on cfg-if")
430         }
431         crate_graph
432     }
433 }
434
435 fn project_json_to_crate_graph(
436     rustc_cfg: Vec<CfgFlag>,
437     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
438     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
439     project: &ProjectJson,
440     sysroot: &Option<Sysroot>,
441 ) -> CrateGraph {
442     let mut crate_graph = CrateGraph::default();
443     let sysroot_deps = sysroot
444         .as_ref()
445         .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
446
447     let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
448     let crates: FxHashMap<CrateId, CrateId> = project
449         .crates()
450         .filter_map(|(crate_id, krate)| {
451             let file_path = &krate.root_module;
452             let file_id = load(file_path)?;
453             Some((crate_id, krate, file_id))
454         })
455         .map(|(crate_id, krate, file_id)| {
456             let env = krate.env.clone().into_iter().collect();
457             let proc_macro = match krate.proc_macro_dylib_path.clone() {
458                 Some(it) => load_proc_macro(
459                     krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
460                     &it,
461                 ),
462                 None => Err("no proc macro dylib present".into()),
463             };
464
465             let target_cfgs = match krate.target.as_deref() {
466                 Some(target) => {
467                     cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
468                 }
469                 None => &rustc_cfg,
470             };
471
472             let mut cfg_options = CfgOptions::default();
473             cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
474             (
475                 crate_id,
476                 crate_graph.add_crate_root(
477                     file_id,
478                     krate.edition,
479                     krate.display_name.clone(),
480                     krate.version.clone(),
481                     cfg_options.clone(),
482                     cfg_options,
483                     env,
484                     proc_macro,
485                     krate.is_proc_macro,
486                     if krate.display_name.is_some() {
487                         CrateOrigin::CratesIo { repo: krate.repository.clone() }
488                     } else {
489                         CrateOrigin::CratesIo { repo: None }
490                     },
491                 ),
492             )
493         })
494         .collect();
495
496     for (from, krate) in project.crates() {
497         if let Some(&from) = crates.get(&from) {
498             if let Some((public_deps, libproc_macro)) = &sysroot_deps {
499                 public_deps.add(from, &mut crate_graph);
500                 if krate.is_proc_macro {
501                     if let Some(proc_macro) = libproc_macro {
502                         add_dep(
503                             &mut crate_graph,
504                             from,
505                             CrateName::new("proc_macro").unwrap(),
506                             *proc_macro,
507                         );
508                     }
509                 }
510             }
511
512             for dep in &krate.deps {
513                 if let Some(&to) = crates.get(&dep.crate_id) {
514                     add_dep(&mut crate_graph, from, dep.name.clone(), to)
515                 }
516             }
517         }
518     }
519     crate_graph
520 }
521
522 fn cargo_to_crate_graph(
523     rustc_cfg: Vec<CfgFlag>,
524     override_cfg: &CfgOverrides,
525     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
526     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
527     cargo: &CargoWorkspace,
528     build_scripts: &WorkspaceBuildScripts,
529     sysroot: Option<&Sysroot>,
530     rustc: &Option<CargoWorkspace>,
531 ) -> CrateGraph {
532     let _p = profile::span("cargo_to_crate_graph");
533     let mut crate_graph = CrateGraph::default();
534     let (public_deps, libproc_macro) = match sysroot {
535         Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
536         None => (SysrootPublicDeps::default(), None),
537     };
538
539     let mut cfg_options = CfgOptions::default();
540     cfg_options.extend(rustc_cfg);
541
542     let mut pkg_to_lib_crate = FxHashMap::default();
543
544     cfg_options.insert_atom("debug_assertions".into());
545
546     let mut pkg_crates = FxHashMap::default();
547     // Does any crate signal to rust-analyzer that they need the rustc_private crates?
548     let mut has_private = false;
549     // Next, create crates for each package, target pair
550     for pkg in cargo.packages() {
551         let mut cfg_options = cfg_options.clone();
552
553         let overrides = match override_cfg {
554             CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
555             CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
556         };
557
558         // Add test cfg for local crates
559         if cargo[pkg].is_local {
560             cfg_options.insert_atom("test".into());
561         }
562
563         if let Some(overrides) = overrides {
564             // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
565             // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
566             // working on rust-lang/rust as that's the only time it appears outside sysroot).
567             //
568             // A more ideal solution might be to reanalyze crates based on where the cursor is and
569             // figure out the set of cfgs that would have to apply to make it active.
570
571             cfg_options.apply_diff(overrides.clone());
572         };
573
574         has_private |= cargo[pkg].metadata.rustc_private;
575         let mut lib_tgt = None;
576         for &tgt in cargo[pkg].targets.iter() {
577             if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
578                 // For non-workspace-members, Cargo does not resolve dev-dependencies, so we don't
579                 // add any targets except the library target, since those will not work correctly if
580                 // they use dev-dependencies.
581                 // In fact, they can break quite badly if multiple client workspaces get merged:
582                 // https://github.com/rust-lang/rust-analyzer/issues/11300
583                 continue;
584             }
585
586             if let Some(file_id) = load(&cargo[tgt].root) {
587                 let crate_id = add_target_crate_root(
588                     &mut crate_graph,
589                     &cargo[pkg],
590                     build_scripts.get_output(pkg),
591                     cfg_options.clone(),
592                     &mut |path| load_proc_macro(&cargo[tgt].name, path),
593                     file_id,
594                     &cargo[tgt].name,
595                     cargo[tgt].is_proc_macro,
596                 );
597                 if cargo[tgt].kind == TargetKind::Lib {
598                     lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
599                     pkg_to_lib_crate.insert(pkg, crate_id);
600                 }
601                 if let Some(proc_macro) = libproc_macro {
602                     add_dep_with_prelude(
603                         &mut crate_graph,
604                         crate_id,
605                         CrateName::new("proc_macro").unwrap(),
606                         proc_macro,
607                         cargo[tgt].is_proc_macro,
608                     );
609                 }
610
611                 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
612             }
613         }
614
615         // Set deps to the core, std and to the lib target of the current package
616         for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
617             // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
618             public_deps.add(*from, &mut crate_graph);
619
620             if let Some((to, name)) = lib_tgt.clone() {
621                 if to != *from && *kind != TargetKind::BuildScript {
622                     // (build script can not depend on its library target)
623
624                     // For root projects with dashes in their name,
625                     // cargo metadata does not do any normalization,
626                     // so we do it ourselves currently
627                     let name = CrateName::normalize_dashes(&name);
628                     add_dep(&mut crate_graph, *from, name, to);
629                 }
630             }
631         }
632     }
633
634     // Now add a dep edge from all targets of upstream to the lib
635     // target of downstream.
636     for pkg in cargo.packages() {
637         for dep in cargo[pkg].dependencies.iter() {
638             let name = CrateName::new(&dep.name).unwrap();
639             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
640                 for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
641                     if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
642                         // Only build scripts may depend on build dependencies.
643                         continue;
644                     }
645                     if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
646                         // Build scripts may only depend on build dependencies.
647                         continue;
648                     }
649
650                     add_dep(&mut crate_graph, *from, name.clone(), to)
651                 }
652             }
653         }
654     }
655
656     if has_private {
657         // If the user provided a path to rustc sources, we add all the rustc_private crates
658         // and create dependencies on them for the crates which opt-in to that
659         if let Some(rustc_workspace) = rustc {
660             handle_rustc_crates(
661                 rustc_workspace,
662                 load,
663                 &mut crate_graph,
664                 &cfg_options,
665                 override_cfg,
666                 load_proc_macro,
667                 &mut pkg_to_lib_crate,
668                 &public_deps,
669                 cargo,
670                 &pkg_crates,
671                 build_scripts,
672             );
673         }
674     }
675     crate_graph
676 }
677
678 fn detached_files_to_crate_graph(
679     rustc_cfg: Vec<CfgFlag>,
680     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
681     detached_files: &[AbsPathBuf],
682     sysroot: &Sysroot,
683 ) -> CrateGraph {
684     let _p = profile::span("detached_files_to_crate_graph");
685     let mut crate_graph = CrateGraph::default();
686     let (public_deps, _libproc_macro) =
687         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
688
689     let mut cfg_options = CfgOptions::default();
690     cfg_options.extend(rustc_cfg);
691
692     for detached_file in detached_files {
693         let file_id = match load(detached_file) {
694             Some(file_id) => file_id,
695             None => {
696                 tracing::error!("Failed to load detached file {:?}", detached_file);
697                 continue;
698             }
699         };
700         let display_name = detached_file
701             .file_stem()
702             .and_then(|os_str| os_str.to_str())
703             .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
704         let detached_file_crate = crate_graph.add_crate_root(
705             file_id,
706             Edition::CURRENT,
707             display_name,
708             None,
709             cfg_options.clone(),
710             cfg_options.clone(),
711             Env::default(),
712             Ok(Vec::new()),
713             false,
714             CrateOrigin::CratesIo { repo: None },
715         );
716
717         public_deps.add(detached_file_crate, &mut crate_graph);
718     }
719     crate_graph
720 }
721
722 fn handle_rustc_crates(
723     rustc_workspace: &CargoWorkspace,
724     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
725     crate_graph: &mut CrateGraph,
726     cfg_options: &CfgOptions,
727     override_cfg: &CfgOverrides,
728     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
729     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
730     public_deps: &SysrootPublicDeps,
731     cargo: &CargoWorkspace,
732     pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
733     build_scripts: &WorkspaceBuildScripts,
734 ) {
735     let mut rustc_pkg_crates = FxHashMap::default();
736     // The root package of the rustc-dev component is rustc_driver, so we match that
737     let root_pkg =
738         rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
739     // The rustc workspace might be incomplete (such as if rustc-dev is not
740     // installed for the current toolchain) and `rustc_source` is set to discover.
741     if let Some(root_pkg) = root_pkg {
742         // Iterate through every crate in the dependency subtree of rustc_driver using BFS
743         let mut queue = VecDeque::new();
744         queue.push_back(root_pkg);
745         while let Some(pkg) = queue.pop_front() {
746             // Don't duplicate packages if they are dependended on a diamond pattern
747             // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
748             // which is not ideal
749             if rustc_pkg_crates.contains_key(&pkg) {
750                 continue;
751             }
752             for dep in &rustc_workspace[pkg].dependencies {
753                 queue.push_back(dep.pkg);
754             }
755
756             let mut cfg_options = cfg_options.clone();
757
758             let overrides = match override_cfg {
759                 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
760                 CfgOverrides::Selective(cfg_overrides) => {
761                     cfg_overrides.get(&rustc_workspace[pkg].name)
762                 }
763             };
764
765             if let Some(overrides) = overrides {
766                 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
767                 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
768                 // working on rust-lang/rust as that's the only time it appears outside sysroot).
769                 //
770                 // A more ideal solution might be to reanalyze crates based on where the cursor is and
771                 // figure out the set of cfgs that would have to apply to make it active.
772
773                 cfg_options.apply_diff(overrides.clone());
774             };
775
776             for &tgt in rustc_workspace[pkg].targets.iter() {
777                 if rustc_workspace[tgt].kind != TargetKind::Lib {
778                     continue;
779                 }
780                 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
781                     let crate_id = add_target_crate_root(
782                         crate_graph,
783                         &rustc_workspace[pkg],
784                         build_scripts.get_output(pkg),
785                         cfg_options.clone(),
786                         &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
787                         file_id,
788                         &rustc_workspace[tgt].name,
789                         rustc_workspace[tgt].is_proc_macro,
790                     );
791                     pkg_to_lib_crate.insert(pkg, crate_id);
792                     // Add dependencies on core / std / alloc for this crate
793                     public_deps.add(crate_id, crate_graph);
794                     rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
795                 }
796             }
797         }
798     }
799     // Now add a dep edge from all targets of upstream to the lib
800     // target of downstream.
801     for pkg in rustc_pkg_crates.keys().copied() {
802         for dep in rustc_workspace[pkg].dependencies.iter() {
803             let name = CrateName::new(&dep.name).unwrap();
804             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
805                 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
806                     add_dep(crate_graph, from, name.clone(), to);
807                 }
808             }
809         }
810     }
811     // Add a dependency on the rustc_private crates for all targets of each package
812     // which opts in
813     for dep in rustc_workspace.packages() {
814         let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
815
816         if let Some(&to) = pkg_to_lib_crate.get(&dep) {
817             for pkg in cargo.packages() {
818                 let package = &cargo[pkg];
819                 if !package.metadata.rustc_private {
820                     continue;
821                 }
822                 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
823                     // Avoid creating duplicate dependencies
824                     // This avoids the situation where `from` depends on e.g. `arrayvec`, but
825                     // `rust_analyzer` thinks that it should use the one from the `rustc_source`
826                     // instead of the one from `crates.io`
827                     if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
828                         add_dep(crate_graph, *from, name.clone(), to);
829                     }
830                 }
831             }
832         }
833     }
834 }
835
836 fn add_target_crate_root(
837     crate_graph: &mut CrateGraph,
838     pkg: &PackageData,
839     build_data: Option<&BuildScriptOutput>,
840     cfg_options: CfgOptions,
841     load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
842     file_id: FileId,
843     cargo_name: &str,
844     is_proc_macro: bool,
845 ) -> CrateId {
846     let edition = pkg.edition;
847     let mut potential_cfg_options = cfg_options.clone();
848     potential_cfg_options.extend(
849         pkg.features
850             .iter()
851             .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
852     );
853     let cfg_options = {
854         let mut opts = cfg_options;
855         for feature in pkg.active_features.iter() {
856             opts.insert_key_value("feature".into(), feature.into());
857         }
858         if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
859             opts.extend(cfgs.iter().cloned());
860         }
861         opts
862     };
863
864     let mut env = Env::default();
865     inject_cargo_env(pkg, &mut env);
866
867     if let Some(envs) = build_data.map(|it| &it.envs) {
868         for (k, v) in envs {
869             env.set(k, v.clone());
870         }
871     }
872
873     let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
874         Some(Some(it)) => load_proc_macro(it),
875         Some(None) => Err("no proc macro dylib present".into()),
876         None => Err("crate has not (yet) been built".into()),
877     };
878
879     let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
880     crate_graph.add_crate_root(
881         file_id,
882         edition,
883         Some(display_name),
884         Some(pkg.version.to_string()),
885         cfg_options,
886         potential_cfg_options,
887         env,
888         proc_macro,
889         is_proc_macro,
890         CrateOrigin::CratesIo { repo: pkg.repository.clone() },
891     )
892 }
893
894 #[derive(Default)]
895 struct SysrootPublicDeps {
896     deps: Vec<(CrateName, CrateId, bool)>,
897 }
898
899 impl SysrootPublicDeps {
900     /// Makes `from` depend on the public sysroot crates.
901     fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) {
902         for (name, krate, prelude) in &self.deps {
903             add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude);
904         }
905     }
906 }
907
908 fn sysroot_to_crate_graph(
909     crate_graph: &mut CrateGraph,
910     sysroot: &Sysroot,
911     rustc_cfg: Vec<CfgFlag>,
912     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
913 ) -> (SysrootPublicDeps, Option<CrateId>) {
914     let _p = profile::span("sysroot_to_crate_graph");
915     let mut cfg_options = CfgOptions::default();
916     cfg_options.extend(rustc_cfg);
917     let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
918         .crates()
919         .filter_map(|krate| {
920             let file_id = load(&sysroot[krate].root)?;
921
922             let env = Env::default();
923             let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
924             let crate_id = crate_graph.add_crate_root(
925                 file_id,
926                 Edition::CURRENT,
927                 Some(display_name),
928                 None,
929                 cfg_options.clone(),
930                 cfg_options.clone(),
931                 env,
932                 Err("no proc macro loaded for sysroot crate".into()),
933                 false,
934                 CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
935             );
936             Some((krate, crate_id))
937         })
938         .collect();
939
940     for from in sysroot.crates() {
941         for &to in sysroot[from].deps.iter() {
942             let name = CrateName::new(&sysroot[to].name).unwrap();
943             if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
944                 add_dep(crate_graph, from, name, to);
945             }
946         }
947     }
948
949     let public_deps = SysrootPublicDeps {
950         deps: sysroot
951             .public_deps()
952             .map(|(name, idx, prelude)| {
953                 (CrateName::new(name).unwrap(), sysroot_crates[&idx], prelude)
954             })
955             .collect::<Vec<_>>(),
956     };
957
958     let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
959     (public_deps, libproc_macro)
960 }
961
962 fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
963     add_dep_inner(graph, from, Dependency::new(name, to))
964 }
965
966 fn add_dep_with_prelude(
967     graph: &mut CrateGraph,
968     from: CrateId,
969     name: CrateName,
970     to: CrateId,
971     prelude: bool,
972 ) {
973     add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
974 }
975
976 fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
977     if let Err(err) = graph.add_dep(from, dep) {
978         tracing::error!("{}", err)
979     }
980 }
981
982 /// Recreates the compile-time environment variables that Cargo sets.
983 ///
984 /// Should be synced with
985 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
986 ///
987 /// FIXME: ask Cargo to provide this data instead of re-deriving.
988 fn inject_cargo_env(package: &PackageData, env: &mut Env) {
989     // FIXME: Missing variables:
990     // CARGO_BIN_NAME, CARGO_BIN_EXE_<name>
991
992     let manifest_dir = package.manifest.parent();
993     env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned());
994
995     // Not always right, but works for common cases.
996     env.set("CARGO", "cargo".into());
997
998     env.set("CARGO_PKG_VERSION", package.version.to_string());
999     env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string());
1000     env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string());
1001     env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string());
1002     env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string());
1003
1004     env.set("CARGO_PKG_AUTHORS", String::new());
1005
1006     env.set("CARGO_PKG_NAME", package.name.clone());
1007     // FIXME: This isn't really correct (a package can have many crates with different names), but
1008     // it's better than leaving the variable unset.
1009     env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string());
1010     env.set("CARGO_PKG_DESCRIPTION", String::new());
1011     env.set("CARGO_PKG_HOMEPAGE", String::new());
1012     env.set("CARGO_PKG_REPOSITORY", String::new());
1013     env.set("CARGO_PKG_LICENSE", String::new());
1014
1015     env.set("CARGO_PKG_LICENSE_FILE", String::new());
1016 }