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:: {
@@ -844,12 +849,12 @@ fn cargo_to_crate_graph(
844
849
None => ( SysrootPublicDeps :: default ( ) , None ) ,
845
850
} ;
846
851
847
- let cfg_options = {
852
+ let cfg_options = forced_cfg . clone ( ) . unwrap_or_else ( || {
848
853
let mut cfg_options = CfgOptions :: default ( ) ;
849
854
cfg_options. extend ( rustc_cfg) ;
850
855
cfg_options. insert_atom ( "debug_assertions" . into ( ) ) ;
851
856
cfg_options
852
- } ;
857
+ } ) ;
853
858
854
859
// Mapping of a package to its library target
855
860
let mut pkg_to_lib_crate = FxHashMap :: default ( ) ;
@@ -861,32 +866,6 @@ fn cargo_to_crate_graph(
861
866
for pkg in cargo. packages ( ) {
862
867
has_private |= cargo[ pkg] . metadata . rustc_private ;
863
868
864
- let cfg_options = forced_cfg. clone ( ) . unwrap_or_else ( || {
865
- let mut cfg_options = cfg_options. clone ( ) ;
866
-
867
- // Add test cfg for local crates
868
- if cargo[ pkg] . is_local {
869
- cfg_options. insert_atom ( "test" . into ( ) ) ;
870
- }
871
-
872
- let overrides = match override_cfg {
873
- CfgOverrides :: Wildcard ( cfg_diff) => Some ( cfg_diff) ,
874
- CfgOverrides :: Selective ( cfg_overrides) => cfg_overrides. get ( & cargo[ pkg] . name ) ,
875
- } ;
876
-
877
- if let Some ( overrides) = overrides {
878
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
879
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
880
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
881
- //
882
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
883
- // figure out the set of cfgs that would have to apply to make it active.
884
-
885
- cfg_options. apply_diff ( overrides. clone ( ) ) ;
886
- } ;
887
- cfg_options
888
- } ) ;
889
-
890
869
let mut lib_tgt = None ;
891
870
for & tgt in cargo[ pkg] . targets . iter ( ) {
892
871
if cargo[ tgt] . kind != TargetKind :: Lib && !cargo[ pkg] . is_member {
@@ -897,7 +876,7 @@ fn cargo_to_crate_graph(
897
876
// https://github.com/rust-lang/rust-analyzer/issues/11300
898
877
continue ;
899
878
}
900
- let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
879
+ let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
901
880
902
881
let crate_id = add_target_crate_root (
903
882
crate_graph,
@@ -925,15 +904,19 @@ fn cargo_to_crate_graph(
925
904
pkg_crates. entry ( pkg) . or_insert_with ( Vec :: new) . push ( ( crate_id, cargo[ tgt] . kind ) ) ;
926
905
}
927
906
907
+ let Some ( targets) = pkg_crates. get ( & pkg) else { continue } ;
928
908
// Set deps to the core, std and to the lib target of the current package
929
- for & ( from, kind) in pkg_crates . get ( & pkg ) . into_iter ( ) . flatten ( ) {
909
+ for & ( from, kind) in targets {
930
910
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
931
911
public_deps. add_to_crate_graph ( crate_graph, from) ;
932
912
933
913
// Add dep edge of all targets to the package's lib target
934
914
if let Some ( ( to, name) ) = lib_tgt. clone ( ) {
935
- if to != from && kind != TargetKind :: BuildScript {
936
- // (build script can not depend on its library target)
915
+ if to != from {
916
+ if kind == TargetKind :: BuildScript {
917
+ // build script can not depend on its library target
918
+ continue ;
919
+ }
937
920
938
921
// For root projects with dashes in their name,
939
922
// cargo metadata does not do any normalization,
@@ -945,6 +928,43 @@ fn cargo_to_crate_graph(
945
928
}
946
929
}
947
930
931
+ // We now need to duplicate workspace members that are used as dev-dependencies to prevent
932
+ // cycles from forming.
933
+
934
+ // Map from crate id to it's dev-dependency clone id
935
+ let mut test_dupes = FxHashMap :: default ( ) ;
936
+ let mut work = vec ! [ ] ;
937
+
938
+ // Get all dependencies of the workspace members that are used as dev-dependencies
939
+ for pkg in cargo. packages ( ) {
940
+ for dep in & cargo[ pkg] . dependencies {
941
+ if dep. kind == DepKind :: Dev {
942
+ work. push ( dep. pkg ) ;
943
+ }
944
+ }
945
+ }
946
+ while let Some ( pkg) = work. pop ( ) {
947
+ let Some ( & to) = pkg_to_lib_crate. get ( & pkg) else { continue } ;
948
+ match test_dupes. entry ( to) {
949
+ Entry :: Occupied ( _) => continue ,
950
+ Entry :: Vacant ( v) => {
951
+ for dep in & cargo[ pkg] . dependencies {
952
+ if dep. kind == DepKind :: Normal && cargo[ dep. pkg ] . is_member {
953
+ work. push ( dep. pkg ) ;
954
+ }
955
+ }
956
+ v. insert ( {
957
+ let duped = crate_graph. duplicate ( to) ;
958
+ if let Some ( proc_macro) = proc_macros. get ( & to) . cloned ( ) {
959
+ proc_macros. insert ( duped, proc_macro) ;
960
+ }
961
+ crate_graph[ duped] . cfg_options . insert_atom ( "test" . into ( ) ) ;
962
+ duped
963
+ } ) ;
964
+ }
965
+ }
966
+ }
967
+
948
968
// Now add a dep edge from all targets of upstream to the lib
949
969
// target of downstream.
950
970
for pkg in cargo. packages ( ) {
@@ -958,12 +978,66 @@ fn cargo_to_crate_graph(
958
978
if ( dep. kind == DepKind :: Build ) != ( kind == TargetKind :: BuildScript ) {
959
979
continue ;
960
980
}
981
+ add_dep (
982
+ crate_graph,
983
+ from,
984
+ name. clone ( ) ,
985
+ if dep. kind == DepKind :: Dev {
986
+ // point to the test enabled duplicate for dev-dependencies
987
+ test_dupes. get ( & to) . copied ( ) . unwrap_or ( to)
988
+ } else {
989
+ to
990
+ } ,
991
+ ) ;
961
992
962
- add_dep ( crate_graph, from, name. clone ( ) , to)
993
+ if dep. kind == DepKind :: Normal && cargo[ dep. pkg ] . is_member {
994
+ // Also apply the dependency as a test enabled dependency to the test duplicate
995
+ if let Some ( & dupe) = test_dupes. get ( & from) {
996
+ let to = test_dupes. get ( & to) . copied ( ) . unwrap_or_else ( || {
997
+ panic ! (
998
+ "dependency of a dev dependency did not get duplicated! {:?} {:?}" ,
999
+ crate_graph[ to] . display_name, crate_graph[ from] . display_name,
1000
+ )
1001
+ } ) ;
1002
+ add_dep ( crate_graph, dupe, name. clone ( ) , to) ;
1003
+ }
1004
+ }
1005
+ }
1006
+ }
1007
+ }
1008
+
1009
+ for ( & pkg, targets) in & pkg_crates {
1010
+ for & ( krate, _) in targets {
1011
+ if test_dupes. get ( & krate) . is_some ( ) {
1012
+ // if the crate got duped as a dev-dep the dupe already has test set
1013
+ continue ;
1014
+ }
1015
+ let cfg_options = & mut crate_graph[ krate] . cfg_options ;
1016
+
1017
+ // Add test cfg for local crates
1018
+ if cargo[ pkg] . is_local {
1019
+ cfg_options. insert_atom ( "test" . into ( ) ) ;
963
1020
}
1021
+
1022
+ let overrides = match override_cfg {
1023
+ CfgOverrides :: Wildcard ( cfg_diff) => Some ( cfg_diff) ,
1024
+ CfgOverrides :: Selective ( cfg_overrides) => cfg_overrides. get ( & cargo[ pkg] . name ) ,
1025
+ } ;
1026
+
1027
+ if let Some ( overrides) = overrides {
1028
+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
1029
+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
1030
+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
1031
+ //
1032
+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
1033
+ // figure out the set of cfgs that would have to apply to make it active.
1034
+
1035
+ cfg_options. apply_diff ( overrides. clone ( ) ) ;
1036
+ } ;
964
1037
}
965
1038
}
966
1039
1040
+ // FIXME: Handle rustc private crates properly when used as dev-dependencies
967
1041
if has_private {
968
1042
// If the user provided a path to rustc sources, we add all the rustc_private crates
969
1043
// and create dependencies on them for the crates which opt-in to that
@@ -1087,7 +1161,9 @@ fn handle_rustc_crates(
1087
1161
continue ;
1088
1162
}
1089
1163
for dep in & rustc_workspace[ pkg] . dependencies {
1090
- queue. push_back ( dep. pkg ) ;
1164
+ if dep. kind == DepKind :: Normal {
1165
+ queue. push_back ( dep. pkg ) ;
1166
+ }
1091
1167
}
1092
1168
1093
1169
let mut cfg_options = cfg_options. clone ( ) ;
@@ -1397,10 +1473,12 @@ fn handle_hack_cargo_workspace(
1397
1473
. collect ( )
1398
1474
}
1399
1475
1476
+ #[ track_caller]
1400
1477
fn add_dep ( graph : & mut CrateGraph , from : CrateId , name : CrateName , to : CrateId ) {
1401
1478
add_dep_inner ( graph, from, Dependency :: new ( name, to) )
1402
1479
}
1403
1480
1481
+ #[ track_caller]
1404
1482
fn add_dep_with_prelude (
1405
1483
graph : & mut CrateGraph ,
1406
1484
from : CrateId ,
@@ -1411,13 +1489,18 @@ fn add_dep_with_prelude(
1411
1489
add_dep_inner ( graph, from, Dependency :: with_prelude ( name, to, prelude) )
1412
1490
}
1413
1491
1492
+ #[ track_caller]
1414
1493
fn add_proc_macro_dep ( crate_graph : & mut CrateGraph , from : CrateId , to : CrateId , prelude : bool ) {
1415
1494
add_dep_with_prelude ( crate_graph, from, CrateName :: new ( "proc_macro" ) . unwrap ( ) , to, prelude) ;
1416
1495
}
1417
1496
1497
+ #[ track_caller]
1418
1498
fn add_dep_inner ( graph : & mut CrateGraph , from : CrateId , dep : Dependency ) {
1419
1499
if let Err ( err) = graph. add_dep ( from, dep) {
1420
- tracing:: error!( "{}" , err)
1500
+ if cfg ! ( test) {
1501
+ panic ! ( "{}" , err) ;
1502
+ }
1503
+ tracing:: error!( "{}" , err) ;
1421
1504
}
1422
1505
}
1423
1506
0 commit comments