1use std::thread::Builder;
6use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
7
8use anyhow::Context;
9use base_db::{
10 CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
11 CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError,
12 ProcMacroPaths, target::TargetLoadResult,
13};
14use cfg::{CfgAtom, CfgDiff, CfgOptions};
15use intern::{Symbol, sym};
16use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
17use rustc_hash::{FxHashMap, FxHashSet};
18use semver::Version;
19use span::{Edition, FileId};
20use toolchain::Tool;
21use tracing::instrument;
22use tracing::{debug, error, info};
23use triomphe::Arc;
24
25use crate::{
26 CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package,
27 ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
28 WorkspaceBuildScripts,
29 build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
30 cargo_config_file::CargoConfigFile,
31 cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
32 env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
33 project_json::{Crate, CrateArrayIdx},
34 sysroot::RustLibSrcWorkspace,
35 toolchain_info::{QueryConfig, rustc_cfg, target_data, target_tuple, version},
36 utf8_stdout,
37};
38
39pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option<FileId>;
40
41#[derive(Debug, Clone, Eq, PartialEq, Hash)]
45pub struct PackageRoot {
46 pub is_local: bool,
48 pub include: Vec<AbsPathBuf>,
50 pub exclude: Vec<AbsPathBuf>,
52}
53
54#[derive(Clone)]
55pub struct ProjectWorkspace {
56 pub kind: ProjectWorkspaceKind,
57 pub sysroot: Sysroot,
59 pub rustc_cfg: Vec<CfgAtom>,
64 pub toolchain: Option<Version>,
66 pub target: TargetLoadResult,
68 pub cfg_overrides: CfgOverrides,
70 pub extra_includes: Vec<AbsPathBuf>,
72 pub set_test: bool,
74}
75
76#[derive(Clone)]
77#[allow(clippy::large_enum_variant)]
78pub enum ProjectWorkspaceKind {
79 Cargo {
81 cargo: CargoWorkspace,
83 error: Option<Arc<anyhow::Error>>,
85 build_scripts: WorkspaceBuildScripts,
87 rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
90 },
91 Json(ProjectJson),
93 DetachedFile {
104 file: ManifestPath,
106 cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>,
108 },
109}
110
111impl fmt::Debug for ProjectWorkspace {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 let Self {
115 kind,
116 sysroot,
117 rustc_cfg,
118 toolchain,
119 target: target_layout,
120 cfg_overrides,
121 extra_includes,
122 set_test,
123 } = self;
124 match kind {
125 ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc } => f
126 .debug_struct("Cargo")
127 .field("root", &cargo.workspace_root().file_name())
128 .field("n_packages", &cargo.packages().len())
129 .field("n_sysroot_crates", &sysroot.num_packages())
130 .field(
131 "n_rustc_compiler_crates",
132 &rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(rc, _)| rc.packages().len()),
133 )
134 .field("n_rustc_cfg", &rustc_cfg.len())
135 .field("n_cfg_overrides", &cfg_overrides.len())
136 .field("n_extra_includes", &extra_includes.len())
137 .field("toolchain", &toolchain)
138 .field("data_layout", &target_layout)
139 .field("set_test", set_test)
140 .field("build_scripts", &build_scripts.error().unwrap_or("ok"))
141 .finish(),
142 ProjectWorkspaceKind::Json(project) => {
143 let mut debug_struct = f.debug_struct("Json");
144 debug_struct
145 .field("n_crates", &project.n_crates())
146 .field("n_sysroot_crates", &sysroot.num_packages())
147 .field("n_rustc_cfg", &rustc_cfg.len())
148 .field("toolchain", &toolchain)
149 .field("data_layout", &target_layout)
150 .field("n_cfg_overrides", &cfg_overrides.len())
151 .field("n_extra_includes", &extra_includes.len())
152 .field("set_test", set_test);
153
154 debug_struct.finish()
155 }
156 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script } => f
157 .debug_struct("DetachedFiles")
158 .field("file", &file)
159 .field("cargo_script", &cargo_script.is_some())
160 .field("n_sysroot_crates", &sysroot.num_packages())
161 .field("n_rustc_cfg", &rustc_cfg.len())
162 .field("toolchain", &toolchain)
163 .field("data_layout", &target_layout)
164 .field("n_cfg_overrides", &cfg_overrides.len())
165 .field("n_extra_includes", &extra_includes.len())
166 .field("set_test", set_test)
167 .finish(),
168 }
169 }
170}
171
172impl ProjectWorkspace {
173 pub fn load(
174 manifest: ProjectManifest,
175 config: &CargoConfig,
176 progress: &(dyn Fn(String) + Sync),
177 ) -> anyhow::Result<ProjectWorkspace> {
178 ProjectWorkspace::load_inner(&manifest, config, progress)
179 .with_context(|| format!("Failed to load the project at {manifest}"))
180 }
181
182 fn load_inner(
183 manifest: &ProjectManifest,
184 config: &CargoConfig,
185 progress: &(dyn Fn(String) + Sync),
186 ) -> anyhow::Result<ProjectWorkspace> {
187 let res = match manifest {
188 ProjectManifest::ProjectJson(project_json) => {
189 let file = fs::read_to_string(project_json)
190 .with_context(|| format!("Failed to read json file {project_json}"))?;
191 let data = serde_json::from_str(&file)
192 .with_context(|| format!("Failed to deserialize json file {project_json}"))?;
193 let project_location = project_json.parent().to_path_buf();
194 let project_json: ProjectJson =
195 ProjectJson::new(Some(project_json.clone()), &project_location, data);
196 ProjectWorkspace::load_inline(project_json, config, progress)
197 }
198 ProjectManifest::CargoScript(rust_file) => {
199 ProjectWorkspace::load_detached_file(rust_file, config)?
200 }
201 ProjectManifest::CargoToml(cargo_toml) => {
202 ProjectWorkspace::load_cargo(cargo_toml, config, progress)?
203 }
204 };
205
206 Ok(res)
207 }
208
209 fn load_cargo(
210 cargo_toml: &ManifestPath,
211 config: &CargoConfig,
212 progress: &(dyn Fn(String) + Sync),
213 ) -> Result<ProjectWorkspace, anyhow::Error> {
214 progress("discovering sysroot".to_owned());
215 let CargoConfig {
216 features,
217 rustc_source,
218 extra_args,
219 metadata_extra_args,
220 extra_env,
221 set_test,
222 cfg_overrides,
223 extra_includes,
224 sysroot,
225 sysroot_src,
226 target,
227 no_deps,
228 ..
229 } = config;
230 let workspace_dir = cargo_toml.parent();
231 let mut sysroot = match (sysroot, sysroot_src) {
232 (Some(RustLibSource::Discover), None) => Sysroot::discover(workspace_dir, extra_env),
233 (Some(RustLibSource::Discover), Some(sysroot_src)) => {
234 Sysroot::discover_with_src_override(workspace_dir, extra_env, sysroot_src.clone())
235 }
236 (Some(RustLibSource::Path(path)), None) => {
237 Sysroot::discover_rust_lib_src_dir(path.clone())
238 }
239 (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
240 Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone()))
241 }
242 (None, _) => Sysroot::empty(),
243 };
244
245 let mut cmd = sysroot.tool(Tool::Cargo, workspace_dir, extra_env);
247 cmd.args(["locate-project", "--workspace", "--manifest-path", cargo_toml.as_str()]);
248 let cargo_toml = &match utf8_stdout(&mut cmd) {
249 Ok(output) => {
250 #[derive(serde_derive::Deserialize)]
251 struct Root {
252 root: Utf8PathBuf,
253 }
254 match serde_json::from_str::<Root>(&output) {
255 Ok(object) => ManifestPath::try_from(AbsPathBuf::assert(object.root))
256 .expect("manifest path should be absolute"),
257 Err(e) => {
258 tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root");
259 cargo_toml.clone()
260 }
261 }
262 }
263 Err(e) => {
264 tracing::error!(%e, %cargo_toml, "failed fetching cargo workspace root");
265 cargo_toml.clone()
266 }
267 };
268 let workspace_dir = cargo_toml.parent();
269
270 tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
271 progress("querying project metadata".to_owned());
272 let config_file = CargoConfigFile::load(cargo_toml, extra_env, &sysroot);
273 let config_file_ = config_file.clone();
274 let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
275 let targets =
276 target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
277 let toolchain = version::get(toolchain_config, extra_env)
278 .inspect_err(|e| {
279 tracing::error!(%e,
280 "failed fetching toolchain version for {cargo_toml:?} workspace"
281 )
282 })
283 .ok()
284 .flatten();
285
286 let fetch_metadata = FetchMetadata::new(
287 cargo_toml,
288 workspace_dir,
289 &CargoMetadataConfig {
290 features: features.clone(),
291 targets: targets.clone(),
292 extra_args: extra_args.clone(),
293 metadata_extra_args: metadata_extra_args.clone(),
294 extra_env: extra_env.clone(),
295 toolchain_version: toolchain.clone(),
296 kind: "workspace",
297 },
298 &sysroot,
299 *no_deps,
300 );
301
302 let join = thread::scope(|s| {
307 let rustc_cfg = Builder::new()
308 .name("ProjectWorkspace::rustc_cfg".to_owned())
309 .spawn_scoped(s, || {
310 rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
311 })
312 .expect("failed to spawn thread");
313 let target_data = Builder::new()
314 .name("ProjectWorkspace::target_data".to_owned())
315 .spawn_scoped(s, || {
316 target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
317 .inspect_err(|e| {
318 tracing::error!(%e,
319 "failed fetching data layout for \
320 {cargo_toml:?} workspace"
321 )
322 })
323 })
324 .expect("failed to spawn thread");
325
326 let rustc_dir = Builder::new()
327 .name("ProjectWorkspace::rustc_dir".to_owned())
328 .spawn_scoped(s, || {
329 let rustc_dir = match rustc_source {
330 Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
331 .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
332 Some(RustLibSource::Discover) => {
333 sysroot.discover_rustc_src().ok_or_else(|| {
334 Some("Failed to discover rustc source for sysroot.".to_owned())
335 })
336 }
337 None => Err(None),
338 };
339 rustc_dir.and_then(|rustc_dir| {
340 info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
341 match FetchMetadata::new(
342 &rustc_dir,
343 workspace_dir,
344 &CargoMetadataConfig {
345 features: crate::CargoFeatures::default(),
346 targets: targets.clone(),
347 extra_args: extra_args.clone(),
348 metadata_extra_args: metadata_extra_args.clone(),
349 extra_env: extra_env.clone(),
350 toolchain_version: toolchain.clone(),
351 kind: "rustc-dev"
352 },
353 &sysroot,
354 *no_deps,
355 ).exec(true, progress) {
356 Ok((meta, _error)) => {
357 let workspace = CargoWorkspace::new(
358 meta,
359 cargo_toml.clone(),
360 Env::default(),
361 false,
362 );
363 let build_scripts = WorkspaceBuildScripts::rustc_crates(
364 &workspace,
365 workspace_dir,
366 extra_env,
367 &sysroot,
368 );
369 Ok(Box::new((workspace, build_scripts)))
370 }
371 Err(e) => {
372 tracing::error!(
373 %e,
374 "Failed to read Cargo metadata from rustc source \
375 at {rustc_dir}",
376 );
377 Err(Some(format!(
378 "Failed to read Cargo metadata from rustc source \
379 at {rustc_dir}: {e}"
380 )))
381 }
382 }
383 })
384 })
385 .expect("failed to spawn thread");
386
387 let cargo_metadata = Builder::new()
388 .name("ProjectWorkspace::cargo_metadata".to_owned())
389 .spawn_scoped(s, || fetch_metadata.exec(false, progress))
390 .expect("failed to spawn thread");
391 let loaded_sysroot = Builder::new()
392 .name("ProjectWorkspace::loaded_sysroot".to_owned())
393 .spawn_scoped(s, || {
394 sysroot.load_workspace(
395 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
396 config,
397 workspace_dir,
398 &targets,
399 toolchain.clone(),
400 )),
401 config.no_deps,
402 progress,
403 )
404 })
405 .expect("failed to spawn thread");
406 let cargo_env = Builder::new()
407 .name("ProjectWorkspace::cargo_env".to_owned())
408 .spawn_scoped(s, move || cargo_config_env(&config_file, &config.extra_env))
409 .expect("failed to spawn thread");
410 thread::Result::Ok((
411 rustc_cfg.join()?,
412 target_data.join()?,
413 rustc_dir.join()?,
414 loaded_sysroot.join()?,
415 cargo_metadata.join()?,
416 cargo_env.join()?,
417 ))
418 });
419
420 let (rustc_cfg, data_layout, mut rustc, loaded_sysroot, cargo_metadata, mut cargo_env) =
421 match join {
422 Ok(it) => it,
423 Err(e) => std::panic::resume_unwind(e),
424 };
425
426 for (key, value) in config.extra_env.iter() {
427 if let Some(value) = value {
428 cargo_env.insert(key.clone(), value.clone());
429 }
430 }
431
432 let (meta, error) = cargo_metadata.with_context(|| {
433 format!(
434 "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
435 )
436 })?;
437 let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_env, false);
438 if let Some(loaded_sysroot) = loaded_sysroot {
439 tracing::info!(src_root = ?sysroot.rust_lib_src_root(), root = %loaded_sysroot, "Loaded sysroot");
440 sysroot.set_workspace(loaded_sysroot);
441 }
442
443 if !cargo.requires_rustc_private()
444 && let Err(e) = &mut rustc
445 {
446 _ = e.take();
449 }
450
451 Ok(ProjectWorkspace {
452 kind: ProjectWorkspaceKind::Cargo {
453 cargo,
454 build_scripts: WorkspaceBuildScripts::default(),
455 rustc,
456 error: error.map(Arc::new),
457 },
458 sysroot,
459 rustc_cfg,
460 cfg_overrides: cfg_overrides.clone(),
461 toolchain,
462 target: data_layout.map_err(|it| it.to_string().into()),
463 extra_includes: extra_includes.clone(),
464 set_test: *set_test,
465 })
466 }
467
468 pub fn load_inline(
469 mut project_json: ProjectJson,
470 config: &CargoConfig,
471 progress: &(dyn Fn(String) + Sync),
472 ) -> ProjectWorkspace {
473 progress("discovering sysroot".to_owned());
474 let mut sysroot =
475 Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
476
477 tracing::info!(workspace = %project_json.manifest_or_root(), src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
478 progress("querying project metadata".to_owned());
479 let sysroot_project = project_json.sysroot_project.take();
480 let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
481 let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
482 .unwrap_or_default();
483 let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
484
485 let join = thread::scope(|s| {
490 let rustc_cfg = s.spawn(|| {
491 rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
492 });
493 let data_layout = s.spawn(|| {
494 target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
495 });
496 let loaded_sysroot = s.spawn(|| {
497 if let Some(sysroot_project) = sysroot_project {
498 sysroot.load_workspace(
499 &RustSourceWorkspaceConfig::Json(*sysroot_project),
500 config.no_deps,
501 progress,
502 )
503 } else {
504 sysroot.load_workspace(
505 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
506 config,
507 project_json.project_root(),
508 &targets,
509 toolchain.clone(),
510 )),
511 config.no_deps,
512 progress,
513 )
514 }
515 });
516
517 thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?))
518 });
519
520 let (rustc_cfg, target_data, loaded_sysroot) = match join {
521 Ok(it) => it,
522 Err(e) => std::panic::resume_unwind(e),
523 };
524
525 if let Some(loaded_sysroot) = loaded_sysroot {
526 sysroot.set_workspace(loaded_sysroot);
527 }
528
529 ProjectWorkspace {
530 kind: ProjectWorkspaceKind::Json(project_json),
531 sysroot,
532 rustc_cfg,
533 toolchain,
534 target: target_data.map_err(|it| it.to_string().into()),
535 cfg_overrides: config.cfg_overrides.clone(),
536 extra_includes: config.extra_includes.clone(),
537 set_test: config.set_test,
538 }
539 }
540
541 pub fn load_detached_file(
542 detached_file: &ManifestPath,
543 config: &CargoConfig,
544 ) -> anyhow::Result<ProjectWorkspace> {
545 let dir = detached_file.parent();
546 let mut sysroot = match &config.sysroot {
547 Some(RustLibSource::Path(path)) => Sysroot::discover_rust_lib_src_dir(path.clone()),
548 Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env),
549 None => Sysroot::empty(),
550 };
551
552 let config_file = CargoConfigFile::load(detached_file, &config.extra_env, &sysroot);
553 let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
554 let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
555 let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
556 .unwrap_or_default();
557 let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
558 let target_data = target_data::get(query_config, None, &config.extra_env);
559
560 let loaded_sysroot = sysroot.load_workspace(
561 &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
562 config,
563 dir,
564 &targets,
565 toolchain.clone(),
566 )),
567 config.no_deps,
568 &|_| (),
569 );
570 if let Some(loaded_sysroot) = loaded_sysroot {
571 sysroot.set_workspace(loaded_sysroot);
572 }
573
574 let fetch_metadata = FetchMetadata::new(
575 detached_file,
576 dir,
577 &CargoMetadataConfig {
578 features: config.features.clone(),
579 targets,
580 extra_args: config.extra_args.clone(),
581 metadata_extra_args: config.metadata_extra_args.clone(),
582 extra_env: config.extra_env.clone(),
583 toolchain_version: toolchain.clone(),
584 kind: "detached-file",
585 },
586 &sysroot,
587 config.no_deps,
588 );
589 let cargo_script = fetch_metadata.exec(false, &|_| ()).ok().map(|(ws, error)| {
590 let cargo_config_extra_env = cargo_config_env(&config_file, &config.extra_env);
591 (
592 CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
593 WorkspaceBuildScripts::default(),
594 error.map(Arc::new),
595 )
596 });
597
598 Ok(ProjectWorkspace {
599 kind: ProjectWorkspaceKind::DetachedFile {
600 file: detached_file.to_owned(),
601 cargo: cargo_script,
602 },
603 sysroot,
604 rustc_cfg,
605 toolchain,
606 target: target_data.map_err(|it| it.to_string().into()),
607 cfg_overrides: config.cfg_overrides.clone(),
608 extra_includes: config.extra_includes.clone(),
609 set_test: config.set_test,
610 })
611 }
612
613 pub fn load_detached_files(
614 detached_files: Vec<ManifestPath>,
615 config: &CargoConfig,
616 ) -> Vec<anyhow::Result<ProjectWorkspace>> {
617 detached_files.into_iter().map(|file| Self::load_detached_file(&file, config)).collect()
618 }
619
620 pub fn run_build_scripts(
622 &self,
623 config: &CargoConfig,
624 progress: &dyn Fn(String),
625 ) -> anyhow::Result<WorkspaceBuildScripts> {
626 match &self.kind {
627 ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, None)), .. }
628 | ProjectWorkspaceKind::Cargo { cargo, error: None, .. } => {
629 WorkspaceBuildScripts::run_for_workspace(
630 config,
631 cargo,
632 progress,
633 &self.sysroot,
634 self.toolchain.as_ref(),
635 )
636 .with_context(|| {
637 format!("Failed to run build scripts for {}", cargo.workspace_root())
638 })
639 }
640 _ => Ok(WorkspaceBuildScripts::default()),
641 }
642 }
643
644 pub fn run_all_build_scripts(
647 workspaces: &[ProjectWorkspace],
648 config: &CargoConfig,
649 progress: &dyn Fn(String),
650 working_directory: &AbsPathBuf,
651 ) -> Vec<anyhow::Result<WorkspaceBuildScripts>> {
652 if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
653 || config.run_build_script_command.is_none()
654 {
655 return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
656 }
657
658 let cargo_ws: Vec<_> = workspaces
659 .iter()
660 .filter_map(|it| match &it.kind {
661 ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo),
662 _ => None,
663 })
664 .collect();
665 let outputs = &mut match WorkspaceBuildScripts::run_once(
666 config,
667 &cargo_ws,
668 progress,
669 working_directory,
670 ) {
671 Ok(it) => Ok(it.into_iter()),
672 Err(e) => Err(sync::Arc::new(e)),
674 };
675
676 workspaces
677 .iter()
678 .map(|it| match &it.kind {
679 ProjectWorkspaceKind::Cargo { cargo, .. } => match outputs {
680 Ok(outputs) => Ok(outputs.next().unwrap()),
681 Err(e) => Err(e.clone()).with_context(|| {
682 format!("Failed to run build scripts for {}", cargo.workspace_root())
683 }),
684 },
685 _ => Ok(WorkspaceBuildScripts::default()),
686 })
687 .collect()
688 }
689
690 pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
691 match &mut self.kind {
692 ProjectWorkspaceKind::Cargo { build_scripts, .. }
693 | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts, _)), .. } => {
694 *build_scripts = bs
695 }
696 _ => assert_eq!(bs, WorkspaceBuildScripts::default()),
697 }
698 }
699
700 pub fn manifest_or_root(&self) -> &AbsPath {
701 match &self.kind {
702 ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.manifest_path(),
703 ProjectWorkspaceKind::Json(project) => project.manifest_or_root(),
704 ProjectWorkspaceKind::DetachedFile { file, .. } => file,
705 }
706 }
707
708 pub fn workspace_root(&self) -> &AbsPath {
709 match &self.kind {
710 ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.workspace_root(),
711 ProjectWorkspaceKind::Json(project) => project.project_root(),
712 ProjectWorkspaceKind::DetachedFile { file, .. } => file.parent(),
713 }
714 }
715
716 pub fn manifest(&self) -> Option<&ManifestPath> {
717 match &self.kind {
718 ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo.manifest_path()),
719 ProjectWorkspaceKind::Json(project) => project.manifest(),
720 ProjectWorkspaceKind::DetachedFile { cargo, .. } => {
721 Some(cargo.as_ref()?.0.manifest_path())
722 }
723 }
724 }
725
726 pub fn buildfiles(&self) -> Vec<AbsPathBuf> {
727 match &self.kind {
728 ProjectWorkspaceKind::Json(project) => project
729 .crates()
730 .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone()))
731 .map(|build_file| self.workspace_root().join(build_file))
732 .collect(),
733 _ => vec![],
734 }
735 }
736
737 pub fn find_sysroot_proc_macro_srv(&self) -> Option<anyhow::Result<AbsPathBuf>> {
738 self.sysroot.discover_proc_macro_srv()
739 }
740
741 pub fn to_roots(&self) -> Vec<PackageRoot> {
745 let mk_sysroot = || {
746 let mut r = match self.sysroot.workspace() {
747 RustLibSrcWorkspace::Workspace { ws, .. } => ws
748 .packages()
749 .filter_map(|pkg| {
750 if ws[pkg].is_local {
751 return None;
753 }
754 let pkg_root = ws[pkg].manifest.parent().to_path_buf();
755
756 let include = vec![pkg_root.clone()];
757
758 let exclude = vec![
759 pkg_root.join(".git"),
760 pkg_root.join("target"),
761 pkg_root.join("tests"),
762 pkg_root.join("examples"),
763 pkg_root.join("benches"),
764 ];
765 Some(PackageRoot { is_local: false, include, exclude })
766 })
767 .collect(),
768 RustLibSrcWorkspace::Json(project_json) => project_json
769 .crates()
770 .map(|(_, krate)| PackageRoot {
771 is_local: false,
772 include: krate.include.clone(),
773 exclude: krate.exclude.clone(),
774 })
775 .collect(),
776 RustLibSrcWorkspace::Stitched(_) => vec![],
777 RustLibSrcWorkspace::Empty => vec![],
778 };
779
780 r.push(PackageRoot {
781 is_local: false,
782 include: self
783 .sysroot
784 .rust_lib_src_root()
785 .map(|it| it.to_path_buf())
786 .into_iter()
787 .collect(),
788 exclude: Vec::new(),
789 });
790 r
791 };
792 match &self.kind {
793 ProjectWorkspaceKind::Json(project) => project
794 .crates()
795 .map(|(_, krate)| PackageRoot {
796 is_local: krate.is_workspace_member,
797 include: krate
798 .include
799 .iter()
800 .cloned()
801 .chain(self.extra_includes.iter().cloned())
802 .collect(),
803 exclude: krate.exclude.clone(),
804 })
805 .collect::<FxHashSet<_>>()
806 .into_iter()
807 .chain(mk_sysroot())
808 .collect::<Vec<_>>(),
809 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
810 cargo
811 .packages()
812 .map(|pkg| {
813 let is_local = cargo[pkg].is_local;
814 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
815
816 let mut include = vec![pkg_root.clone()];
817 let out_dir =
818 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
819 include.extend(out_dir);
820
821 let extra_targets = cargo[pkg]
837 .targets
838 .iter()
839 .filter_map(|&tgt| cargo[tgt].root.parent())
840 .map(|tgt| tgt.normalize().to_path_buf())
841 .filter(|path| !path.starts_with(&pkg_root));
842 include.extend(extra_targets);
843
844 let mut exclude = vec![pkg_root.join(".git")];
845 if is_local {
846 include.extend(self.extra_includes.iter().cloned());
847
848 exclude.push(pkg_root.join("target"));
849 } else {
850 exclude.push(pkg_root.join("tests"));
851 exclude.push(pkg_root.join("examples"));
852 exclude.push(pkg_root.join("benches"));
853 }
854 include.sort();
855 include.dedup();
856 PackageRoot { is_local, include, exclude }
857 })
858 .chain(mk_sysroot())
859 .chain(rustc.iter().map(|a| a.as_ref()).flat_map(|(rustc, _)| {
860 rustc.packages().map(move |krate| PackageRoot {
861 is_local: false,
862 include: vec![rustc[krate].manifest.parent().to_path_buf()],
863 exclude: Vec::new(),
864 })
865 }))
866 .collect()
867 }
868 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
869 iter::once(PackageRoot {
870 is_local: true,
871 include: vec![file.to_path_buf()],
872 exclude: Vec::new(),
873 })
874 .chain(cargo_script.iter().flat_map(|(cargo, build_scripts, _)| {
875 cargo.packages().map(|pkg| {
876 let is_local = cargo[pkg].is_local;
877 let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
878
879 let mut include = vec![pkg_root.clone()];
880 let out_dir =
881 build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
882 include.extend(out_dir);
883
884 let extra_targets = cargo[pkg]
900 .targets
901 .iter()
902 .filter_map(|&tgt| cargo[tgt].root.parent())
903 .map(|tgt| tgt.normalize().to_path_buf())
904 .filter(|path| !path.starts_with(&pkg_root));
905 include.extend(extra_targets);
906
907 let mut exclude = vec![pkg_root.join(".git")];
908 if is_local {
909 include.extend(self.extra_includes.iter().cloned());
910
911 exclude.push(pkg_root.join("target"));
912 } else {
913 exclude.push(pkg_root.join("tests"));
914 exclude.push(pkg_root.join("examples"));
915 exclude.push(pkg_root.join("benches"));
916 }
917 include.sort();
918 include.dedup();
919 PackageRoot { is_local, include, exclude }
920 })
921 }))
922 .chain(mk_sysroot())
923 .collect()
924 }
925 }
926 }
927
928 pub fn n_packages(&self) -> usize {
929 let sysroot_package_len = self.sysroot.num_packages();
930 match &self.kind {
931 ProjectWorkspaceKind::Json(project) => sysroot_package_len + project.n_crates(),
932 ProjectWorkspaceKind::Cargo { cargo, rustc, .. } => {
933 let rustc_package_len =
934 rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(it, _)| it.packages().len());
935 cargo.packages().len() + sysroot_package_len + rustc_package_len
936 }
937 ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => {
938 sysroot_package_len
939 + cargo_script.as_ref().map_or(1, |(cargo, _, _)| cargo.packages().len())
940 }
941 }
942 }
943
944 pub fn to_crate_graph(
945 &self,
946 load: FileLoader<'_>,
947 extra_env: &FxHashMap<String, Option<String>>,
948 ) -> (CrateGraphBuilder, ProcMacroPaths) {
949 let _p = tracing::info_span!("ProjectWorkspace::to_crate_graph").entered();
950
951 let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self;
952 let crate_ws_data = Arc::new(CrateWorkspaceData {
953 toolchain: self.toolchain.clone(),
954 target: self.target.clone(),
955 });
956 let (crate_graph, proc_macros) = match kind {
957 ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph(
958 rustc_cfg.clone(),
959 load,
960 project,
961 sysroot,
962 extra_env,
963 cfg_overrides,
964 self.set_test,
965 false,
966 crate_ws_data,
967 ),
968 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _ } => {
969 cargo_to_crate_graph(
970 load,
971 rustc.as_ref().map(|a| a.as_ref()).ok(),
972 cargo,
973 sysroot,
974 rustc_cfg.clone(),
975 cfg_overrides,
976 build_scripts,
977 self.set_test,
978 crate_ws_data,
979 )
980 }
981 ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
982 if let Some((cargo, build_scripts, _)) = cargo_script {
983 cargo_to_crate_graph(
984 &mut |path| load(path),
985 None,
986 cargo,
987 sysroot,
988 rustc_cfg.clone(),
989 cfg_overrides,
990 build_scripts,
991 self.set_test,
992 crate_ws_data,
993 )
994 } else {
995 detached_file_to_crate_graph(
996 rustc_cfg.clone(),
997 load,
998 file,
999 sysroot,
1000 cfg_overrides,
1001 self.set_test,
1002 crate_ws_data,
1003 )
1004 }
1005 }
1006 };
1007
1008 (crate_graph, proc_macros)
1009 }
1010
1011 pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
1012 let Self {
1013 kind, sysroot, rustc_cfg, toolchain, target: target_layout, cfg_overrides, ..
1014 } = self;
1015 let Self {
1016 kind: o_kind,
1017 sysroot: o_sysroot,
1018 rustc_cfg: o_rustc_cfg,
1019 toolchain: o_toolchain,
1020 target: o_target_layout,
1021 cfg_overrides: o_cfg_overrides,
1022 ..
1023 } = other;
1024 (match (kind, o_kind) {
1025 (
1026 ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts: _, error: _ },
1027 ProjectWorkspaceKind::Cargo {
1028 cargo: o_cargo,
1029 rustc: o_rustc,
1030 build_scripts: _,
1031 error: _,
1032 },
1033 ) => cargo == o_cargo && rustc == o_rustc,
1034 (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => {
1035 project == o_project
1036 }
1037 (
1038 ProjectWorkspaceKind::DetachedFile { file, cargo: Some((cargo_script, _, _)) },
1039 ProjectWorkspaceKind::DetachedFile {
1040 file: o_file,
1041 cargo: Some((o_cargo_script, _, _)),
1042 },
1043 ) => file == o_file && cargo_script == o_cargo_script,
1044 _ => return false,
1045 }) && sysroot == o_sysroot
1046 && rustc_cfg == o_rustc_cfg
1047 && toolchain == o_toolchain
1048 && target_layout == o_target_layout
1049 && cfg_overrides == o_cfg_overrides
1050 }
1051
1052 #[must_use]
1056 pub fn is_json(&self) -> bool {
1057 matches!(self.kind, ProjectWorkspaceKind::Json { .. })
1058 }
1059}
1060
1061#[instrument(skip_all)]
1062fn project_json_to_crate_graph(
1063 rustc_cfg: Vec<CfgAtom>,
1064 load: FileLoader<'_>,
1065 project: &ProjectJson,
1066 sysroot: &Sysroot,
1067 extra_env: &FxHashMap<String, Option<String>>,
1068 override_cfg: &CfgOverrides,
1069 set_test: bool,
1070 is_sysroot: bool,
1071 crate_ws_data: Arc<CrateWorkspaceData>,
1072) -> (CrateGraphBuilder, ProcMacroPaths) {
1073 let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
1074 let (crate_graph, proc_macros) = &mut res;
1075 let (public_deps, libproc_macro) = sysroot_to_crate_graph(
1076 crate_graph,
1077 sysroot,
1078 rustc_cfg.clone(),
1079 load,
1080 crate_ws_data.clone(),
1082 );
1083
1084 let mut cfg_cache: FxHashMap<&str, Vec<CfgAtom>> = FxHashMap::default();
1085 let project_root = Arc::new(project.project_root().to_path_buf());
1086
1087 let idx_to_crate_id: FxHashMap<CrateArrayIdx, _> = project
1088 .crates()
1089 .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?)))
1090 .map(
1091 |(
1092 idx,
1093 Crate {
1094 display_name,
1095 edition,
1096 version,
1097 cfg,
1098 target,
1099 env,
1100 crate_attrs,
1101 proc_macro_dylib_path,
1102 is_proc_macro,
1103 repository,
1104 is_workspace_member,
1105 proc_macro_cwd,
1106 ..
1107 },
1108 file_id,
1109 )| {
1110 let mut env = env.clone().into_iter().collect::<Env>();
1111 env.extend(
1113 extra_env
1114 .iter()
1115 .filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))),
1116 );
1117
1118 let target_cfgs = match target.as_deref() {
1119 Some(target) => cfg_cache.entry(target).or_insert_with(|| {
1120 rustc_cfg::get(
1121 QueryConfig::Rustc(sysroot, project.project_root().as_ref()),
1122 Some(target),
1123 extra_env,
1124 )
1125 }),
1126 None => &rustc_cfg,
1127 };
1128
1129 let cfg_options = {
1130 let mut cfg_options: CfgOptions =
1131 target_cfgs.iter().chain(cfg.iter()).cloned().collect();
1132
1133 if *is_workspace_member {
1134 if set_test && !is_sysroot {
1135 cfg_options.insert_atom(sym::test);
1137 }
1138 cfg_options.insert_atom(sym::rust_analyzer);
1139 }
1140
1141 override_cfg.apply(
1142 &mut cfg_options,
1143 display_name
1144 .as_ref()
1145 .map(|it| it.canonical_name().as_str())
1146 .unwrap_or_default(),
1147 );
1148 cfg_options
1149 };
1150
1151 let crate_graph_crate_id = crate_graph.add_crate_root(
1152 file_id,
1153 *edition,
1154 display_name.clone(),
1155 version.clone(),
1156 cfg_options,
1157 None,
1158 env,
1159 if let Some(name) = display_name.clone() {
1160 if is_sysroot {
1161 CrateOrigin::Lang(LangCrateOrigin::from(name.canonical_name().as_str()))
1162 } else {
1163 CrateOrigin::Local {
1164 repo: repository.clone(),
1165 name: Some(name.canonical_name().to_owned()),
1166 }
1167 }
1168 } else if is_sysroot {
1169 CrateOrigin::Lang(LangCrateOrigin::Dependency)
1170 } else {
1171 CrateOrigin::Local { repo: None, name: None }
1172 },
1173 crate_attrs.clone(),
1174 *is_proc_macro,
1175 match proc_macro_cwd {
1176 Some(path) => Arc::new(path.clone()),
1177 None => project_root.clone(),
1178 },
1179 crate_ws_data.clone(),
1180 );
1181 debug!(
1182 ?crate_graph_crate_id,
1183 crate = display_name.as_ref().map(|name| name.canonical_name().as_str()),
1184 "added root to crate graph"
1185 );
1186 if *is_proc_macro && let Some(path) = proc_macro_dylib_path.clone() {
1187 let node = Ok((
1188 display_name
1189 .as_ref()
1190 .map(|it| it.canonical_name().as_str().to_owned())
1191 .unwrap_or_else(|| format!("crate{}", idx.0)),
1192 path,
1193 ));
1194 proc_macros.insert(crate_graph_crate_id, node);
1195 }
1196 (idx, crate_graph_crate_id)
1197 },
1198 )
1199 .collect();
1200
1201 debug!(map = ?idx_to_crate_id);
1202 for (from_idx, krate) in project.crates() {
1203 if let Some(&from) = idx_to_crate_id.get(&from_idx) {
1204 public_deps.add_to_crate_graph(crate_graph, from);
1205 if let Some(proc_macro) = libproc_macro {
1206 add_proc_macro_dep(crate_graph, from, proc_macro, krate.is_proc_macro);
1207 }
1208
1209 for dep in &krate.deps {
1210 if let Some(&to) = idx_to_crate_id.get(&dep.krate) {
1211 add_dep(crate_graph, from, dep.name.clone(), to);
1212 }
1213 }
1214 }
1215 }
1216 res
1217}
1218
1219fn cargo_to_crate_graph(
1220 load: FileLoader<'_>,
1221 rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
1222 cargo: &CargoWorkspace,
1223 sysroot: &Sysroot,
1224 rustc_cfg: Vec<CfgAtom>,
1225 override_cfg: &CfgOverrides,
1226 build_scripts: &WorkspaceBuildScripts,
1227 set_test: bool,
1228 crate_ws_data: Arc<CrateWorkspaceData>,
1229) -> (CrateGraphBuilder, ProcMacroPaths) {
1230 let _p = tracing::info_span!("cargo_to_crate_graph").entered();
1231 let mut res = (CrateGraphBuilder::default(), ProcMacroPaths::default());
1232 let (crate_graph, proc_macros) = &mut res;
1233 let (public_deps, libproc_macro) = sysroot_to_crate_graph(
1234 crate_graph,
1235 sysroot,
1236 rustc_cfg.clone(),
1237 load,
1238 crate_ws_data.clone(),
1239 );
1240 let cargo_path = sysroot.tool_path(Tool::Cargo, cargo.workspace_root(), cargo.env());
1241
1242 let cfg_options = CfgOptions::from_iter(rustc_cfg);
1243
1244 let mut pkg_to_lib_crate = FxHashMap::default();
1246 let mut pkg_crates = FxHashMap::default();
1247 let workspace_proc_macro_cwd = Arc::new(cargo.workspace_root().to_path_buf());
1248
1249 for pkg in cargo.packages() {
1251 let cfg_options = {
1252 let mut cfg_options = cfg_options.clone();
1253
1254 if cargo[pkg].is_local {
1255 if set_test && !cargo.is_sysroot() {
1256 cfg_options.insert_atom(sym::test);
1258 }
1259 cfg_options.insert_atom(sym::rust_analyzer);
1260 }
1261
1262 override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
1263 cfg_options
1264 };
1265
1266 let mut lib_tgt = None;
1267 for &tgt in cargo[pkg].targets.iter() {
1268 let pkg_data = &cargo[pkg];
1269 if !matches!(cargo[tgt].kind, TargetKind::Lib { .. })
1270 && (!pkg_data.is_member || cargo.is_sysroot())
1271 {
1272 continue;
1278 }
1279 let &TargetData { ref name, kind, ref root, .. } = &cargo[tgt];
1280
1281 let Some(file_id) = load(root) else { continue };
1282
1283 let build_data = build_scripts.get_output(pkg);
1284 let crate_id = add_target_crate_root(
1285 crate_graph,
1286 proc_macros,
1287 cargo,
1288 pkg_data,
1289 build_data.zip(Some(build_scripts.error().is_some())),
1290 cfg_options.clone(),
1291 file_id,
1292 name,
1293 kind,
1294 if pkg_data.is_local {
1295 if cargo.is_sysroot() {
1296 CrateOrigin::Lang(LangCrateOrigin::from(&*pkg_data.name))
1297 } else {
1298 CrateOrigin::Local {
1299 repo: pkg_data.repository.clone(),
1300 name: Some(Symbol::intern(&pkg_data.name)),
1301 }
1302 }
1303 } else if cargo.is_sysroot() {
1304 CrateOrigin::Lang(LangCrateOrigin::Dependency)
1305 } else {
1306 CrateOrigin::Library {
1307 repo: pkg_data.repository.clone(),
1308 name: Symbol::intern(&pkg_data.name),
1309 }
1310 },
1311 crate_ws_data.clone(),
1312 if pkg_data.is_member {
1313 workspace_proc_macro_cwd.clone()
1314 } else {
1315 Arc::new(pkg_data.manifest.parent().to_path_buf())
1316 },
1317 &cargo_path,
1318 );
1319 if let TargetKind::Lib { .. } = kind {
1320 lib_tgt = Some((crate_id, name.clone()));
1321 pkg_to_lib_crate.insert(pkg, crate_id);
1322 }
1323 if let Some(proc_macro) = libproc_macro {
1326 add_proc_macro_dep(
1327 crate_graph,
1328 crate_id,
1329 proc_macro,
1330 matches!(kind, TargetKind::Lib { is_proc_macro: true }),
1331 );
1332 }
1333
1334 pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
1335 }
1336
1337 for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
1339 public_deps.add_to_crate_graph(crate_graph, from);
1341
1342 if let Some((to, name)) = lib_tgt.clone()
1344 && to != from
1345 && kind != TargetKind::BuildScript
1346 {
1347 let name = CrateName::normalize_dashes(&name);
1353 add_dep(crate_graph, from, name, to);
1354 }
1355 }
1356 }
1357
1358 let mut delayed_dev_deps = vec![];
1359
1360 for pkg in cargo.packages() {
1363 for dep in &cargo[pkg].dependencies {
1364 let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue };
1365 let Some(targets) = pkg_crates.get(&pkg) else { continue };
1366
1367 let name = CrateName::new(&dep.name).unwrap();
1368 for &(from, kind) in targets {
1369 if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) {
1371 continue;
1372 }
1373
1374 if dep.kind == DepKind::Dev
1382 && matches!(kind, TargetKind::Lib { .. })
1383 && cargo[dep.pkg].is_member
1384 && cargo[pkg].is_member
1385 {
1386 delayed_dev_deps.push((from, name.clone(), to));
1387 continue;
1388 }
1389
1390 add_dep(crate_graph, from, name.clone(), to)
1391 }
1392 }
1393 }
1394
1395 for (from, name, to) in delayed_dev_deps {
1396 add_dep(crate_graph, from, name, to);
1397 }
1398
1399 if cargo.requires_rustc_private() {
1400 if let Some((rustc_workspace, rustc_build_scripts)) = rustc {
1403 handle_rustc_crates(
1404 crate_graph,
1405 proc_macros,
1406 &mut pkg_to_lib_crate,
1407 load,
1408 rustc_workspace,
1409 cargo,
1410 &public_deps,
1411 libproc_macro,
1412 &pkg_crates,
1413 &cfg_options,
1414 override_cfg,
1415 if rustc_workspace.workspace_root() == cargo.workspace_root() {
1417 build_scripts
1420 } else {
1421 rustc_build_scripts
1422 },
1423 crate_ws_data,
1425 &cargo_path,
1426 );
1427 }
1428 }
1429 res
1430}
1431
1432fn detached_file_to_crate_graph(
1433 rustc_cfg: Vec<CfgAtom>,
1434 load: FileLoader<'_>,
1435 detached_file: &ManifestPath,
1436 sysroot: &Sysroot,
1437 override_cfg: &CfgOverrides,
1438 set_test: bool,
1439 crate_ws_data: Arc<CrateWorkspaceData>,
1440) -> (CrateGraphBuilder, ProcMacroPaths) {
1441 let _p = tracing::info_span!("detached_file_to_crate_graph").entered();
1442 let mut crate_graph = CrateGraphBuilder::default();
1443 let (public_deps, _libproc_macro) = sysroot_to_crate_graph(
1444 &mut crate_graph,
1445 sysroot,
1446 rustc_cfg.clone(),
1447 load,
1448 crate_ws_data.clone(),
1450 );
1451
1452 let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
1453 if set_test {
1454 cfg_options.insert_atom(sym::test);
1455 }
1456 cfg_options.insert_atom(sym::rust_analyzer);
1457 override_cfg.apply(&mut cfg_options, "");
1458 let cfg_options = cfg_options;
1459
1460 let file_id = match load(detached_file) {
1461 Some(file_id) => file_id,
1462 None => {
1463 error!("Failed to load detached file {:?}", detached_file);
1464 return (crate_graph, FxHashMap::default());
1465 }
1466 };
1467 let display_name = detached_file.file_stem().map(CrateDisplayName::from_canonical_name);
1468 let detached_file_crate = crate_graph.add_crate_root(
1469 file_id,
1470 Edition::CURRENT,
1471 display_name.clone(),
1472 None,
1473 cfg_options,
1474 None,
1475 Env::default(),
1476 CrateOrigin::Local {
1477 repo: None,
1478 name: display_name.map(|n| n.canonical_name().to_owned()),
1479 },
1480 Vec::new(),
1481 false,
1482 Arc::new(detached_file.parent().to_path_buf()),
1483 crate_ws_data,
1484 );
1485
1486 public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
1487 (crate_graph, FxHashMap::default())
1488}
1489
1490fn handle_rustc_crates(
1492 crate_graph: &mut CrateGraphBuilder,
1493 proc_macros: &mut ProcMacroPaths,
1494 pkg_to_lib_crate: &mut FxHashMap<Package, CrateBuilderId>,
1495 load: FileLoader<'_>,
1496 rustc_workspace: &CargoWorkspace,
1497 cargo: &CargoWorkspace,
1498 public_deps: &SysrootPublicDeps,
1499 libproc_macro: Option<CrateBuilderId>,
1500 pkg_crates: &FxHashMap<Package, Vec<(CrateBuilderId, TargetKind)>>,
1501 cfg_options: &CfgOptions,
1502 override_cfg: &CfgOverrides,
1503 build_scripts: &WorkspaceBuildScripts,
1504 crate_ws_data: Arc<CrateWorkspaceData>,
1505 cargo_path: &Utf8Path,
1506) {
1507 let mut rustc_pkg_crates = FxHashMap::default();
1508 let root_pkg =
1510 rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver");
1511 let workspace_proc_macro_cwd = Arc::new(cargo.workspace_root().to_path_buf());
1512 if let Some(root_pkg) = root_pkg {
1515 let mut queue = VecDeque::new();
1517 queue.push_back(root_pkg);
1518 while let Some(pkg) = queue.pop_front() {
1519 if rustc_pkg_crates.contains_key(&pkg) {
1523 continue;
1524 }
1525 let pkg_data = &rustc_workspace[pkg];
1526 for dep in &pkg_data.dependencies {
1527 queue.push_back(dep.pkg);
1528 }
1529
1530 let mut cfg_options = cfg_options.clone();
1531 override_cfg.apply(&mut cfg_options, &pkg_data.name);
1532
1533 for &tgt in pkg_data.targets.iter() {
1534 let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else {
1535 continue;
1536 };
1537 let pkg_crates = &mut rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new);
1538 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
1539 let crate_id = add_target_crate_root(
1540 crate_graph,
1541 proc_macros,
1542 rustc_workspace,
1543 pkg_data,
1544 build_scripts.get_output(pkg).zip(Some(build_scripts.error().is_some())),
1545 cfg_options.clone(),
1546 file_id,
1547 &rustc_workspace[tgt].name,
1548 kind,
1549 CrateOrigin::Rustc { name: Symbol::intern(&pkg_data.name) },
1550 crate_ws_data.clone(),
1551 if pkg_data.is_member {
1552 workspace_proc_macro_cwd.clone()
1553 } else {
1554 Arc::new(pkg_data.manifest.parent().to_path_buf())
1555 },
1556 cargo_path,
1557 );
1558 pkg_to_lib_crate.insert(pkg, crate_id);
1559 public_deps.add_to_crate_graph(crate_graph, crate_id);
1561 if let Some(proc_macro) = libproc_macro {
1562 add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro);
1563 }
1564 pkg_crates.push(crate_id);
1565 }
1566 }
1567 }
1568 }
1569 for pkg in rustc_pkg_crates.keys().copied() {
1572 for dep in rustc_workspace[pkg].dependencies.iter() {
1573 let name = CrateName::new(&dep.name).unwrap();
1574 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
1575 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
1576 add_dep(crate_graph, from, name.clone(), to);
1577 }
1578 }
1579 }
1580 }
1581 for dep in rustc_workspace.packages() {
1584 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
1585
1586 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
1587 for pkg in cargo.packages() {
1588 let package = &cargo[pkg];
1589 if !package.metadata.rustc_private {
1590 continue;
1591 }
1592 for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
1593 if !crate_graph[*from].basic.dependencies.iter().any(|d| d.name == name) {
1598 add_dep(crate_graph, *from, name.clone(), to);
1599 }
1600 }
1601 }
1602 }
1603 }
1604}
1605
1606fn add_target_crate_root(
1607 crate_graph: &mut CrateGraphBuilder,
1608 proc_macros: &mut ProcMacroPaths,
1609 cargo: &CargoWorkspace,
1610 pkg: &PackageData,
1611 build_data: Option<(&BuildScriptOutput, bool)>,
1612 cfg_options: CfgOptions,
1613 file_id: FileId,
1614 cargo_crate_name: &str,
1615 kind: TargetKind,
1616 origin: CrateOrigin,
1617 crate_ws_data: Arc<CrateWorkspaceData>,
1618 proc_macro_cwd: Arc<AbsPathBuf>,
1619 cargo_path: &Utf8Path,
1620) -> CrateBuilderId {
1621 let edition = pkg.edition;
1622 let potential_cfg_options = if pkg.features.is_empty() {
1623 None
1624 } else {
1625 let mut potential_cfg_options = cfg_options.clone();
1626 potential_cfg_options.extend(
1627 pkg.features
1628 .iter()
1629 .map(|feat| CfgAtom::KeyValue { key: sym::feature, value: Symbol::intern(feat.0) }),
1630 );
1631 Some(potential_cfg_options)
1632 };
1633 let cfg_options = {
1634 let mut opts = cfg_options;
1635 for feature in pkg.active_features.iter() {
1636 opts.insert_key_value(sym::feature, Symbol::intern(feature));
1637 }
1638 if let Some(cfgs) = build_data.map(|(it, _)| &it.cfgs) {
1639 opts.extend(cfgs.iter().cloned());
1640 }
1641 opts
1642 };
1643
1644 let mut env = cargo.env().clone();
1645 inject_cargo_package_env(&mut env, pkg);
1646 inject_cargo_env(&mut env, cargo_path);
1647 inject_rustc_tool_env(&mut env, cargo_crate_name, kind);
1648
1649 if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
1650 env.extend_from_other(envs);
1651 }
1652 let crate_id = crate_graph.add_crate_root(
1653 file_id,
1654 edition,
1655 Some(CrateDisplayName::from_canonical_name(cargo_crate_name)),
1656 Some(pkg.version.to_string()),
1657 cfg_options,
1658 potential_cfg_options,
1659 env,
1660 origin,
1661 Vec::new(),
1662 matches!(kind, TargetKind::Lib { is_proc_macro: true }),
1663 proc_macro_cwd,
1664 crate_ws_data,
1665 );
1666 if let TargetKind::Lib { is_proc_macro: true } = kind {
1667 let proc_macro = match build_data {
1668 Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
1669 match proc_macro_dylib_path {
1670 ProcMacroDylibPath::Path(path) => {
1671 Ok((cargo_crate_name.to_owned(), path.clone()))
1672 }
1673 ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt),
1674 ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound
1675 if has_errors =>
1676 {
1677 Err(ProcMacroLoadingError::FailedToBuild)
1678 }
1679 ProcMacroDylibPath::NotProcMacro => {
1680 Err(ProcMacroLoadingError::ExpectedProcMacroArtifact)
1681 }
1682 ProcMacroDylibPath::DylibNotFound => {
1683 Err(ProcMacroLoadingError::MissingDylibPath)
1684 }
1685 }
1686 }
1687 None => Err(ProcMacroLoadingError::NotYetBuilt),
1688 };
1689 proc_macros.insert(crate_id, proc_macro);
1690 }
1691
1692 crate_id
1693}
1694
1695#[derive(Default, Debug)]
1696struct SysrootPublicDeps {
1697 deps: Vec<(CrateName, CrateBuilderId, bool)>,
1698}
1699
1700impl SysrootPublicDeps {
1701 fn add_to_crate_graph(&self, crate_graph: &mut CrateGraphBuilder, from: CrateBuilderId) {
1703 for (name, krate, prelude) in &self.deps {
1704 add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude, true);
1705 }
1706 }
1707}
1708
1709fn extend_crate_graph_with_sysroot(
1710 crate_graph: &mut CrateGraphBuilder,
1711 mut sysroot_crate_graph: CrateGraphBuilder,
1712 mut sysroot_proc_macros: ProcMacroPaths,
1713) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
1714 let mut pub_deps = vec![];
1715 let mut libproc_macro = None;
1716 for cid in sysroot_crate_graph.iter() {
1717 if let CrateOrigin::Lang(lang_crate) = sysroot_crate_graph[cid].basic.origin {
1718 match lang_crate {
1719 LangCrateOrigin::Test
1720 | LangCrateOrigin::Alloc
1721 | LangCrateOrigin::Core
1722 | LangCrateOrigin::Std => pub_deps.push((
1723 CrateName::normalize_dashes(&lang_crate.to_string()),
1724 cid,
1725 !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
1726 )),
1727 LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
1728 LangCrateOrigin::Other | LangCrateOrigin::Dependency => (),
1729 }
1730 }
1731 }
1732
1733 let mut marker_set = vec![];
1734 for &(_, cid, _) in pub_deps.iter() {
1735 marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1736 }
1737 if let Some(cid) = libproc_macro {
1738 marker_set.extend(sysroot_crate_graph.transitive_deps(cid));
1739 }
1740
1741 marker_set.sort();
1742 marker_set.dedup();
1743
1744 let removed_mapping = sysroot_crate_graph.remove_crates_except(&marker_set);
1746 sysroot_proc_macros = sysroot_proc_macros
1747 .into_iter()
1748 .filter_map(|(k, v)| Some((removed_mapping[k.into_raw().into_u32() as usize]?, v)))
1749 .collect();
1750 let mapping = crate_graph.extend(sysroot_crate_graph, &mut sysroot_proc_macros);
1751
1752 pub_deps.iter_mut().for_each(|(_, cid, _)| {
1754 *cid = mapping[&removed_mapping[cid.into_raw().into_u32() as usize].unwrap()]
1755 });
1756 if let Some(libproc_macro) = &mut libproc_macro {
1757 *libproc_macro =
1758 mapping[&removed_mapping[libproc_macro.into_raw().into_u32() as usize].unwrap()];
1759 }
1760
1761 (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
1762}
1763
1764fn sysroot_to_crate_graph(
1765 crate_graph: &mut CrateGraphBuilder,
1766 sysroot: &Sysroot,
1767 rustc_cfg: Vec<CfgAtom>,
1768 load: FileLoader<'_>,
1769 crate_ws_data: Arc<CrateWorkspaceData>,
1770) -> (SysrootPublicDeps, Option<CrateBuilderId>) {
1771 let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
1772 match sysroot.workspace() {
1773 RustLibSrcWorkspace::Workspace { ws: cargo, .. } => {
1774 let (sysroot_cg, sysroot_pm) = cargo_to_crate_graph(
1775 load,
1776 None,
1777 cargo,
1778 &Sysroot::empty(),
1779 rustc_cfg,
1780 &CfgOverrides {
1781 global: CfgDiff::new(
1782 vec![
1783 CfgAtom::Flag(sym::debug_assertions),
1784 CfgAtom::Flag(sym::miri),
1785 CfgAtom::Flag(sym::bootstrap),
1786 ],
1787 vec![CfgAtom::Flag(sym::test)],
1788 ),
1789 ..Default::default()
1790 },
1791 &WorkspaceBuildScripts::default(),
1792 false,
1793 crate_ws_data,
1794 );
1795
1796 extend_crate_graph_with_sysroot(crate_graph, sysroot_cg, sysroot_pm)
1797 }
1798 RustLibSrcWorkspace::Json(project_json) => {
1799 let (sysroot_cg, sysroot_pm) = project_json_to_crate_graph(
1800 rustc_cfg,
1801 load,
1802 project_json,
1803 &Sysroot::empty(),
1804 &FxHashMap::default(),
1805 &CfgOverrides {
1806 global: CfgDiff::new(
1807 vec![CfgAtom::Flag(sym::debug_assertions), CfgAtom::Flag(sym::miri)],
1808 vec![],
1809 ),
1810 ..Default::default()
1811 },
1812 false,
1813 true,
1814 crate_ws_data,
1815 );
1816
1817 extend_crate_graph_with_sysroot(crate_graph, sysroot_cg, sysroot_pm)
1818 }
1819 RustLibSrcWorkspace::Stitched(stitched) => {
1820 let cfg_options = {
1821 let mut cfg_options = CfgOptions::default();
1822 cfg_options.extend(rustc_cfg);
1823 cfg_options.insert_atom(sym::debug_assertions);
1824 cfg_options.insert_atom(sym::miri);
1825 cfg_options
1826 };
1827 let sysroot_crates: FxHashMap<
1828 crate::sysroot::stitched::RustLibSrcCrate,
1829 CrateBuilderId,
1830 > = stitched
1831 .crates()
1832 .filter_map(|krate| {
1833 let file_id = load(&stitched[krate].root)?;
1834
1835 let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name);
1836 let crate_id = crate_graph.add_crate_root(
1837 file_id,
1838 stitched.edition,
1839 Some(display_name),
1840 None,
1841 cfg_options.clone(),
1842 None,
1843 Env::default(),
1844 CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)),
1845 Vec::new(),
1846 false,
1847 Arc::new(stitched[krate].root.parent().to_path_buf()),
1848 crate_ws_data.clone(),
1849 );
1850 Some((krate, crate_id))
1851 })
1852 .collect();
1853
1854 for from in stitched.crates() {
1855 for &to in stitched[from].deps.iter() {
1856 let name = CrateName::new(&stitched[to].name).unwrap();
1857 if let (Some(&from), Some(&to)) =
1858 (sysroot_crates.get(&from), sysroot_crates.get(&to))
1859 {
1860 add_dep(crate_graph, from, name, to);
1861 }
1862 }
1863 }
1864
1865 let public_deps = SysrootPublicDeps {
1866 deps: stitched
1867 .public_deps()
1868 .filter_map(|(name, idx, prelude)| {
1869 Some((name, *sysroot_crates.get(&idx)?, prelude))
1870 })
1871 .collect::<Vec<_>>(),
1872 };
1873
1874 let libproc_macro =
1875 stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
1876 (public_deps, libproc_macro)
1877 }
1878 RustLibSrcWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
1879 }
1880}
1881
1882fn add_dep(
1883 graph: &mut CrateGraphBuilder,
1884 from: CrateBuilderId,
1885 name: CrateName,
1886 to: CrateBuilderId,
1887) {
1888 add_dep_inner(graph, from, DependencyBuilder::new(name, to))
1889}
1890
1891fn add_dep_with_prelude(
1892 graph: &mut CrateGraphBuilder,
1893 from: CrateBuilderId,
1894 name: CrateName,
1895 to: CrateBuilderId,
1896 prelude: bool,
1897 sysroot: bool,
1898) {
1899 add_dep_inner(graph, from, DependencyBuilder::with_prelude(name, to, prelude, sysroot))
1900}
1901
1902fn add_proc_macro_dep(
1903 crate_graph: &mut CrateGraphBuilder,
1904 from: CrateBuilderId,
1905 to: CrateBuilderId,
1906 prelude: bool,
1907) {
1908 add_dep_with_prelude(
1909 crate_graph,
1910 from,
1911 CrateName::new("proc_macro").unwrap(),
1912 to,
1913 prelude,
1914 true,
1915 );
1916}
1917
1918fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: DependencyBuilder) {
1919 if let Err(err) = graph.add_dep(from, dep) {
1920 tracing::warn!("{}", err)
1921 }
1922}
1923
1924fn sysroot_metadata_config(
1925 config: &CargoConfig,
1926 workspace_root: &AbsPath,
1927 targets: &[String],
1928 toolchain_version: Option<Version>,
1929) -> CargoMetadataConfig {
1930 let targets = targets
1934 .iter()
1935 .map(|target| {
1936 if target.ends_with(".json") {
1937 workspace_root.join(target).to_string()
1939 } else {
1940 target.to_owned()
1941 }
1942 })
1943 .collect();
1944
1945 CargoMetadataConfig {
1946 features: Default::default(),
1947 targets,
1948 extra_args: Default::default(),
1949 metadata_extra_args: config.metadata_extra_args.clone(),
1950 extra_env: config.extra_env.clone(),
1951 toolchain_version,
1952 kind: "sysroot",
1953 }
1954}