@@ -4,16 +4,17 @@ 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;
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 } ;
1112
1213use crate :: macros:: ParentScope ;
13- use crate :: resolve_imports:: ImportResolver ;
14+ use crate :: resolve_imports:: { ImportDirective , ImportDirectiveSubclass , ImportResolver } ;
1415use crate :: { import_candidate_to_enum_paths, is_self_type, is_self_value, path_names_to_string} ;
1516use crate :: { AssocSuggestion , CrateLint , ImportSuggestion , ModuleOrUniformRoot , PathResult ,
16- PathSource , Resolver , Segment } ;
17+ PathSource , Resolver , Segment , Suggestion } ;
1718
1819impl < ' a > Resolver < ' a > {
1920 /// Handles error reporting for `smart_resolve_path_fragment` function.
@@ -428,7 +429,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
428429 span : Span ,
429430 mut path : Vec < Segment > ,
430431 parent_scope : & ParentScope < ' b > ,
431- ) -> Option < ( Vec < Segment > , Option < String > ) > {
432+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
432433 debug ! ( "make_path_suggestion: span={:?} path={:?}" , span, path) ;
433434
434435 match ( path. get ( 0 ) , path. get ( 1 ) ) {
@@ -463,13 +464,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
463464 span : Span ,
464465 mut path : Vec < Segment > ,
465466 parent_scope : & ParentScope < ' b > ,
466- ) -> Option < ( Vec < Segment > , Option < String > ) > {
467+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
467468 // Replace first ident with `self` and check if that is valid.
468469 path[ 0 ] . ident . name = keywords:: SelfLower . name ( ) ;
469470 let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
470471 debug ! ( "make_missing_self_suggestion: path={:?} result={:?}" , path, result) ;
471472 if let PathResult :: Module ( ..) = result {
472- Some ( ( path, None ) )
473+ Some ( ( path, Vec :: new ( ) ) )
473474 } else {
474475 None
475476 }
@@ -487,19 +488,19 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
487488 span : Span ,
488489 mut path : Vec < Segment > ,
489490 parent_scope : & ParentScope < ' b > ,
490- ) -> Option < ( Vec < Segment > , Option < String > ) > {
491+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
491492 // Replace first ident with `crate` and check if that is valid.
492493 path[ 0 ] . ident . name = keywords:: Crate . name ( ) ;
493494 let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
494495 debug ! ( "make_missing_crate_suggestion: path={:?} result={:?}" , path, result) ;
495496 if let PathResult :: Module ( ..) = result {
496497 Some ( (
497498 path,
498- Some (
499+ vec ! [
499500 "`use` statements changed in Rust 2018; read more at \
500501 <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
501502 clarity.html>". to_string( )
502- ) ,
503+ ] ,
503504 ) )
504505 } else {
505506 None
@@ -518,13 +519,13 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
518519 span : Span ,
519520 mut path : Vec < Segment > ,
520521 parent_scope : & ParentScope < ' b > ,
521- ) -> Option < ( Vec < Segment > , Option < String > ) > {
522+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
522523 // Replace first ident with `crate` and check if that is valid.
523524 path[ 0 ] . ident . name = keywords:: Super . name ( ) ;
524525 let result = self . resolve_path ( & path, None , parent_scope, false , span, CrateLint :: No ) ;
525526 debug ! ( "make_missing_super_suggestion: path={:?} result={:?}" , path, result) ;
526527 if let PathResult :: Module ( ..) = result {
527- Some ( ( path, None ) )
528+ Some ( ( path, Vec :: new ( ) ) )
528529 } else {
529530 None
530531 }
@@ -545,7 +546,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
545546 span : Span ,
546547 mut path : Vec < Segment > ,
547548 parent_scope : & ParentScope < ' b > ,
548- ) -> Option < ( Vec < Segment > , Option < String > ) > {
549+ ) -> Option < ( Vec < Segment > , Vec < String > ) > {
549550 if path[ 1 ] . ident . span . rust_2015 ( ) {
550551 return None ;
551552 }
@@ -564,10 +565,290 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
564565 debug ! ( "make_external_crate_suggestion: name={:?} path={:?} result={:?}" ,
565566 name, path, result) ;
566567 if let PathResult :: Module ( ..) = result {
567- return Some ( ( path, None ) ) ;
568+ return Some ( ( path, Vec :: new ( ) ) ) ;
568569 }
569570 }
570571
571572 None
572573 }
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)
573854}
0 commit comments