@@ -4,11 +4,11 @@ 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;
7
+ use rustc:: session:: { Session , config:: nightly_options} ;
8
8
use syntax:: ast:: { Expr , ExprKind , Ident } ;
9
9
use syntax:: ext:: base:: MacroKind ;
10
- use syntax:: symbol:: keywords;
11
- use syntax_pos:: Span ;
10
+ use syntax:: symbol:: { Symbol , keywords} ;
11
+ use syntax_pos:: { BytePos , Span } ;
12
12
13
13
use crate :: macros:: ParentScope ;
14
14
use crate :: resolve_imports:: { ImportDirective , ImportDirectiveSubclass , ImportResolver } ;
@@ -616,10 +616,84 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
616
616
format ! ( "{} as {}" , source, target) ,
617
617
_ => format ! ( "{}" , ident) ,
618
618
} ;
619
+
620
+ // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
621
+ // intermediate segments.
622
+ let ( mut span, mut correction) = ( directive. span ,
623
+ format ! ( "{}::{}" , module_name, import) ) ;
624
+
625
+ if directive. is_nested ( ) {
626
+ span = directive. use_span ;
627
+
628
+ // Find the binding span (and any trailing commas and spaces).
629
+ // ie. `use a::b::{c, d, e};`
630
+ // ^^^
631
+ let ( found_closing_brace, binding_span) = find_span_of_binding_until_next_binding (
632
+ self . resolver . session , directive. span , directive. use_span ,
633
+ ) ;
634
+ debug ! ( "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}" ,
635
+ found_closing_brace, binding_span) ;
636
+
637
+ let mut removal_span = binding_span;
638
+ if found_closing_brace {
639
+ // If the binding span ended with a closing brace, as in the below example:
640
+ // ie. `use a::b::{c, d};`
641
+ // ^
642
+ // Then expand the span of characters to remove to include the previous
643
+ // binding's trailing comma.
644
+ // ie. `use a::b::{c, d};`
645
+ // ^^^
646
+ if let Some ( previous_span) = extend_span_to_previous_binding (
647
+ self . resolver . session , binding_span,
648
+ ) {
649
+ debug ! ( "check_for_module_export_macro: previous_span={:?}" , previous_span) ;
650
+ removal_span = removal_span. with_lo ( previous_span. lo ( ) ) ;
651
+ }
652
+ }
653
+ debug ! ( "check_for_module_export_macro: removal_span={:?}" , removal_span) ;
654
+
655
+ // Find the span after the crate name and if it has nested imports immediatately
656
+ // after the crate name already.
657
+ // ie. `use a::b::{c, d};`
658
+ // ^^^^^^^^^
659
+ // or `use a::{b, c, d}};`
660
+ // ^^^^^^^^^^^
661
+ let ( has_nested, after_crate_name) = find_span_immediately_after_crate_name (
662
+ self . resolver . session , module_name, directive. use_span ,
663
+ ) ;
664
+ debug ! ( "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}" ,
665
+ has_nested, after_crate_name) ;
666
+
667
+ let source_map = self . resolver . session . source_map ( ) ;
668
+
669
+ // Remove two bytes at the end to keep all but the `};` characters.
670
+ // ie. `{b::{c, d}, e::{f, g}};`
671
+ // ^^^^^^^^^^^^^^^^^^^^^
672
+ let end_bytes = BytePos ( if has_nested { 2 } else { 1 } ) ;
673
+ let mut remaining_span = after_crate_name. with_hi (
674
+ after_crate_name. hi ( ) - end_bytes) ;
675
+ if has_nested {
676
+ // Remove two bytes at the start to keep all but the initial `{` character.
677
+ // ie. `{b::{c, d}, e::{f, g}`
678
+ // ^^^^^^^^^^^^^^^^^^^^
679
+ remaining_span = remaining_span. with_lo ( after_crate_name. lo ( ) + BytePos ( 1 ) ) ;
680
+ }
681
+
682
+ // Calculate the number of characters into a snippet to remove the removal
683
+ // span.
684
+ let lo = removal_span. lo ( ) - remaining_span. lo ( ) ;
685
+ let hi = lo + ( removal_span. hi ( ) - removal_span. lo ( ) ) ;
686
+ if let Ok ( mut remaining) = source_map. span_to_snippet ( remaining_span) {
687
+ // Remove the original location of the binding.
688
+ remaining. replace_range ( ( lo. 0 as usize ) ..( hi. 0 as usize ) , "" ) ;
689
+ correction = format ! ( "use {}::{{{}, {}}};" , module_name, import, remaining) ;
690
+ }
691
+ }
692
+
619
693
let suggestion = Some ( (
620
- directive . span ,
694
+ span,
621
695
String :: from ( "a macro with this name exists at the root of the crate" ) ,
622
- format ! ( "{}::{}" , module_name , import ) ,
696
+ correction ,
623
697
Applicability :: MaybeIncorrect ,
624
698
) ) ;
625
699
let note = vec ! [
@@ -632,3 +706,149 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
632
706
}
633
707
}
634
708
}
709
+
710
+ /// Given a `binding_span` of a binding within a use statement:
711
+ ///
712
+ /// ```
713
+ /// use foo::{a, b, c};
714
+ /// ^
715
+ /// ```
716
+ ///
717
+ /// then return the span until the next binding or the end of the statement:
718
+ ///
719
+ /// ```
720
+ /// use foo::{a, b, c};
721
+ /// ^^^
722
+ /// ```
723
+ pub ( crate ) fn find_span_of_binding_until_next_binding (
724
+ sess : & Session ,
725
+ binding_span : Span ,
726
+ use_span : Span ,
727
+ ) -> ( bool , Span ) {
728
+ let source_map = sess. source_map ( ) ;
729
+
730
+ // Find the span of everything after the binding.
731
+ // ie. `a, e};` or `a};`
732
+ let binding_until_end = binding_span. with_hi ( use_span. hi ( ) ) ;
733
+
734
+ // Find everything after the binding but not including the binding.
735
+ // ie. `, e};` or `};`
736
+ let after_binding_until_end = binding_until_end. with_lo ( binding_span. hi ( ) ) ;
737
+
738
+ // Keep characters in the span until we encounter something that isn't a comma or
739
+ // whitespace.
740
+ // ie. `, ` or ``.
741
+ //
742
+ // Also note whether a closing brace character was encountered. If there
743
+ // was, then later go backwards to remove any trailing commas that are left.
744
+ let mut found_closing_brace = false ;
745
+ let after_binding_until_next_binding = source_map. span_take_while (
746
+ after_binding_until_end,
747
+ |& ch| {
748
+ if ch == '}' { found_closing_brace = true ; }
749
+ ch == ' ' || ch == ','
750
+ }
751
+ ) ;
752
+
753
+ // Combine the two spans.
754
+ // ie. `a, ` or `a`.
755
+ //
756
+ // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };`
757
+ let span = binding_span. with_hi ( after_binding_until_next_binding. hi ( ) ) ;
758
+
759
+ ( found_closing_brace, span)
760
+ }
761
+
762
+ /// Given a `binding_span`, return the span through to the comma or opening brace of the previous
763
+ /// binding.
764
+ ///
765
+ /// ```
766
+ /// use foo::a::{a, b, c};
767
+ /// ^^--- binding span
768
+ /// |
769
+ /// returned span
770
+ ///
771
+ /// use foo::{a, b, c};
772
+ /// --- binding span
773
+ /// ```
774
+ pub ( crate ) fn extend_span_to_previous_binding (
775
+ sess : & Session ,
776
+ binding_span : Span ,
777
+ ) -> Option < Span > {
778
+ let source_map = sess. source_map ( ) ;
779
+
780
+ // `prev_source` will contain all of the source that came before the span.
781
+ // Then split based on a command and take the first (ie. closest to our span)
782
+ // snippet. In the example, this is a space.
783
+ let prev_source = source_map. span_to_prev_source ( binding_span) . ok ( ) ?;
784
+
785
+ let prev_comma = prev_source. rsplit ( ',' ) . collect :: < Vec < _ > > ( ) ;
786
+ let prev_starting_brace = prev_source. rsplit ( '{' ) . collect :: < Vec < _ > > ( ) ;
787
+ if prev_comma. len ( ) <= 1 || prev_starting_brace. len ( ) <= 1 {
788
+ return None ;
789
+ }
790
+
791
+ let prev_comma = prev_comma. first ( ) . unwrap ( ) ;
792
+ let prev_starting_brace = prev_starting_brace. first ( ) . unwrap ( ) ;
793
+
794
+ // If the amount of source code before the comma is greater than
795
+ // the amount of source code before the starting brace then we've only
796
+ // got one item in the nested item (eg. `issue_52891::{self}`).
797
+ if prev_comma. len ( ) > prev_starting_brace. len ( ) {
798
+ return None ;
799
+ }
800
+
801
+ Some ( binding_span. with_lo ( BytePos (
802
+ // Take away the number of bytes for the characters we've found and an
803
+ // extra for the comma.
804
+ binding_span. lo ( ) . 0 - ( prev_comma. as_bytes ( ) . len ( ) as u32 ) - 1
805
+ ) ) )
806
+ }
807
+
808
+ /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if
809
+ /// it is a nested use tree.
810
+ ///
811
+ /// ```
812
+ /// use foo::a::{b, c};
813
+ /// ^^^^^^^^^^ // false
814
+ ///
815
+ /// use foo::{a, b, c};
816
+ /// ^^^^^^^^^^ // true
817
+ ///
818
+ /// use foo::{a, b::{c, d}};
819
+ /// ^^^^^^^^^^^^^^^ // true
820
+ /// ```
821
+ fn find_span_immediately_after_crate_name (
822
+ sess : & Session ,
823
+ module_name : Symbol ,
824
+ use_span : Span ,
825
+ ) -> ( bool , Span ) {
826
+ debug ! ( "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}" ,
827
+ module_name, use_span) ;
828
+ let source_map = sess. source_map ( ) ;
829
+
830
+ // Get position of the first `{` character for the use statement.
831
+ // ie. `use foo::{a, b::{c, d}};`
832
+ // ^
833
+ let pos_of_use_tree_left_bracket = source_map. span_until_char ( use_span, '{' ) . hi ( ) ;
834
+ debug ! ( "find_span_immediately_after_crate_name: pos_of_use_tree_left_bracket={:?}" ,
835
+ pos_of_use_tree_left_bracket) ;
836
+
837
+ // Calculate the expected difference between the first `{` character and the start of a
838
+ // use statement.
839
+ // ie. `use foo::{..};`
840
+ // ^^^^
841
+ // | ^^^
842
+ // 4 | ^^
843
+ // 3 |
844
+ // 2
845
+ let expected_difference = BytePos ( ( module_name. as_str ( ) . len ( ) + 4 + 2 ) as u32 ) ;
846
+ debug ! ( "find_span_immediately_after_crate_name: expected_difference={:?}" ,
847
+ expected_difference) ;
848
+ let actual_difference = pos_of_use_tree_left_bracket - use_span. lo ( ) ;
849
+ debug ! ( "find_span_immediately_after_crate_name: actual_difference={:?}" ,
850
+ actual_difference) ;
851
+
852
+ ( expected_difference == actual_difference,
853
+ use_span. with_lo ( use_span. lo ( ) + expected_difference) )
854
+ }
0 commit comments