@@ -4,16 +4,17 @@ use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
4
4
use log:: debug;
5
5
use rustc:: hir:: def:: { Def , CtorKind , Namespace :: * } ;
6
6
use rustc:: hir:: def_id:: { CRATE_DEF_INDEX , DefId } ;
7
- use rustc:: session:: config:: nightly_options;
8
- use syntax:: ast:: { Expr , ExprKind } ;
9
- use syntax:: symbol:: keywords;
10
- use syntax_pos:: Span ;
7
+ use rustc:: session:: { Session , config:: nightly_options} ;
8
+ use syntax:: ast:: { Expr , ExprKind , Ident } ;
9
+ use syntax:: ext:: base:: MacroKind ;
10
+ use syntax:: symbol:: { Symbol , keywords} ;
11
+ use syntax_pos:: { BytePos , Span } ;
11
12
12
13
use crate :: macros:: ParentScope ;
13
- use crate :: resolve_imports:: ImportResolver ;
14
+ use crate :: resolve_imports:: { ImportDirective , ImportDirectiveSubclass , ImportResolver } ;
14
15
use crate :: { import_candidate_to_enum_paths, is_self_type, is_self_value, path_names_to_string} ;
15
16
use crate :: { AssocSuggestion , CrateLint , ImportSuggestion , ModuleOrUniformRoot , PathResult ,
16
- PathSource , Resolver , Segment } ;
17
+ PathSource , Resolver , Segment , Suggestion } ;
17
18
18
19
impl < ' a > Resolver < ' a > {
19
20
/// Handles error reporting for `smart_resolve_path_fragment` function.
@@ -428,7 +429,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
428
429
span : Span ,
429
430
mut path : Vec < Segment > ,
430
431
parent_scope : & ParentScope < ' b > ,
431
- ) -> Option < ( Vec < Segment > , Option < String > ) > {
432
+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
432
433
debug ! ( "make_path_suggestion: span={:?} path={:?}" , span, path) ;
433
434
434
435
match ( path. get ( 0 ) , path. get ( 1 ) ) {
@@ -463,13 +464,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
463
464
span : Span ,
464
465
mut path : Vec < Segment > ,
465
466
parent_scope : & ParentScope < ' b > ,
466
- ) -> Option < ( Vec < Segment > , Option < String > ) > {
467
+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
467
468
// Replace first ident with `self` and check if that is valid.
468
469
path[ 0 ] . ident . name = keywords:: SelfLower . name ( ) ;
469
470
let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
470
471
debug ! ( "make_missing_self_suggestion: path={:?} result={:?}" , path, result) ;
471
472
if let PathResult :: Module ( ..) = result {
472
- Some ( ( path, None ) )
473
+ Some ( ( path, Vec :: new ( ) ) )
473
474
} else {
474
475
None
475
476
}
@@ -487,19 +488,19 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
487
488
span : Span ,
488
489
mut path : Vec < Segment > ,
489
490
parent_scope : & ParentScope < ' b > ,
490
- ) -> Option < ( Vec < Segment > , Option < String > ) > {
491
+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
491
492
// Replace first ident with `crate` and check if that is valid.
492
493
path[ 0 ] . ident . name = keywords:: Crate . name ( ) ;
493
494
let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
494
495
debug ! ( "make_missing_crate_suggestion: path={:?} result={:?}" , path, result) ;
495
496
if let PathResult :: Module ( ..) = result {
496
497
Some ( (
497
498
path,
498
- Some (
499
+ vec ! [
499
500
"`use` statements changed in Rust 2018; read more at \
500
501
<https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
501
502
clarity.html>". to_string( )
502
- ) ,
503
+ ] ,
503
504
) )
504
505
} else {
505
506
None
@@ -518,13 +519,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
518
519
span : Span ,
519
520
mut path : Vec < Segment > ,
520
521
parent_scope : & ParentScope < ' b > ,
521
- ) -> Option < ( Vec < Segment > , Option < String > ) > {
522
+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
522
523
// Replace first ident with `crate` and check if that is valid.
523
524
path[ 0 ] . ident . name = keywords:: Super . name ( ) ;
524
525
let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
525
526
debug ! ( "make_missing_super_suggestion: path={:?} result={:?}" , path, result) ;
526
527
if let PathResult :: Module ( ..) = result {
527
- Some ( ( path, None ) )
528
+ Some ( ( path, Vec :: new ( ) ) )
528
529
} else {
529
530
None
530
531
}
@@ -545,7 +546,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
545
546
span : Span ,
546
547
mut path : Vec < Segment > ,
547
548
parent_scope : & ParentScope < ' b > ,
548
- ) -> Option < ( Vec < Segment > , Option < String > ) > {
549
+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
549
550
if path[ 1 ] . ident . span . rust_2015 ( ) {
550
551
return None ;
551
552
}
@@ -564,10 +565,290 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
564
565
debug ! ( "make_external_crate_suggestion: name={:?} path={:?} result={:?}" ,
565
566
name, path, result) ;
566
567
if let PathResult :: Module ( ..) = result {
567
- return Some ( ( path, None ) ) ;
568
+ return Some ( ( path, Vec :: new ( ) ) ) ;
568
569
}
569
570
}
570
571
571
572
None
572
573
}
574
+
575
+ /// Suggests importing a macro from the root of the crate rather than a module within
576
+ /// the crate.
577
+ ///
578
+ /// ```
579
+ /// help: a macro with this name exists at the root of the crate
580
+ /// |
581
+ /// LL | use issue_59764::makro;
582
+ /// | ^^^^^^^^^^^^^^^^^^
583
+ /// |
584
+ /// = note: this could be because a macro annotated with `#[macro_export]` will be exported
585
+ /// at the root of the crate instead of the module where it is defined
586
+ /// ```
587
+ pub ( crate ) fn check_for_module_export_macro (
588
+ & self ,
589
+ directive : & ' b ImportDirective < ' b > ,
590
+ module : ModuleOrUniformRoot < ' b > ,
591
+ ident : Ident ,
592
+ ) -> Option < ( Option < Suggestion > , Vec < String > ) > {
593
+ let mut crate_module = if let ModuleOrUniformRoot :: Module ( module) = module {
594
+ module
595
+ } else {
596
+ return None ;
597
+ } ;
598
+
599
+ while let Some ( parent) = crate_module. parent {
600
+ crate_module = parent;
601
+ }
602
+
603
+ if ModuleOrUniformRoot :: same_def ( ModuleOrUniformRoot :: Module ( crate_module) , module) {
604
+ // Don't make a suggestion if the import was already from the root of the
605
+ // crate.
606
+ return None ;
607
+ }
608
+
609
+ let resolutions = crate_module. resolutions . borrow ( ) ;
610
+ let resolution = resolutions. get ( & ( ident, MacroNS ) ) ?;
611
+ let binding = resolution. borrow ( ) . binding ( ) ?;
612
+ if let Def :: Macro ( _, MacroKind :: Bang ) = binding. def ( ) {
613
+ let module_name = crate_module. kind . name ( ) . unwrap ( ) ;
614
+ let import = match directive. subclass {
615
+ ImportDirectiveSubclass :: SingleImport { source, target, .. } if source != target =>
616
+ format ! ( "{} as {}" , source, target) ,
617
+ _ => format ! ( "{}" , ident) ,
618
+ } ;
619
+
620
+ let mut corrections: Vec < ( Span , String ) > = Vec :: new ( ) ;
621
+ if !directive. is_nested ( ) {
622
+ // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
623
+ // intermediate segments.
624
+ corrections. push ( ( directive. span , format ! ( "{}::{}" , module_name, import) ) ) ;
625
+ } else {
626
+ // Find the binding span (and any trailing commas and spaces).
627
+ // ie. `use a::b::{c, d, e};`
628
+ // ^^^
629
+ let ( found_closing_brace, binding_span) = find_span_of_binding_until_next_binding (
630
+ self . resolver . session , directive. span , directive. use_span ,
631
+ ) ;
632
+ debug ! ( "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}" ,
633
+ found_closing_brace, binding_span) ;
634
+
635
+ let mut removal_span = binding_span;
636
+ if found_closing_brace {
637
+ // If the binding span ended with a closing brace, as in the below example:
638
+ // ie. `use a::b::{c, d};`
639
+ // ^
640
+ // Then expand the span of characters to remove to include the previous
641
+ // binding's trailing comma.
642
+ // ie. `use a::b::{c, d};`
643
+ // ^^^
644
+ if let Some ( previous_span) = extend_span_to_previous_binding (
645
+ self . resolver . session , binding_span,
646
+ ) {
647
+ debug ! ( "check_for_module_export_macro: previous_span={:?}" , previous_span) ;
648
+ removal_span = removal_span. with_lo ( previous_span. lo ( ) ) ;
649
+ }
650
+ }
651
+ debug ! ( "check_for_module_export_macro: removal_span={:?}" , removal_span) ;
652
+
653
+ // Remove the `removal_span`.
654
+ corrections. push ( ( removal_span, "" . to_string ( ) ) ) ;
655
+
656
+ // Find the span after the crate name and if it has nested imports immediatately
657
+ // after the crate name already.
658
+ // ie. `use a::b::{c, d};`
659
+ // ^^^^^^^^^
660
+ // or `use a::{b, c, d}};`
661
+ // ^^^^^^^^^^^
662
+ let ( has_nested, after_crate_name) = find_span_immediately_after_crate_name (
663
+ self . resolver . session , module_name, directive. use_span ,
664
+ ) ;
665
+ debug ! ( "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}" ,
666
+ has_nested, after_crate_name) ;
667
+
668
+ let source_map = self . resolver . session . source_map ( ) ;
669
+
670
+ // Add the import to the start, with a `{` if required.
671
+ let start_point = source_map. start_point ( after_crate_name) ;
672
+ if let Ok ( start_snippet) = source_map. span_to_snippet ( start_point) {
673
+ corrections. push ( (
674
+ start_point,
675
+ if has_nested {
676
+ // In this case, `start_snippet` must equal '{'.
677
+ format ! ( "{}{}, " , start_snippet, import)
678
+ } else {
679
+ // In this case, add a `{`, then the moved import, then whatever
680
+ // was there before.
681
+ format ! ( "{{{}, {}" , import, start_snippet)
682
+ }
683
+ ) ) ;
684
+ }
685
+
686
+ // Add a `};` to the end if nested, matching the `{` added at the start.
687
+ if !has_nested {
688
+ corrections. push ( ( source_map. end_point ( after_crate_name) ,
689
+ "};" . to_string ( ) ) ) ;
690
+ }
691
+ }
692
+
693
+ let suggestion = Some ( (
694
+ corrections,
695
+ String :: from ( "a macro with this name exists at the root of the crate" ) ,
696
+ Applicability :: MaybeIncorrect ,
697
+ ) ) ;
698
+ let note = vec ! [
699
+ "this could be because a macro annotated with `#[macro_export]` will be exported \
700
+ at the root of the crate instead of the module where it is defined". to_string( ) ,
701
+ ] ;
702
+ Some ( ( suggestion, note) )
703
+ } else {
704
+ None
705
+ }
706
+ }
707
+ }
708
+
709
+ /// Given a `binding_span` of a binding within a use statement:
710
+ ///
711
+ /// ```
712
+ /// use foo::{a, b, c};
713
+ /// ^
714
+ /// ```
715
+ ///
716
+ /// then return the span until the next binding or the end of the statement:
717
+ ///
718
+ /// ```
719
+ /// use foo::{a, b, c};
720
+ /// ^^^
721
+ /// ```
722
+ pub ( crate ) fn find_span_of_binding_until_next_binding (
723
+ sess : & Session ,
724
+ binding_span : Span ,
725
+ use_span : Span ,
726
+ ) -> ( bool , Span ) {
727
+ let source_map = sess. source_map ( ) ;
728
+
729
+ // Find the span of everything after the binding.
730
+ // ie. `a, e};` or `a};`
731
+ let binding_until_end = binding_span. with_hi ( use_span. hi ( ) ) ;
732
+
733
+ // Find everything after the binding but not including the binding.
734
+ // ie. `, e};` or `};`
735
+ let after_binding_until_end = binding_until_end. with_lo ( binding_span. hi ( ) ) ;
736
+
737
+ // Keep characters in the span until we encounter something that isn't a comma or
738
+ // whitespace.
739
+ // ie. `, ` or ``.
740
+ //
741
+ // Also note whether a closing brace character was encountered. If there
742
+ // was, then later go backwards to remove any trailing commas that are left.
743
+ let mut found_closing_brace = false ;
744
+ let after_binding_until_next_binding = source_map. span_take_while (
745
+ after_binding_until_end,
746
+ |& ch| {
747
+ if ch == '}' { found_closing_brace = true ; }
748
+ ch == ' ' || ch == ','
749
+ }
750
+ ) ;
751
+
752
+ // Combine the two spans.
753
+ // ie. `a, ` or `a`.
754
+ //
755
+ // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };`
756
+ let span = binding_span. with_hi ( after_binding_until_next_binding. hi ( ) ) ;
757
+
758
+ ( found_closing_brace, span)
759
+ }
760
+
761
+ /// Given a `binding_span`, return the span through to the comma or opening brace of the previous
762
+ /// binding.
763
+ ///
764
+ /// ```
765
+ /// use foo::a::{a, b, c};
766
+ /// ^^--- binding span
767
+ /// |
768
+ /// returned span
769
+ ///
770
+ /// use foo::{a, b, c};
771
+ /// --- binding span
772
+ /// ```
773
+ pub ( crate ) fn extend_span_to_previous_binding (
774
+ sess : & Session ,
775
+ binding_span : Span ,
776
+ ) -> Option < Span > {
777
+ let source_map = sess. source_map ( ) ;
778
+
779
+ // `prev_source` will contain all of the source that came before the span.
780
+ // Then split based on a command and take the first (ie. closest to our span)
781
+ // snippet. In the example, this is a space.
782
+ let prev_source = source_map. span_to_prev_source ( binding_span) . ok ( ) ?;
783
+
784
+ let prev_comma = prev_source. rsplit ( ',' ) . collect :: < Vec < _ > > ( ) ;
785
+ let prev_starting_brace = prev_source. rsplit ( '{' ) . collect :: < Vec < _ > > ( ) ;
786
+ if prev_comma. len ( ) <= 1 || prev_starting_brace. len ( ) <= 1 {
787
+ return None ;
788
+ }
789
+
790
+ let prev_comma = prev_comma. first ( ) . unwrap ( ) ;
791
+ let prev_starting_brace = prev_starting_brace. first ( ) . unwrap ( ) ;
792
+
793
+ // If the amount of source code before the comma is greater than
794
+ // the amount of source code before the starting brace then we've only
795
+ // got one item in the nested item (eg. `issue_52891::{self}`).
796
+ if prev_comma. len ( ) > prev_starting_brace. len ( ) {
797
+ return None ;
798
+ }
799
+
800
+ Some ( binding_span. with_lo ( BytePos (
801
+ // Take away the number of bytes for the characters we've found and an
802
+ // extra for the comma.
803
+ binding_span. lo ( ) . 0 - ( prev_comma. as_bytes ( ) . len ( ) as u32 ) - 1
804
+ ) ) )
805
+ }
806
+
807
+ /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if
808
+ /// it is a nested use tree.
809
+ ///
810
+ /// ```
811
+ /// use foo::a::{b, c};
812
+ /// ^^^^^^^^^^ // false
813
+ ///
814
+ /// use foo::{a, b, c};
815
+ /// ^^^^^^^^^^ // true
816
+ ///
817
+ /// use foo::{a, b::{c, d}};
818
+ /// ^^^^^^^^^^^^^^^ // true
819
+ /// ```
820
+ fn find_span_immediately_after_crate_name (
821
+ sess : & Session ,
822
+ module_name : Symbol ,
823
+ use_span : Span ,
824
+ ) -> ( bool , Span ) {
825
+ debug ! ( "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}" ,
826
+ module_name, use_span) ;
827
+ let source_map = sess. source_map ( ) ;
828
+
829
+ // Using `use issue_59764::foo::{baz, makro};` as an example throughout..
830
+ let mut num_colons = 0 ;
831
+ // Find second colon.. `use issue_59764:`
832
+ let until_second_colon = source_map. span_take_while ( use_span, |c| {
833
+ if * c == ':' { num_colons += 1 ; }
834
+ match c {
835
+ ':' if num_colons == 2 => false ,
836
+ _ => true ,
837
+ }
838
+ } ) ;
839
+ // Find everything after the second colon.. `foo::{baz, makro};`
840
+ let from_second_colon = use_span. with_lo ( until_second_colon. hi ( ) + BytePos ( 1 ) ) ;
841
+
842
+ let mut found_a_non_whitespace_character = false ;
843
+ // Find the first non-whitespace character in `from_second_colon`.. `f`
844
+ let after_second_colon = source_map. span_take_while ( from_second_colon, |c| {
845
+ if found_a_non_whitespace_character { return false ; }
846
+ if !c. is_whitespace ( ) { found_a_non_whitespace_character = true ; }
847
+ true
848
+ } ) ;
849
+
850
+ // Find the first `{` in from_second_colon.. `foo::{`
851
+ let next_left_bracket = source_map. span_through_char ( from_second_colon, '{' ) ;
852
+
853
+ ( next_left_bracket == after_second_colon, from_second_colon)
573
854
}
0 commit comments