Skip to content

Commit f9f03c9

Browse files
committed
Detect sysroot dependencies
1 parent 9b54e39 commit f9f03c9

File tree

3 files changed

+172
-52
lines changed

3 files changed

+172
-52
lines changed

crates/base-db/src/input.rs

+47-7
Original file line numberDiff line numberDiff line change
@@ -386,24 +386,64 @@ impl CrateGraph {
386386
self.arena.alloc(data)
387387
}
388388

389+
/// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
390+
/// with the second input.
391+
pub fn remove_and_replace(
392+
&mut self,
393+
id: CrateId,
394+
replace_with: CrateId,
395+
) -> Result<(), CyclicDependenciesError> {
396+
for (x, data) in self.arena.iter() {
397+
if x == id {
398+
continue;
399+
}
400+
for edge in &data.dependencies {
401+
if edge.crate_id == id {
402+
self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
403+
}
404+
}
405+
}
406+
// if everything was ok, start to replace
407+
for (x, data) in self.arena.iter_mut() {
408+
if x == id {
409+
continue;
410+
}
411+
for edge in &mut data.dependencies {
412+
if edge.crate_id == id {
413+
edge.crate_id = replace_with;
414+
}
415+
}
416+
}
417+
Ok(())
418+
}
419+
389420
pub fn add_dep(
390421
&mut self,
391422
from: CrateId,
392423
dep: Dependency,
393424
) -> Result<(), CyclicDependenciesError> {
394425
let _p = profile::span("add_dep");
395426

396-
// Check if adding a dep from `from` to `to` creates a cycle. To figure
397-
// that out, look for a path in the *opposite* direction, from `to` to
398-
// `from`.
399-
if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
427+
self.check_cycle_after_dependency(from, dep.crate_id)?;
428+
429+
self.arena[from].add_dep(dep);
430+
Ok(())
431+
}
432+
433+
/// Check if adding a dep from `from` to `to` creates a cycle. To figure
434+
/// that out, look for a path in the *opposite* direction, from `to` to
435+
/// `from`.
436+
fn check_cycle_after_dependency(
437+
&self,
438+
from: CrateId,
439+
to: CrateId,
440+
) -> Result<(), CyclicDependenciesError> {
441+
if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
400442
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
401443
let err = CyclicDependenciesError { path };
402-
assert!(err.from().0 == from && err.to().0 == dep.crate_id);
444+
assert!(err.from().0 == from && err.to().0 == to);
403445
return Err(err);
404446
}
405-
406-
self.arena[from].add_dep(dep);
407447
Ok(())
408448
}
409449

crates/project-model/src/sysroot.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ use la_arena::{Arena, Idx};
1212
use paths::{AbsPath, AbsPathBuf};
1313
use rustc_hash::FxHashMap;
1414

15-
use crate::{utf8_stdout, ManifestPath};
15+
use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
1616

1717
#[derive(Debug, Clone, Eq, PartialEq)]
1818
pub struct Sysroot {
1919
root: AbsPathBuf,
2020
src_root: AbsPathBuf,
2121
crates: Arena<SysrootCrateData>,
22+
pub cargo_workspace: Option<CargoWorkspace>,
2223
}
2324

2425
pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
@@ -125,9 +126,31 @@ impl Sysroot {
125126
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
126127
}
127128

128-
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot {
129-
let mut sysroot =
130-
Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };
129+
pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot {
130+
// FIXME: Remove this `cargo_workspace` field completely once we support sysroot dependencies
131+
let cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") {
132+
let cargo_toml = ManifestPath::try_from(
133+
AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(),
134+
)
135+
.unwrap();
136+
sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library");
137+
CargoWorkspace::fetch_metadata(
138+
&cargo_toml,
139+
&AbsPathBuf::try_from("/home/hamid/oss/rust-analyzer").unwrap(),
140+
&CargoConfig::default(),
141+
&|_| (),
142+
)
143+
.map(CargoWorkspace::new)
144+
.ok()
145+
} else {
146+
None
147+
};
148+
let mut sysroot = Sysroot {
149+
root: sysroot_dir,
150+
src_root: sysroot_src_dir,
151+
crates: Arena::default(),
152+
cargo_workspace,
153+
};
131154

132155
for path in SYSROOT_CRATES.trim().lines() {
133156
let name = path.split('/').last().unwrap();

crates/project-model/src/workspace.rs

+98-41
Original file line numberDiff line numberDiff line change
@@ -615,20 +615,27 @@ impl ProjectWorkspace {
615615
build_scripts,
616616
toolchain,
617617
target_layout,
618-
} => cargo_to_crate_graph(
619-
load,
620-
rustc.as_ref().ok(),
621-
cargo,
622-
sysroot.as_ref().ok(),
623-
rustc_cfg.clone(),
624-
cfg_overrides,
625-
build_scripts,
626-
match target_layout.as_ref() {
627-
Ok(it) => Ok(Arc::from(it.as_str())),
628-
Err(it) => Err(Arc::from(it.as_str())),
629-
},
630-
toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
631-
),
618+
} => {
619+
let mut res = (CrateGraph::default(), ProcMacroPaths::default());
620+
cargo_to_crate_graph(
621+
&mut res.0,
622+
&mut res.1,
623+
load,
624+
rustc.as_ref().ok(),
625+
cargo,
626+
sysroot.as_ref().ok(),
627+
rustc_cfg.clone(),
628+
cfg_overrides,
629+
None,
630+
build_scripts,
631+
match target_layout.as_ref() {
632+
Ok(it) => Ok(Arc::from(it.as_str())),
633+
Err(it) => Err(Arc::from(it.as_str())),
634+
},
635+
toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
636+
);
637+
res
638+
}
632639
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
633640
detached_files_to_crate_graph(
634641
rustc_cfg.clone(),
@@ -815,20 +822,21 @@ fn project_json_to_crate_graph(
815822
}
816823

817824
fn cargo_to_crate_graph(
825+
crate_graph: &mut CrateGraph,
826+
proc_macros: &mut ProcMacroPaths,
818827
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
819828
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
820829
cargo: &CargoWorkspace,
821830
sysroot: Option<&Sysroot>,
822831
rustc_cfg: Vec<CfgFlag>,
823832
override_cfg: &CfgOverrides,
833+
// Don't compute cfg and use this if present
834+
forced_cfg: Option<CfgOptions>,
824835
build_scripts: &WorkspaceBuildScripts,
825836
target_layout: TargetLayoutLoadResult,
826837
channel: Option<ReleaseChannel>,
827-
) -> (CrateGraph, ProcMacroPaths) {
838+
) {
828839
let _p = profile::span("cargo_to_crate_graph");
829-
let mut res = (CrateGraph::default(), ProcMacroPaths::default());
830-
let crate_graph = &mut res.0;
831-
let proc_macros = &mut res.1;
832840
let (public_deps, libproc_macro) = match sysroot {
833841
Some(sysroot) => sysroot_to_crate_graph(
834842
crate_graph,
@@ -858,7 +866,7 @@ fn cargo_to_crate_graph(
858866
for pkg in cargo.packages() {
859867
has_private |= cargo[pkg].metadata.rustc_private;
860868

861-
let cfg_options = {
869+
let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
862870
let mut cfg_options = cfg_options.clone();
863871

864872
// Add test cfg for local crates
@@ -882,7 +890,7 @@ fn cargo_to_crate_graph(
882890
cfg_options.apply_diff(overrides.clone());
883891
};
884892
cfg_options
885-
};
893+
});
886894

887895
let mut lib_tgt = None;
888896
for &tgt in cargo[pkg].targets.iter() {
@@ -989,7 +997,6 @@ fn cargo_to_crate_graph(
989997
);
990998
}
991999
}
992-
res
9931000
}
9941001

9951002
fn detached_files_to_crate_graph(
@@ -1280,31 +1287,81 @@ fn sysroot_to_crate_graph(
12801287
) -> (SysrootPublicDeps, Option<CrateId>) {
12811288
let _p = profile::span("sysroot_to_crate_graph");
12821289
let mut cfg_options = CfgOptions::default();
1283-
cfg_options.extend(rustc_cfg);
1284-
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
1285-
.crates()
1286-
.filter_map(|krate| {
1287-
let file_id = load(&sysroot[krate].root)?;
1288-
1289-
let env = Env::default();
1290-
let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
1291-
let crate_id = crate_graph.add_crate_root(
1292-
file_id,
1293-
Edition::CURRENT,
1294-
Some(display_name),
1290+
cfg_options.extend(rustc_cfg.clone());
1291+
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> =
1292+
if let Some(cargo) = &sysroot.cargo_workspace {
1293+
cargo_to_crate_graph(
1294+
crate_graph,
1295+
&mut Default::default(),
1296+
load,
12951297
None,
1296-
cfg_options.clone(),
1298+
cargo,
12971299
None,
1298-
env,
1299-
false,
1300-
CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
1301-
target_layout.clone(),
1300+
rustc_cfg,
1301+
&CfgOverrides::default(),
1302+
Some(cfg_options),
1303+
&WorkspaceBuildScripts::default(),
1304+
target_layout,
13021305
channel,
13031306
);
1304-
Some((krate, crate_id))
1305-
})
1306-
.collect();
1307+
for crate_name in ["std", "alloc", "core"] {
1308+
let original = crate_graph
1309+
.iter()
1310+
.find(|x| {
1311+
crate_graph[*x]
1312+
.display_name
1313+
.as_ref()
1314+
.map(|x| x.canonical_name() == crate_name)
1315+
.unwrap_or(false)
1316+
})
1317+
.unwrap();
1318+
let fake_crate_name = format!("rustc-std-workspace-{}", crate_name);
1319+
let fake = crate_graph
1320+
.iter()
1321+
.find(|x| {
1322+
crate_graph[*x]
1323+
.display_name
1324+
.as_ref()
1325+
.map(|x| x.canonical_name() == fake_crate_name)
1326+
.unwrap_or(false)
1327+
})
1328+
.unwrap();
1329+
crate_graph.remove_and_replace(fake, original).unwrap();
1330+
}
1331+
sysroot
1332+
.crates()
1333+
.filter_map(|krate| {
1334+
let file_id = load(&sysroot[krate].root)?;
1335+
let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
1336+
Some((krate, crate_id))
1337+
})
1338+
.collect()
1339+
} else {
1340+
sysroot
1341+
.crates()
1342+
.filter_map(|krate| {
1343+
let file_id = load(&sysroot[krate].root)?;
13071344

1345+
let env = Env::default();
1346+
let display_name =
1347+
CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
1348+
let crate_id = crate_graph.add_crate_root(
1349+
file_id,
1350+
Edition::CURRENT,
1351+
Some(display_name),
1352+
None,
1353+
cfg_options.clone(),
1354+
None,
1355+
env,
1356+
false,
1357+
CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
1358+
target_layout.clone(),
1359+
channel,
1360+
);
1361+
Some((krate, crate_id))
1362+
})
1363+
.collect()
1364+
};
13081365
for from in sysroot.crates() {
13091366
for &to in sysroot[from].deps.iter() {
13101367
let name = CrateName::new(&sysroot[to].name).unwrap();

0 commit comments

Comments
 (0)