@@ -49,13 +49,13 @@ pub type ContextAge = usize;
49
49
/// By storing this in a hash map we ensure that there is only one
50
50
/// semver compatible version of each crate.
51
51
/// This all so stores the `ContextAge`.
52
- pub type Activations =
53
- im_rc:: HashMap < ( InternedString , SourceId , SemverCompatibility ) , ( Summary , ContextAge ) > ;
52
+ pub type ActivationsKey = ( InternedString , SourceId , SemverCompatibility ) ;
53
+ pub type Activations = im_rc:: HashMap < ActivationsKey , ( Summary , ContextAge ) > ;
54
54
55
55
/// A type that represents when cargo treats two Versions as compatible.
56
56
/// Versions `a` and `b` are compatible if their left-most nonzero digit is the
57
57
/// same.
58
- #[ derive( Clone , Copy , Eq , PartialEq , Hash , Debug ) ]
58
+ #[ derive( Clone , Copy , Eq , PartialEq , Hash , Debug , PartialOrd , Ord ) ]
59
59
pub enum SemverCompatibility {
60
60
Major ( NonZeroU64 ) ,
61
61
Minor ( NonZeroU64 ) ,
@@ -75,7 +75,7 @@ impl From<&semver::Version> for SemverCompatibility {
75
75
}
76
76
77
77
impl PackageId {
78
- pub fn as_activations_key ( self ) -> ( InternedString , SourceId , SemverCompatibility ) {
78
+ pub fn as_activations_key ( self ) -> ActivationsKey {
79
79
( self . name ( ) , self . source_id ( ) , self . version ( ) . into ( ) )
80
80
}
81
81
}
@@ -189,6 +189,42 @@ impl Context {
189
189
. and_then ( |( s, l) | if s. package_id ( ) == id { Some ( * l) } else { None } )
190
190
}
191
191
192
+ /// If the conflict reason on the package still applies returns the `ContextAge` when it was added
193
+ pub fn still_applies ( & self , id : PackageId , reason : & ConflictReason ) -> Option < ContextAge > {
194
+ self . is_active ( id) . and_then ( |mut max| {
195
+ match reason {
196
+ ConflictReason :: PublicDependency ( name) => {
197
+ if & id == name {
198
+ return Some ( max) ;
199
+ }
200
+ max = std:: cmp:: max ( max, self . is_active ( * name) ?) ;
201
+ max = std:: cmp:: max (
202
+ max,
203
+ self . public_dependency
204
+ . as_ref ( )
205
+ . unwrap ( )
206
+ . can_see_item ( * name, id) ?,
207
+ ) ;
208
+ }
209
+ ConflictReason :: PubliclyExports ( name) => {
210
+ if & id == name {
211
+ return Some ( max) ;
212
+ }
213
+ max = std:: cmp:: max ( max, self . is_active ( * name) ?) ;
214
+ max = std:: cmp:: max (
215
+ max,
216
+ self . public_dependency
217
+ . as_ref ( )
218
+ . unwrap ( )
219
+ . publicly_exports_item ( * name, id) ?,
220
+ ) ;
221
+ }
222
+ _ => { }
223
+ }
224
+ Some ( max)
225
+ } )
226
+ }
227
+
192
228
/// Checks whether all of `parent` and the keys of `conflicting activations`
193
229
/// are still active.
194
230
/// If so returns the `ContextAge` when the newest one was added.
@@ -198,12 +234,12 @@ impl Context {
198
234
conflicting_activations : & ConflictMap ,
199
235
) -> Option < usize > {
200
236
let mut max = 0 ;
201
- for & id in conflicting_activations . keys ( ) . chain ( parent. as_ref ( ) ) {
202
- if let Some ( age ) = self . is_active ( id ) {
203
- max = std :: cmp :: max ( max , age ) ;
204
- } else {
205
- return None ;
206
- }
237
+ if let Some ( parent) = parent {
238
+ max = std :: cmp :: max ( max , self . is_active ( parent ) ? ) ;
239
+ }
240
+
241
+ for ( id , reason ) in conflicting_activations . iter ( ) {
242
+ max = std :: cmp :: max ( max , self . still_applies ( * id , reason ) ? ) ;
207
243
}
208
244
Some ( max)
209
245
}
@@ -270,6 +306,31 @@ impl PublicDependency {
270
306
. chain ( Some ( candidate_pid) ) // but even if not we know that everything exports itself
271
307
. collect ( )
272
308
}
309
+ fn publicly_exports_item (
310
+ & self ,
311
+ candidate_pid : PackageId ,
312
+ target : PackageId ,
313
+ ) -> Option < ContextAge > {
314
+ debug_assert_ne ! ( candidate_pid, target) ;
315
+ let out = self
316
+ . inner
317
+ . get ( & candidate_pid)
318
+ . and_then ( |names| names. get ( & target. name ( ) ) )
319
+ . filter ( |( p, _, _) | * p == target)
320
+ . and_then ( |( _, _, age) | * age) ;
321
+ debug_assert_eq ! (
322
+ out. is_some( ) ,
323
+ self . publicly_exports( candidate_pid) . contains( & target)
324
+ ) ;
325
+ out
326
+ }
327
+ pub fn can_see_item ( & self , candidate_pid : PackageId , target : PackageId ) -> Option < ContextAge > {
328
+ self . inner
329
+ . get ( & candidate_pid)
330
+ . and_then ( |names| names. get ( & target. name ( ) ) )
331
+ . filter ( |( p, _, _) | * p == target)
332
+ . map ( |( _, age, _) | * age)
333
+ }
273
334
pub fn add_edge (
274
335
& mut self ,
275
336
candidate_pid : PackageId ,
@@ -322,7 +383,13 @@ impl PublicDependency {
322
383
parent : PackageId ,
323
384
is_public : bool ,
324
385
parents : & Graph < PackageId , Rc < Vec < Dependency > > > ,
325
- ) -> Result < ( ) , ( PackageId , ConflictReason ) > {
386
+ ) -> Result <
387
+ ( ) ,
388
+ (
389
+ ( ( PackageId , ConflictReason ) , ( PackageId , ConflictReason ) ) ,
390
+ Option < ( PackageId , ConflictReason ) > ,
391
+ ) ,
392
+ > {
326
393
// one tricky part is that `candidate_pid` may already be active and
327
394
// have public dependencies of its own. So we not only need to check
328
395
// `b_id` as visible to its parents but also all of its existing
@@ -336,7 +403,17 @@ impl PublicDependency {
336
403
if o. 0 != t {
337
404
// the (transitive) parent can already see a different version by `t`s name.
338
405
// So, adding `b` will cause `p` to have a public dependency conflict on `t`.
339
- return Err ( ( p, ConflictReason :: PublicDependency ) ) ;
406
+ return Err ( (
407
+ ( o. 0 , ConflictReason :: PublicDependency ( p) ) , // p can see the other version and
408
+ ( parent, ConflictReason :: PublicDependency ( p) ) , // p can see us
409
+ ) )
410
+ . map_err ( |e| {
411
+ if t == b_id {
412
+ ( e, None )
413
+ } else {
414
+ ( e, Some ( ( t, ConflictReason :: PubliclyExports ( b_id) ) ) )
415
+ }
416
+ } ) ;
340
417
}
341
418
if o. 2 . is_some ( ) {
342
419
// The previous time the parent saw `t`, it was a public dependency.
0 commit comments