@@ -4,11 +4,11 @@ use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
44use log:: debug;
55use rustc:: hir:: def:: { Def , CtorKind , Namespace :: * } ;
66use rustc:: hir:: def_id:: { CRATE_DEF_INDEX , DefId } ;
7- use rustc:: session:: config:: nightly_options;
7+ use rustc:: session:: { Session , config:: nightly_options} ;
88use syntax:: ast:: { Expr , ExprKind , Ident } ;
99use 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 } ;
1212
1313use crate :: macros:: ParentScope ;
1414use crate :: resolve_imports:: { ImportDirective , ImportDirectiveSubclass , ImportResolver } ;
@@ -616,10 +616,84 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
616616 format ! ( "{} as {}" , source, target) ,
617617 _ => format ! ( "{}" , ident) ,
618618 } ;
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+
619693 let suggestion = Some ( (
620- directive . span ,
694+ span,
621695 String :: from ( "a macro with this name exists at the root of the crate" ) ,
622- format ! ( "{}::{}" , module_name , import ) ,
696+ correction ,
623697 Applicability :: MaybeIncorrect ,
624698 ) ) ;
625699 let note = vec ! [
@@ -632,3 +706,149 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
632706 }
633707 }
634708}
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