about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_resolve/lib.rs25
-rw-r--r--src/librustc_resolve/macros.rs43
-rw-r--r--src/librustc_typeck/check/method/suggest.rs117
-rw-r--r--src/test/ui/impl-trait/no-method-suggested-traits.stderr54
-rw-r--r--src/test/ui/issue-35976.stderr5
-rw-r--r--src/test/ui/trait-method-private.stderr6
6 files changed, 207 insertions, 43 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 42c31a6e47e..2bd08d2c798 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -588,6 +588,18 @@ struct UsePlacementFinder {
     found_use: bool,
 }
 
+impl UsePlacementFinder {
+    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+        let mut finder = UsePlacementFinder {
+            target_module,
+            span: None,
+            found_use: false,
+        };
+        visit::walk_crate(&mut finder, krate);
+        (finder.span, finder.found_use)
+    }
+}
+
 impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
     fn visit_mod(
         &mut self,
@@ -1278,6 +1290,8 @@ pub struct Resolver<'a> {
     ambiguity_errors: Vec<AmbiguityError<'a>>,
     /// `use` injections are delayed for better placement and deduplication
     use_injections: Vec<UseError<'a>>,
+    /// `use` injections for proc macros wrongly imported with #[macro_use]
+    proc_mac_errors: Vec<macros::ProcMacError>,
 
     gated_errors: FxHashSet<Span>,
     disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
@@ -1486,6 +1500,7 @@ impl<'a> Resolver<'a> {
             privacy_errors: Vec::new(),
             ambiguity_errors: Vec::new(),
             use_injections: Vec::new(),
+            proc_mac_errors: Vec::new(),
             gated_errors: FxHashSet(),
             disallowed_shadowing: Vec::new(),
 
@@ -3569,6 +3584,7 @@ impl<'a> Resolver<'a> {
     fn report_errors(&mut self, krate: &Crate) {
         self.report_shadowing_errors();
         self.report_with_use_injections(krate);
+        self.report_proc_macro_import(krate);
         let mut reported_spans = FxHashSet();
 
         for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
@@ -3618,14 +3634,9 @@ impl<'a> Resolver<'a> {
 
     fn report_with_use_injections(&mut self, krate: &Crate) {
         for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) {
-            let mut finder = UsePlacementFinder {
-                target_module: node_id,
-                span: None,
-                found_use: false,
-            };
-            visit::walk_crate(&mut finder, krate);
+            let (span, found_use) = UsePlacementFinder::check(krate, node_id);
             if !candidates.is_empty() {
-                show_candidates(&mut err, finder.span, &candidates, better, finder.found_use);
+                show_candidates(&mut err, span, &candidates, better, found_use);
             }
             err.emit();
         }
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index a52e6fcad14..3d1d7c0c48a 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -83,6 +83,14 @@ pub struct LegacyBinding<'a> {
     pub span: Span,
 }
 
+pub struct ProcMacError {
+    crate_name: Symbol,
+    name: Symbol,
+    module: ast::NodeId,
+    use_span: Span,
+    warn_msg: &'static str,
+}
+
 #[derive(Copy, Clone)]
 pub enum MacroBinding<'a> {
     Legacy(&'a LegacyBinding<'a>),
@@ -779,12 +787,37 @@ impl<'a> Resolver<'a> {
             _ => return,
         };
 
-        let crate_name = self.cstore.crate_name_untracked(krate);
+        let def_id = self.current_module.normal_ancestor_id;
+        let node_id = self.definitions.as_local_node_id(def_id).unwrap();
+
+        self.proc_mac_errors.push(ProcMacError {
+            crate_name: self.cstore.crate_name_untracked(krate),
+            name,
+            module: node_id,
+            use_span,
+            warn_msg,
+        });
+    }
+
+    pub fn report_proc_macro_import(&mut self, krate: &ast::Crate) {
+        for err in self.proc_mac_errors.drain(..) {
+            let (span, found_use) = ::UsePlacementFinder::check(krate, err.module);
 
-        self.session.struct_span_err(use_span, warn_msg)
-            .help(&format!("instead, import the procedural macro like any other item: \
-                             `use {}::{};`", crate_name, name))
-            .emit();
+            if let Some(span) = span {
+                let found_use = if found_use { "" } else { "\n" };
+                self.session.struct_span_err(err.use_span, err.warn_msg)
+                    .span_suggestion(
+                        span,
+                        "instead, import the procedural macro like any other item",
+                        format!("use {}::{};{}", err.crate_name, err.name, found_use),
+                    ).emit();
+            } else {
+                self.session.struct_span_err(err.use_span, err.warn_msg)
+                    .help(&format!("instead, import the procedural macro like any other item: \
+                                    `use {}::{};`", err.crate_name, err.name))
+                    .emit();
+            }
+        }
     }
 
     fn gate_legacy_custom_derive(&mut self, name: Symbol, span: Span) {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 8613ec86b4a..50e996dd954 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -340,16 +340,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                               err: &mut DiagnosticBuilder,
                               mut msg: String,
                               candidates: Vec<DefId>) {
-        let limit = if candidates.len() == 5 { 5 } else { 4 };
-        for (i, trait_did) in candidates.iter().take(limit).enumerate() {
-            msg.push_str(&format!("\ncandidate #{}: `use {};`",
-                                    i + 1,
-                                    self.tcx.item_path_str(*trait_did)));
-        }
-        if candidates.len() > limit {
-            msg.push_str(&format!("\nand {} others", candidates.len() - limit));
+        let module_did = self.tcx.hir.get_module_parent(self.body_id);
+        let module_id = self.tcx.hir.as_local_node_id(module_did).unwrap();
+        let krate = self.tcx.hir.krate();
+        let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id);
+        if let Some(span) = span {
+            let path_strings = candidates.iter().map(|did| {
+                // produce an additional newline to separate the new use statement
+                // from the directly following item.
+                let additional_newline = if found_use {
+                    ""
+                } else {
+                    "\n"
+                };
+                format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline)
+            }).collect();
+
+            err.span_suggestions(span, &msg, path_strings);
+        } else {
+            let limit = if candidates.len() == 5 { 5 } else { 4 };
+            for (i, trait_did) in candidates.iter().take(limit).enumerate() {
+                msg.push_str(&format!("\ncandidate #{}: `use {};`",
+                                        i + 1,
+                                        self.tcx.item_path_str(*trait_did)));
+            }
+            if candidates.len() > limit {
+                msg.push_str(&format!("\nand {} others", candidates.len() - limit));
+            }
+            err.note(&msg[..]);
         }
-        err.note(&msg[..]);
     }
 
     fn suggest_valid_traits(&self,
@@ -604,3 +623,83 @@ impl<'a> Iterator for AllTraits<'a> {
         })
     }
 }
+
+
+struct UsePlacementFinder<'a, 'tcx: 'a, 'gcx: 'tcx> {
+    target_module: ast::NodeId,
+    span: Option<Span>,
+    found_use: bool,
+    tcx: TyCtxt<'a, 'gcx, 'tcx>
+}
+
+impl<'a, 'tcx, 'gcx> UsePlacementFinder<'a, 'tcx, 'gcx> {
+    fn check(
+        tcx: TyCtxt<'a, 'gcx, 'tcx>,
+        krate: &'tcx hir::Crate,
+        target_module: ast::NodeId,
+    ) -> (Option<Span>, bool) {
+        let mut finder = UsePlacementFinder {
+            target_module,
+            span: None,
+            found_use: false,
+            tcx,
+        };
+        hir::intravisit::walk_crate(&mut finder, krate);
+        (finder.span, finder.found_use)
+    }
+}
+
+impl<'a, 'tcx, 'gcx> hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'a, 'tcx, 'gcx> {
+    fn visit_mod(
+        &mut self,
+        module: &'tcx hir::Mod,
+        _: Span,
+        node_id: ast::NodeId,
+    ) {
+        if self.span.is_some() {
+            return;
+        }
+        if node_id != self.target_module {
+            hir::intravisit::walk_mod(self, module, node_id);
+            return;
+        }
+        // find a use statement
+        for item_id in &module.item_ids {
+            let item = self.tcx.hir.expect_item(item_id.id);
+            match item.node {
+                hir::ItemUse(..) => {
+                    // don't suggest placing a use before the prelude
+                    // import or other generated ones
+                    if item.span.ctxt().outer().expn_info().is_none() {
+                        self.span = Some(item.span.with_hi(item.span.lo()));
+                        self.found_use = true;
+                        return;
+                    }
+                },
+                // don't place use before extern crate
+                hir::ItemExternCrate(_) => {}
+                // but place them before the first other item
+                _ => if self.span.map_or(true, |span| item.span < span ) {
+                    if item.span.ctxt().outer().expn_info().is_none() {
+                        // don't insert between attributes and an item
+                        if item.attrs.is_empty() {
+                            self.span = Some(item.span.with_hi(item.span.lo()));
+                        } else {
+                            // find the first attribute on the item
+                            for attr in &item.attrs {
+                                if self.span.map_or(true, |span| attr.span < span) {
+                                    self.span = Some(attr.span.with_hi(attr.span.lo()));
+                                }
+                            }
+                        }
+                    }
+                },
+            }
+        }
+    }
+    fn nested_visit_map<'this>(
+        &'this mut self
+    ) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
+        hir::intravisit::NestedVisitorMap::None
+    }
+}
diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr
index 23f115858cd..793e1c66683 100644
--- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr
+++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr
@@ -5,11 +5,16 @@ error[E0599]: no method named `method` found for type `u32` in the current scope
    |          ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
-           candidate #1: `use foo::Bar;`
-           candidate #2: `use no_method_suggested_traits::foo::PubPub;`
-           candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
-           candidate #4: `use no_method_suggested_traits::Reexported;`
+help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
+   |
+14 | use foo::Bar;
+   |
+14 | use no_method_suggested_traits::foo::PubPub;
+   |
+14 | use no_method_suggested_traits::qux::PrivPub;
+   |
+14 | use no_method_suggested_traits::Reexported;
+   |
 
 error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope
   --> $DIR/no-method-suggested-traits.rs:38:44
@@ -18,11 +23,16 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box
    |                                            ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
-           candidate #1: `use foo::Bar;`
-           candidate #2: `use no_method_suggested_traits::foo::PubPub;`
-           candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
-           candidate #4: `use no_method_suggested_traits::Reexported;`
+help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
+   |
+14 | use foo::Bar;
+   |
+14 | use no_method_suggested_traits::foo::PubPub;
+   |
+14 | use no_method_suggested_traits::qux::PrivPub;
+   |
+14 | use no_method_suggested_traits::Reexported;
+   |
 
 error[E0599]: no method named `method` found for type `char` in the current scope
   --> $DIR/no-method-suggested-traits.rs:44:9
@@ -31,8 +41,10 @@ error[E0599]: no method named `method` found for type `char` in the current scop
    |         ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
-           candidate #1: `use foo::Bar;`
+help: the following trait is implemented but not in scope, perhaps add a `use` for it:
+   |
+14 | use foo::Bar;
+   |
 
 error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
   --> $DIR/no-method-suggested-traits.rs:48:43
@@ -41,8 +53,10 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box
    |                                           ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
-           candidate #1: `use foo::Bar;`
+help: the following trait is implemented but not in scope, perhaps add a `use` for it:
+   |
+14 | use foo::Bar;
+   |
 
 error[E0599]: no method named `method` found for type `i32` in the current scope
   --> $DIR/no-method-suggested-traits.rs:53:10
@@ -51,8 +65,10 @@ error[E0599]: no method named `method` found for type `i32` in the current scope
    |          ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
-           candidate #1: `use no_method_suggested_traits::foo::PubPub;`
+help: the following trait is implemented but not in scope, perhaps add a `use` for it:
+   |
+14 | use no_method_suggested_traits::foo::PubPub;
+   |
 
 error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope
   --> $DIR/no-method-suggested-traits.rs:57:44
@@ -61,8 +77,10 @@ error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::box
    |                                            ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
-           candidate #1: `use no_method_suggested_traits::foo::PubPub;`
+help: the following trait is implemented but not in scope, perhaps add a `use` for it:
+   |
+14 | use no_method_suggested_traits::foo::PubPub;
+   |
 
 error[E0599]: no method named `method` found for type `Foo` in the current scope
   --> $DIR/no-method-suggested-traits.rs:62:9
diff --git a/src/test/ui/issue-35976.stderr b/src/test/ui/issue-35976.stderr
index 9fb67449734..146d0ff72d8 100644
--- a/src/test/ui/issue-35976.stderr
+++ b/src/test/ui/issue-35976.stderr
@@ -3,9 +3,10 @@ error: the `wait` method cannot be invoked on a trait object
    |
 24 |     arg.wait();
    |         ^^^^
+help: another candidate was found in the following trait, perhaps add a `use` for it:
+   |
+11 | use private::Future;
    |
-   = note: another candidate was found in the following trait, perhaps add a `use` for it:
-           candidate #1: `use private::Future;`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/trait-method-private.stderr b/src/test/ui/trait-method-private.stderr
index c7a7b689edc..549c84b9b03 100644
--- a/src/test/ui/trait-method-private.stderr
+++ b/src/test/ui/trait-method-private.stderr
@@ -5,8 +5,10 @@ error[E0624]: method `method` is private
    |         ^^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-   = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
-           candidate #1: `use inner::Bar;`
+help: the following trait is implemented but not in scope, perhaps add a `use` for it:
+   |
+11 | use inner::Bar;
+   |
 
 error: aborting due to previous error