@@ -340,16 +340,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
340
340
err : & mut DiagnosticBuilder ,
341
341
mut msg : String ,
342
342
candidates : Vec < DefId > ) {
343
- let limit = if candidates. len ( ) == 5 { 5 } else { 4 } ;
344
- for ( i, trait_did) in candidates. iter ( ) . take ( limit) . enumerate ( ) {
345
- msg. push_str ( & format ! ( "\n candidate #{}: `use {};`" ,
346
- i + 1 ,
347
- self . tcx. item_path_str( * trait_did) ) ) ;
348
- }
349
- if candidates. len ( ) > limit {
350
- msg. push_str ( & format ! ( "\n and {} others" , candidates. len( ) - limit) ) ;
343
+ let module_did = self . tcx . hir . get_module_parent ( self . body_id ) ;
344
+ let module_id = self . tcx . hir . as_local_node_id ( module_did) . unwrap ( ) ;
345
+ let krate = self . tcx . hir . krate ( ) ;
346
+ let ( span, found_use) = UsePlacementFinder :: check ( self . tcx , krate, module_id) ;
347
+ if let Some ( span) = span {
348
+ let path_strings = candidates. iter ( ) . map ( |did| {
349
+ // produce an additional newline to separate the new use statement
350
+ // from the directly following item.
351
+ let additional_newline = if found_use {
352
+ ""
353
+ } else {
354
+ "\n "
355
+ } ;
356
+ format ! ( "use {};\n {}" , self . tcx. item_path_str( * did) , additional_newline)
357
+ } ) . collect ( ) ;
358
+
359
+ err. span_suggestions ( span, & msg, path_strings) ;
360
+ } else {
361
+ let limit = if candidates. len ( ) == 5 { 5 } else { 4 } ;
362
+ for ( i, trait_did) in candidates. iter ( ) . take ( limit) . enumerate ( ) {
363
+ msg. push_str ( & format ! ( "\n candidate #{}: `use {};`" ,
364
+ i + 1 ,
365
+ self . tcx. item_path_str( * trait_did) ) ) ;
366
+ }
367
+ if candidates. len ( ) > limit {
368
+ msg. push_str ( & format ! ( "\n and {} others" , candidates. len( ) - limit) ) ;
369
+ }
370
+ err. note ( & msg[ ..] ) ;
351
371
}
352
- err. note ( & msg[ ..] ) ;
353
372
}
354
373
355
374
fn suggest_valid_traits ( & self ,
@@ -604,3 +623,83 @@ impl<'a> Iterator for AllTraits<'a> {
604
623
} )
605
624
}
606
625
}
626
+
627
+
628
+ struct UsePlacementFinder < ' a , ' tcx : ' a , ' gcx : ' tcx > {
629
+ target_module : ast:: NodeId ,
630
+ span : Option < Span > ,
631
+ found_use : bool ,
632
+ tcx : TyCtxt < ' a , ' gcx , ' tcx >
633
+ }
634
+
635
+ impl < ' a , ' tcx , ' gcx > UsePlacementFinder < ' a , ' tcx , ' gcx > {
636
+ fn check (
637
+ tcx : TyCtxt < ' a , ' gcx , ' tcx > ,
638
+ krate : & ' tcx hir:: Crate ,
639
+ target_module : ast:: NodeId ,
640
+ ) -> ( Option < Span > , bool ) {
641
+ let mut finder = UsePlacementFinder {
642
+ target_module,
643
+ span : None ,
644
+ found_use : false ,
645
+ tcx,
646
+ } ;
647
+ hir:: intravisit:: walk_crate ( & mut finder, krate) ;
648
+ ( finder. span , finder. found_use )
649
+ }
650
+ }
651
+
652
+ impl < ' a , ' tcx , ' gcx > hir:: intravisit:: Visitor < ' tcx > for UsePlacementFinder < ' a , ' tcx , ' gcx > {
653
+ fn visit_mod (
654
+ & mut self ,
655
+ module : & ' tcx hir:: Mod ,
656
+ _: Span ,
657
+ node_id : ast:: NodeId ,
658
+ ) {
659
+ if self . span . is_some ( ) {
660
+ return ;
661
+ }
662
+ if node_id != self . target_module {
663
+ hir:: intravisit:: walk_mod ( self , module, node_id) ;
664
+ return ;
665
+ }
666
+ // find a use statement
667
+ for item_id in & module. item_ids {
668
+ let item = self . tcx . hir . expect_item ( item_id. id ) ;
669
+ match item. node {
670
+ hir:: ItemUse ( ..) => {
671
+ // don't suggest placing a use before the prelude
672
+ // import or other generated ones
673
+ if item. span . ctxt ( ) . outer ( ) . expn_info ( ) . is_none ( ) {
674
+ self . span = Some ( item. span . with_hi ( item. span . lo ( ) ) ) ;
675
+ self . found_use = true ;
676
+ return ;
677
+ }
678
+ } ,
679
+ // don't place use before extern crate
680
+ hir:: ItemExternCrate ( _) => { }
681
+ // but place them before the first other item
682
+ _ => if self . span . map_or ( true , |span| item. span < span ) {
683
+ if item. span . ctxt ( ) . outer ( ) . expn_info ( ) . is_none ( ) {
684
+ // don't insert between attributes and an item
685
+ if item. attrs . is_empty ( ) {
686
+ self . span = Some ( item. span . with_hi ( item. span . lo ( ) ) ) ;
687
+ } else {
688
+ // find the first attribute on the item
689
+ for attr in & item. attrs {
690
+ if self . span . map_or ( true , |span| attr. span < span) {
691
+ self . span = Some ( attr. span . with_hi ( attr. span . lo ( ) ) ) ;
692
+ }
693
+ }
694
+ }
695
+ }
696
+ } ,
697
+ }
698
+ }
699
+ }
700
+ fn nested_visit_map < ' this > (
701
+ & ' this mut self
702
+ ) -> hir:: intravisit:: NestedVisitorMap < ' this , ' tcx > {
703
+ hir:: intravisit:: NestedVisitorMap :: None
704
+ }
705
+ }
0 commit comments