2
2
//! metadata` or `rust-project.json`) into representation stored in the salsa
3
3
//! database -- `CrateGraph`.
4
4
5
- use std:: { collections:: VecDeque , fmt, fs, process:: Command , sync:: Arc } ;
5
+ use std:: {
6
+ collections:: { hash_map:: Entry , VecDeque } ,
7
+ fmt, fs,
8
+ process:: Command ,
9
+ sync:: Arc ,
10
+ } ;
6
11
7
12
use anyhow:: { format_err, Context , Result } ;
8
13
use base_db:: {
@@ -858,32 +863,6 @@ fn cargo_to_crate_graph(
858
863
for pkg in cargo. packages ( ) {
859
864
has_private |= cargo[ pkg] . metadata . rustc_private ;
860
865
861
- let cfg_options = {
862
- let mut cfg_options = cfg_options. clone ( ) ;
863
-
864
- // Add test cfg for local crates
865
- if cargo[ pkg] . is_local {
866
- cfg_options. insert_atom ( "test" . into ( ) ) ;
867
- }
868
-
869
- let overrides = match override_cfg {
870
- CfgOverrides :: Wildcard ( cfg_diff) => Some ( cfg_diff) ,
871
- CfgOverrides :: Selective ( cfg_overrides) => cfg_overrides. get ( & cargo[ pkg] . name ) ,
872
- } ;
873
-
874
- if let Some ( overrides) = overrides {
875
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
876
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
877
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
878
- //
879
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
880
- // figure out the set of cfgs that would have to apply to make it active.
881
-
882
- cfg_options. apply_diff ( overrides. clone ( ) ) ;
883
- } ;
884
- cfg_options
885
- } ;
886
-
887
866
let mut lib_tgt = None ;
888
867
for & tgt in cargo[ pkg] . targets . iter ( ) {
889
868
if cargo[ tgt] . kind != TargetKind :: Lib && !cargo[ pkg] . is_member {
@@ -894,7 +873,7 @@ fn cargo_to_crate_graph(
894
873
// https://github.com/rust-lang/rust-analyzer/issues/11300
895
874
continue ;
896
875
}
897
- let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
876
+ let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
898
877
899
878
let crate_id = add_target_crate_root (
900
879
crate_graph,
@@ -922,15 +901,19 @@ fn cargo_to_crate_graph(
922
901
pkg_crates. entry ( pkg) . or_insert_with ( Vec :: new) . push ( ( crate_id, cargo[ tgt] . kind ) ) ;
923
902
}
924
903
904
+ let Some ( targets) = pkg_crates. get ( & pkg) else { continue } ;
925
905
// Set deps to the core, std and to the lib target of the current package
926
- for & ( from, kind) in pkg_crates . get ( & pkg ) . into_iter ( ) . flatten ( ) {
906
+ for & ( from, kind) in targets {
927
907
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
928
908
public_deps. add_to_crate_graph ( crate_graph, from) ;
929
909
930
910
// Add dep edge of all targets to the package's lib target
931
911
if let Some ( ( to, name) ) = lib_tgt. clone ( ) {
932
- if to != from && kind != TargetKind :: BuildScript {
933
- // (build script can not depend on its library target)
912
+ if to != from {
913
+ if kind == TargetKind :: BuildScript {
914
+ // build script can not depend on its library target
915
+ continue ;
916
+ }
934
917
935
918
// For root projects with dashes in their name,
936
919
// cargo metadata does not do any normalization,
@@ -942,6 +925,43 @@ fn cargo_to_crate_graph(
942
925
}
943
926
}
944
927
928
+ // We now need to duplicate workspace members that are used as dev-dependencies to prevent
929
+ // cycles from forming.
930
+
931
+ // Map from crate id to it's dev-dependency clone id
932
+ let mut test_dupes = FxHashMap :: default ( ) ;
933
+ let mut work = vec ! [ ] ;
934
+
935
+ // Get all dependencies of the workspace members that are used as dev-dependencies
936
+ for pkg in cargo. packages ( ) {
937
+ for dep in & cargo[ pkg] . dependencies {
938
+ if dep. kind == DepKind :: Dev {
939
+ work. push ( dep. pkg ) ;
940
+ }
941
+ }
942
+ }
943
+ while let Some ( pkg) = work. pop ( ) {
944
+ let Some ( & to) = pkg_to_lib_crate. get ( & pkg) else { continue } ;
945
+ match test_dupes. entry ( to) {
946
+ Entry :: Occupied ( _) => continue ,
947
+ Entry :: Vacant ( v) => {
948
+ for dep in & cargo[ pkg] . dependencies {
949
+ if dep. kind == DepKind :: Normal && cargo[ dep. pkg ] . is_member {
950
+ work. push ( dep. pkg ) ;
951
+ }
952
+ }
953
+ v. insert ( {
954
+ let duped = crate_graph. duplicate ( to) ;
955
+ if let Some ( proc_macro) = proc_macros. get ( & to) . cloned ( ) {
956
+ proc_macros. insert ( duped, proc_macro) ;
957
+ }
958
+ crate_graph[ duped] . cfg_options . insert_atom ( "test" . into ( ) ) ;
959
+ duped
960
+ } ) ;
961
+ }
962
+ }
963
+ }
964
+
945
965
// Now add a dep edge from all targets of upstream to the lib
946
966
// target of downstream.
947
967
for pkg in cargo. packages ( ) {
@@ -955,12 +975,66 @@ fn cargo_to_crate_graph(
955
975
if ( dep. kind == DepKind :: Build ) != ( kind == TargetKind :: BuildScript ) {
956
976
continue ;
957
977
}
978
+ add_dep (
979
+ crate_graph,
980
+ from,
981
+ name. clone ( ) ,
982
+ if dep. kind == DepKind :: Dev {
983
+ // point to the test enabled duplicate for dev-dependencies
984
+ test_dupes. get ( & to) . copied ( ) . unwrap_or ( to)
985
+ } else {
986
+ to
987
+ } ,
988
+ ) ;
958
989
959
- add_dep ( crate_graph, from, name. clone ( ) , to)
990
+ if dep. kind == DepKind :: Normal && cargo[ dep. pkg ] . is_member {
991
+ // Also apply the dependency as a test enabled dependency to the test duplicate
992
+ if let Some ( & dupe) = test_dupes. get ( & from) {
993
+ let to = test_dupes. get ( & to) . copied ( ) . unwrap_or_else ( || {
994
+ panic ! (
995
+ "dependency of a dev dependency did not get duplicated! {:?} {:?}" ,
996
+ crate_graph[ to] . display_name, crate_graph[ from] . display_name,
997
+ )
998
+ } ) ;
999
+ add_dep ( crate_graph, dupe, name. clone ( ) , to) ;
1000
+ }
1001
+ }
960
1002
}
961
1003
}
962
1004
}
963
1005
1006
+ for ( & pkg, targets) in & pkg_crates {
1007
+ for & ( krate, _) in targets {
1008
+ if test_dupes. get ( & krate) . is_some ( ) {
1009
+ // if the crate got duped as a dev-dep the dupe already has test set
1010
+ continue ;
1011
+ }
1012
+ let cfg_options = & mut crate_graph[ krate] . cfg_options ;
1013
+
1014
+ // Add test cfg for local crates
1015
+ if cargo[ pkg] . is_local {
1016
+ cfg_options. insert_atom ( "test" . into ( ) ) ;
1017
+ }
1018
+
1019
+ let overrides = match override_cfg {
1020
+ CfgOverrides :: Wildcard ( cfg_diff) => Some ( cfg_diff) ,
1021
+ CfgOverrides :: Selective ( cfg_overrides) => cfg_overrides. get ( & cargo[ pkg] . name ) ,
1022
+ } ;
1023
+
1024
+ if let Some ( overrides) = overrides {
1025
+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
1026
+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
1027
+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
1028
+ //
1029
+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
1030
+ // figure out the set of cfgs that would have to apply to make it active.
1031
+
1032
+ cfg_options. apply_diff ( overrides. clone ( ) ) ;
1033
+ } ;
1034
+ }
1035
+ }
1036
+
1037
+ // FIXME: Handle rustc private crates properly when used as dev-dependencies
964
1038
if has_private {
965
1039
// If the user provided a path to rustc sources, we add all the rustc_private crates
966
1040
// and create dependencies on them for the crates which opt-in to that
@@ -1084,7 +1158,9 @@ fn handle_rustc_crates(
1084
1158
continue ;
1085
1159
}
1086
1160
for dep in & rustc_workspace[ pkg] . dependencies {
1087
- queue. push_back ( dep. pkg ) ;
1161
+ if dep. kind == DepKind :: Normal {
1162
+ queue. push_back ( dep. pkg ) ;
1163
+ }
1088
1164
}
1089
1165
1090
1166
let mut cfg_options = cfg_options. clone ( ) ;
@@ -1325,10 +1401,12 @@ fn sysroot_to_crate_graph(
1325
1401
( public_deps, libproc_macro)
1326
1402
}
1327
1403
1404
+ #[ track_caller]
1328
1405
fn add_dep ( graph : & mut CrateGraph , from : CrateId , name : CrateName , to : CrateId ) {
1329
1406
add_dep_inner ( graph, from, Dependency :: new ( name, to) )
1330
1407
}
1331
1408
1409
+ #[ track_caller]
1332
1410
fn add_dep_with_prelude (
1333
1411
graph : & mut CrateGraph ,
1334
1412
from : CrateId ,
@@ -1339,13 +1417,18 @@ fn add_dep_with_prelude(
1339
1417
add_dep_inner ( graph, from, Dependency :: with_prelude ( name, to, prelude) )
1340
1418
}
1341
1419
1420
+ #[ track_caller]
1342
1421
fn add_proc_macro_dep ( crate_graph : & mut CrateGraph , from : CrateId , to : CrateId , prelude : bool ) {
1343
1422
add_dep_with_prelude ( crate_graph, from, CrateName :: new ( "proc_macro" ) . unwrap ( ) , to, prelude) ;
1344
1423
}
1345
1424
1425
+ #[ track_caller]
1346
1426
fn add_dep_inner ( graph : & mut CrateGraph , from : CrateId , dep : Dependency ) {
1347
1427
if let Err ( err) = graph. add_dep ( from, dep) {
1348
- tracing:: error!( "{}" , err)
1428
+ if cfg ! ( test) {
1429
+ panic ! ( "{}" , err) ;
1430
+ }
1431
+ tracing:: error!( "{}" , err) ;
1349
1432
}
1350
1433
}
1351
1434
0 commit comments