11//! Converts from `cargo_metadata` crate structs to `auditable-serde` structs,
22//! which map to our own serialialized representation.
33
4- use std:: { cmp:: min, cmp:: Ordering :: * , collections:: HashMap , error:: Error , fmt:: Display } ;
4+ use std:: {
5+ cmp:: { min, Ordering :: * } ,
6+ collections:: { HashMap , HashSet } ,
7+ error:: Error ,
8+ fmt:: Display ,
9+ } ;
510
611use auditable_serde:: { DependencyKind , Package , Source , VersionInfo } ;
712
@@ -84,6 +89,8 @@ pub fn encode_audit_data(
8489 . repr
8590 . as_str ( ) ;
8691
92+ let proc_macros = proc_macro_packages ( metadata) ;
93+
8794 // Walk the dependency tree and resolve dependency kinds for each package.
8895 // We need this because there may be several different paths to the same package
8996 // and we need to aggregate dependency types across all of them.
@@ -103,8 +110,14 @@ pub fn encode_audit_data(
103110 let parent_dep_kind = id_to_dep_kind[ parent. id . repr . as_str ( ) ] ;
104111 for child in & parent. deps {
105112 let child_id = child. pkg . repr . as_str ( ) ;
106- let dep_kind = strongest_dep_kind ( child. dep_kinds . as_slice ( ) ) ;
107- let dep_kind = min ( dep_kind, parent_dep_kind) ;
113+ let mut dep_kind = strongest_dep_kind ( child. dep_kinds . as_slice ( ) ) ;
114+ // If the parent is a build dependency that has a runtime dependency, overall dependency should be 'build'.
115+ // This propagates the dependency kinds that way from parent to child.
116+ dep_kind = min ( dep_kind, parent_dep_kind) ;
117+ // proc macros require special handling since cargo_metadata reports them as normal deps
118+ if proc_macros. contains ( child_id) {
119+ dep_kind = min ( dep_kind, PrivateDepKind :: Build ) ;
120+ }
108121 let dep_kind_on_previous_visit = id_to_dep_kind. get ( child_id) ;
109122 if dep_kind_on_previous_visit. is_none ( )
110123 || & dep_kind > dep_kind_on_previous_visit. unwrap ( )
@@ -215,6 +228,25 @@ fn strongest_dep_kind(deps: &[cargo_metadata::DepKindInfo]) -> PrivateDepKind {
215228 . unwrap_or ( PrivateDepKind :: Runtime ) // for compatibility with Rust earlier than 1.41
216229}
217230
231+ fn proc_macro_packages ( metadata : & cargo_metadata:: Metadata ) -> HashSet < & str > {
232+ metadata
233+ . packages
234+ . iter ( )
235+ . filter_map ( |pkg| {
236+ // As of Rust 1.88 a single crate cannot be both a proc macro and something else.
237+ // Checking that length is 1 is purely to hedge against support for it being added in the future.
238+ if pkg. targets . len ( ) == 1
239+ && pkg. targets [ 0 ] . kind . len ( ) == 1
240+ && pkg. targets [ 0 ] . kind [ 0 ] == "proc-macro"
241+ {
242+ Some ( pkg. id . repr . as_str ( ) )
243+ } else {
244+ None
245+ }
246+ } )
247+ . collect ( )
248+ }
249+
218250#[ cfg( test) ]
219251mod tests {
220252 #![ allow( unused_imports) ] // otherwise conditional compilation emits warnings
0 commit comments