22//! metadata` or `rust-project.json`) into representation stored in the salsa
33//! database -- `CrateGraph`.
44
5- use std:: { collections:: VecDeque , fmt, fs, process:: Command , sync:: Arc } ;
5+ use std:: {
6+ collections:: { hash_map:: Entry , VecDeque } ,
7+ fmt, fs,
8+ panic:: Location ,
9+ process:: Command ,
10+ sync:: Arc ,
11+ } ;
612
713use anyhow:: { format_err, Context , Result } ;
814use base_db:: {
@@ -858,32 +864,6 @@ fn cargo_to_crate_graph(
858864 for pkg in cargo. packages ( ) {
859865 has_private |= cargo[ pkg] . metadata . rustc_private ;
860866
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-
887867 let mut lib_tgt = None ;
888868 for & tgt in cargo[ pkg] . targets . iter ( ) {
889869 if cargo[ tgt] . kind != TargetKind :: Lib && !cargo[ pkg] . is_member {
@@ -894,7 +874,7 @@ fn cargo_to_crate_graph(
894874 // https://github.com/rust-lang/rust-analyzer/issues/11300
895875 continue ;
896876 }
897- let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
877+ let Some ( file_id) = load ( & cargo[ tgt] . root ) else { continue } ;
898878
899879 let crate_id = add_target_crate_root (
900880 crate_graph,
@@ -922,15 +902,19 @@ fn cargo_to_crate_graph(
922902 pkg_crates. entry ( pkg) . or_insert_with ( Vec :: new) . push ( ( crate_id, cargo[ tgt] . kind ) ) ;
923903 }
924904
905+ let Some ( targets) = pkg_crates. get ( & pkg) else { continue } ;
925906 // 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 ( ) {
907+ for & ( from, kind) in targets {
927908 // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
928909 public_deps. add_to_crate_graph ( crate_graph, from) ;
929910
930911 // Add dep edge of all targets to the package's lib target
931912 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)
913+ if to != from {
914+ if kind == TargetKind :: BuildScript {
915+ // build script can not depend on its library target
916+ continue ;
917+ }
934918
935919 // For root projects with dashes in their name,
936920 // cargo metadata does not do any normalization,
@@ -942,6 +926,40 @@ fn cargo_to_crate_graph(
942926 }
943927 }
944928
929+ // Map from crate id to it's dev-dependency clone id
930+ let mut test_dupes = FxHashMap :: default ( ) ;
931+ let mut work = vec ! [ ] ;
932+
933+ // Get all dependencies of the workspace members that are used as dev-dependencies
934+ for pkg in cargo. packages ( ) {
935+ for dep in & cargo[ pkg] . dependencies {
936+ if dep. kind == DepKind :: Dev {
937+ work. push ( dep. pkg ) ;
938+ }
939+ }
940+ }
941+ while let Some ( pkg) = work. pop ( ) {
942+ let Some ( & to) = pkg_to_lib_crate. get ( & pkg) else { continue } ;
943+ match test_dupes. entry ( to) {
944+ Entry :: Occupied ( _) => continue ,
945+ Entry :: Vacant ( v) => {
946+ for dep in & cargo[ pkg] . dependencies {
947+ if dep. kind == DepKind :: Normal {
948+ work. push ( dep. pkg ) ;
949+ }
950+ }
951+ v. insert ( {
952+ let duped = crate_graph. duplicate ( to) ;
953+ if let Some ( proc_macro) = proc_macros. get ( & to) . cloned ( ) {
954+ proc_macros. insert ( duped, proc_macro) ;
955+ }
956+ crate_graph[ duped] . cfg_options . insert_atom ( "test" . into ( ) ) ;
957+ duped
958+ } ) ;
959+ }
960+ }
961+ }
962+
945963 // Now add a dep edge from all targets of upstream to the lib
946964 // target of downstream.
947965 for pkg in cargo. packages ( ) {
@@ -955,12 +973,65 @@ fn cargo_to_crate_graph(
955973 if ( dep. kind == DepKind :: Build ) != ( kind == TargetKind :: BuildScript ) {
956974 continue ;
957975 }
976+ add_dep (
977+ crate_graph,
978+ from,
979+ name. clone ( ) ,
980+ if dep. kind == DepKind :: Dev {
981+ // point to the test enabled duplicate for dev-dependencies
982+ test_dupes. get ( & to) . copied ( ) . unwrap ( )
983+ } else {
984+ to
985+ } ,
986+ ) ;
987+
988+ if dep. kind == DepKind :: Normal {
989+ // Also apply the dependency as a test enabled dependency to the test duplicate
990+ if let Some ( & dupe) = test_dupes. get ( & from) {
991+ let to = test_dupes. get ( & to) . copied ( ) . unwrap_or_else ( || {
992+ panic ! (
993+ "dependency of a dev dependency did not get duplicated! {:?} {:?}" ,
994+ crate_graph[ to] . display_name, crate_graph[ from] . display_name,
995+ )
996+ } ) ;
997+ add_dep ( crate_graph, dupe, name. clone ( ) , to) ;
998+ }
999+ }
1000+ }
1001+ }
1002+ }
9581003
959- add_dep ( crate_graph, from, name. clone ( ) , to)
1004+ for ( & pkg, targets) in & pkg_crates {
1005+ for & ( krate, _) in targets {
1006+ if test_dupes. get ( & krate) . is_some ( ) {
1007+ continue ;
1008+ }
1009+ let cfg_options = & mut crate_graph[ krate] . cfg_options ;
1010+
1011+ // Add test cfg for local crates
1012+ if cargo[ pkg] . is_local {
1013+ cfg_options. insert_atom ( "test" . into ( ) ) ;
9601014 }
1015+
1016+ let overrides = match override_cfg {
1017+ CfgOverrides :: Wildcard ( cfg_diff) => Some ( cfg_diff) ,
1018+ CfgOverrides :: Selective ( cfg_overrides) => cfg_overrides. get ( & cargo[ pkg] . name ) ,
1019+ } ;
1020+
1021+ if let Some ( overrides) = overrides {
1022+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
1023+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
1024+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
1025+ //
1026+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
1027+ // figure out the set of cfgs that would have to apply to make it active.
1028+
1029+ cfg_options. apply_diff ( overrides. clone ( ) ) ;
1030+ } ;
9611031 }
9621032 }
9631033
1034+ // FIXME: Handle rustc private crates properly when used as dev-dependencies
9641035 if has_private {
9651036 // If the user provided a path to rustc sources, we add all the rustc_private crates
9661037 // and create dependencies on them for the crates which opt-in to that
@@ -1325,10 +1396,12 @@ fn sysroot_to_crate_graph(
13251396 ( public_deps, libproc_macro)
13261397}
13271398
1399+ #[ track_caller]
13281400fn add_dep ( graph : & mut CrateGraph , from : CrateId , name : CrateName , to : CrateId ) {
13291401 add_dep_inner ( graph, from, Dependency :: new ( name, to) )
13301402}
13311403
1404+ #[ track_caller]
13321405fn add_dep_with_prelude (
13331406 graph : & mut CrateGraph ,
13341407 from : CrateId ,
@@ -1339,13 +1412,18 @@ fn add_dep_with_prelude(
13391412 add_dep_inner ( graph, from, Dependency :: with_prelude ( name, to, prelude) )
13401413}
13411414
1415+ #[ track_caller]
13421416fn add_proc_macro_dep ( crate_graph : & mut CrateGraph , from : CrateId , to : CrateId , prelude : bool ) {
13431417 add_dep_with_prelude ( crate_graph, from, CrateName :: new ( "proc_macro" ) . unwrap ( ) , to, prelude) ;
13441418}
13451419
1420+ #[ track_caller]
13461421fn add_dep_inner ( graph : & mut CrateGraph , from : CrateId , dep : Dependency ) {
13471422 if let Err ( err) = graph. add_dep ( from, dep) {
1348- tracing:: error!( "{}" , err)
1423+ if cfg ! ( test) {
1424+ panic ! ( "{}" , err) ;
1425+ }
1426+ tracing:: error!( "{}" , err) ;
13491427 }
13501428}
13511429
0 commit comments