1
1
//! Look up accessible paths for items.
2
+
2
3
use hir:: {
3
- AsAssocItem , AssocItem , AssocItemContainer , Crate , ItemInNs , ModPath , Module , ModuleDef ,
4
+ AsAssocItem , AssocItem , AssocItemContainer , Crate , ItemInNs , ModPath , Module , ModuleDef , Name ,
4
5
PathResolution , PrefixKind , ScopeDef , Semantics , SemanticsScope , Type ,
5
6
} ;
6
- use itertools:: Itertools ;
7
- use rustc_hash:: FxHashSet ;
7
+ use itertools:: { EitherOrBoth , Itertools } ;
8
+ use rustc_hash:: { FxHashMap , FxHashSet } ;
8
9
use syntax:: {
9
10
ast:: { self , make, HasName } ,
10
- utils:: path_to_string_stripping_turbo_fish,
11
- AstNode , SyntaxNode ,
11
+ AstNode , SmolStr , SyntaxNode ,
12
12
} ;
13
13
14
14
use crate :: {
@@ -51,18 +51,11 @@ pub struct TraitImportCandidate {
51
51
#[ derive( Debug ) ]
52
52
pub struct PathImportCandidate {
53
53
/// Optional qualifier before name.
54
- pub qualifier : Option < FirstSegmentUnresolved > ,
54
+ pub qualifier : Option < Vec < SmolStr > > ,
55
55
/// The name the item (struct, trait, enum, etc.) should have.
56
56
pub name : NameToImport ,
57
57
}
58
58
59
- /// A qualifier that has a first segment and it's unresolved.
60
- #[ derive( Debug ) ]
61
- pub struct FirstSegmentUnresolved {
62
- fist_segment : ast:: NameRef ,
63
- full_qualifier : ast:: Path ,
64
- }
65
-
66
59
/// A name that will be used during item lookups.
67
60
#[ derive( Debug , Clone ) ]
68
61
pub enum NameToImport {
@@ -348,60 +341,71 @@ fn path_applicable_imports(
348
341
} )
349
342
. collect ( )
350
343
}
351
- Some ( first_segment_unresolved) => {
352
- let unresolved_qualifier =
353
- path_to_string_stripping_turbo_fish ( & first_segment_unresolved. full_qualifier ) ;
354
- let unresolved_first_segment = first_segment_unresolved. fist_segment . text ( ) ;
355
- items_locator:: items_with_name (
356
- sema,
357
- current_crate,
358
- path_candidate. name . clone ( ) ,
359
- AssocSearchMode :: Include ,
360
- Some ( DEFAULT_QUERY_SEARCH_LIMIT . inner ( ) ) ,
361
- )
362
- . filter_map ( |item| {
363
- import_for_item (
364
- sema. db ,
365
- mod_path,
366
- & unresolved_first_segment,
367
- & unresolved_qualifier,
368
- item,
369
- )
370
- } )
371
- . collect ( )
372
- }
344
+ Some ( qualifier) => items_locator:: items_with_name (
345
+ sema,
346
+ current_crate,
347
+ path_candidate. name . clone ( ) ,
348
+ AssocSearchMode :: Include ,
349
+ Some ( DEFAULT_QUERY_SEARCH_LIMIT . inner ( ) ) ,
350
+ )
351
+ . filter_map ( |item| import_for_item ( sema. db , mod_path, & qualifier, item) )
352
+ . collect ( ) ,
373
353
}
374
354
}
375
355
376
356
fn import_for_item (
377
357
db : & RootDatabase ,
378
358
mod_path : impl Fn ( ItemInNs ) -> Option < ModPath > ,
379
- unresolved_first_segment : & str ,
380
- unresolved_qualifier : & str ,
359
+ unresolved_qualifier : & [ SmolStr ] ,
381
360
original_item : ItemInNs ,
382
361
) -> Option < LocatedImport > {
383
362
let _p = profile:: span ( "import_assets::import_for_item" ) ;
363
+ let [ first_segment, ..] = unresolved_qualifier else { return None } ;
384
364
385
- let original_item_candidate = item_for_path_search ( db, original_item) ?;
365
+ let item_as_assoc = item_as_assoc ( db, original_item) ;
366
+
367
+ let ( original_item_candidate, trait_item_to_import) = match item_as_assoc {
368
+ Some ( assoc_item) => match assoc_item. container ( db) {
369
+ AssocItemContainer :: Trait ( trait_) => {
370
+ let trait_ = ItemInNs :: from ( ModuleDef :: from ( trait_) ) ;
371
+ ( trait_, Some ( trait_) )
372
+ }
373
+ AssocItemContainer :: Impl ( impl_) => {
374
+ ( ItemInNs :: from ( ModuleDef :: from ( impl_. self_ty ( db) . as_adt ( ) ?) ) , None )
375
+ }
376
+ } ,
377
+ None => ( original_item, None ) ,
378
+ } ;
386
379
let import_path_candidate = mod_path ( original_item_candidate) ?;
387
- let import_path_string = import_path_candidate. display ( db) . to_string ( ) ;
388
380
389
- let expected_import_end = if item_as_assoc ( db, original_item) . is_some ( ) {
390
- unresolved_qualifier. to_string ( )
391
- } else {
392
- format ! ( "{unresolved_qualifier}::{}" , item_name( db, original_item) ?. display( db) )
381
+ let mut import_path_candidate_segments = import_path_candidate. segments ( ) . iter ( ) . rev ( ) ;
382
+ let predicate = |it : EitherOrBoth < & SmolStr , & Name > | match it {
383
+ // segments match, check next one
384
+ EitherOrBoth :: Both ( a, b) if b. as_str ( ) == Some ( & * * a) => None ,
385
+ // segments mismatch / qualifier is longer than the path, bail out
386
+ EitherOrBoth :: Both ( ..) | EitherOrBoth :: Left ( _) => Some ( false ) ,
387
+ // all segments match and we have exhausted the qualifier, proceed
388
+ EitherOrBoth :: Right ( _) => Some ( true ) ,
393
389
} ;
394
- if !import_path_string. contains ( unresolved_first_segment)
395
- || !import_path_string. ends_with ( & expected_import_end)
396
- {
390
+ if item_as_assoc. is_none ( ) {
391
+ let item_name = item_name ( db, original_item) ?. as_text ( ) ?;
392
+ let last_segment = import_path_candidate_segments. next ( ) ?;
393
+ if last_segment. as_str ( ) != Some ( & * item_name) {
394
+ return None ;
395
+ }
396
+ }
397
+ let ends_with = unresolved_qualifier
398
+ . iter ( )
399
+ . rev ( )
400
+ . zip_longest ( import_path_candidate_segments)
401
+ . find_map ( predicate)
402
+ . unwrap_or ( true ) ;
403
+ if !ends_with {
397
404
return None ;
398
405
}
399
406
400
- let segment_import =
401
- find_import_for_segment ( db, original_item_candidate, unresolved_first_segment) ?;
402
- let trait_item_to_import = item_as_assoc ( db, original_item)
403
- . and_then ( |assoc| assoc. containing_trait ( db) )
404
- . map ( |trait_| ItemInNs :: from ( ModuleDef :: from ( trait_) ) ) ;
407
+ let segment_import = find_import_for_segment ( db, original_item_candidate, first_segment) ?;
408
+
405
409
Some ( match ( segment_import == original_item_candidate, trait_item_to_import) {
406
410
( true , Some ( _) ) => {
407
411
// FIXME we should be able to import both the trait and the segment,
@@ -424,18 +428,22 @@ fn import_for_item(
424
428
pub fn item_for_path_search ( db : & RootDatabase , item : ItemInNs ) -> Option < ItemInNs > {
425
429
Some ( match item {
426
430
ItemInNs :: Types ( _) | ItemInNs :: Values ( _) => match item_as_assoc ( db, item) {
427
- Some ( assoc_item) => match assoc_item. container ( db) {
428
- AssocItemContainer :: Trait ( trait_) => ItemInNs :: from ( ModuleDef :: from ( trait_) ) ,
429
- AssocItemContainer :: Impl ( impl_) => {
430
- ItemInNs :: from ( ModuleDef :: from ( impl_. self_ty ( db) . as_adt ( ) ?) )
431
- }
432
- } ,
431
+ Some ( assoc_item) => item_for_path_search_assoc ( db, assoc_item) ?,
433
432
None => item,
434
433
} ,
435
434
ItemInNs :: Macros ( _) => item,
436
435
} )
437
436
}
438
437
438
+ fn item_for_path_search_assoc ( db : & RootDatabase , assoc_item : AssocItem ) -> Option < ItemInNs > {
439
+ Some ( match assoc_item. container ( db) {
440
+ AssocItemContainer :: Trait ( trait_) => ItemInNs :: from ( ModuleDef :: from ( trait_) ) ,
441
+ AssocItemContainer :: Impl ( impl_) => {
442
+ ItemInNs :: from ( ModuleDef :: from ( impl_. self_ty ( db) . as_adt ( ) ?) )
443
+ }
444
+ } )
445
+ }
446
+
439
447
fn find_import_for_segment (
440
448
db : & RootDatabase ,
441
449
original_item : ItemInNs ,
@@ -512,6 +520,7 @@ fn trait_applicable_items(
512
520
. collect ( ) ;
513
521
514
522
let mut located_imports = FxHashSet :: default ( ) ;
523
+ let mut trait_import_paths = FxHashMap :: default ( ) ;
515
524
516
525
if trait_assoc_item {
517
526
trait_candidate. receiver_ty . iterate_path_candidates (
@@ -529,11 +538,14 @@ fn trait_applicable_items(
529
538
}
530
539
let located_trait = assoc. containing_trait ( db) ?;
531
540
let trait_item = ItemInNs :: from ( ModuleDef :: from ( located_trait) ) ;
532
- let original_item = assoc_to_item ( assoc) ;
541
+ let import_path = trait_import_paths
542
+ . entry ( trait_item)
543
+ . or_insert_with ( || mod_path ( trait_item) )
544
+ . clone ( ) ?;
533
545
located_imports. insert ( LocatedImport :: new (
534
- mod_path ( trait_item ) ? ,
546
+ import_path ,
535
547
trait_item,
536
- original_item ,
548
+ assoc_to_item ( assoc ) ,
537
549
) ) ;
538
550
}
539
551
None :: < ( ) >
@@ -551,11 +563,14 @@ fn trait_applicable_items(
551
563
if required_assoc_items. contains ( & assoc) {
552
564
let located_trait = assoc. containing_trait ( db) ?;
553
565
let trait_item = ItemInNs :: from ( ModuleDef :: from ( located_trait) ) ;
554
- let original_item = assoc_to_item ( assoc) ;
566
+ let import_path = trait_import_paths
567
+ . entry ( trait_item)
568
+ . or_insert_with ( || mod_path ( trait_item) )
569
+ . clone ( ) ?;
555
570
located_imports. insert ( LocatedImport :: new (
556
- mod_path ( trait_item ) ? ,
571
+ import_path ,
557
572
trait_item,
558
- original_item ,
573
+ assoc_to_item ( assoc ) ,
559
574
) ) ;
560
575
}
561
576
None :: < ( ) >
@@ -653,18 +668,13 @@ fn path_import_candidate(
653
668
Some ( match qualifier {
654
669
Some ( qualifier) => match sema. resolve_path ( & qualifier) {
655
670
None => {
656
- let qualifier_start =
657
- qualifier. syntax ( ) . descendants ( ) . find_map ( ast:: NameRef :: cast) ?;
658
- let qualifier_start_path =
659
- qualifier_start. syntax ( ) . ancestors ( ) . find_map ( ast:: Path :: cast) ?;
660
- if sema. resolve_path ( & qualifier_start_path) . is_none ( ) {
661
- ImportCandidate :: Path ( PathImportCandidate {
662
- qualifier : Some ( FirstSegmentUnresolved {
663
- fist_segment : qualifier_start,
664
- full_qualifier : qualifier,
665
- } ) ,
666
- name,
667
- } )
671
+ if qualifier. first_qualifier ( ) . map_or ( true , |it| sema. resolve_path ( & it) . is_none ( ) ) {
672
+ let mut qualifier = qualifier
673
+ . segments_of_this_path_only_rev ( )
674
+ . map ( |seg| seg. name_ref ( ) . map ( |name| SmolStr :: new ( name. text ( ) ) ) )
675
+ . collect :: < Option < Vec < _ > > > ( ) ?;
676
+ qualifier. reverse ( ) ;
677
+ ImportCandidate :: Path ( PathImportCandidate { qualifier : Some ( qualifier) , name } )
668
678
} else {
669
679
return None ;
670
680
}
0 commit comments