]> git.lizzy.rs Git - rust.git/commitdiff
Improve placement of `use` suggestions
authorOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Thu, 17 Aug 2017 09:03:59 +0000 (11:03 +0200)
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Thu, 17 Aug 2017 16:16:15 +0000 (18:16 +0200)
src/librustc_resolve/lib.rs
src/test/ui/resolve/enums-are-namespaced-xc.stderr
src/test/ui/resolve/issue-21221-3.stderr
src/test/ui/resolve/issue-21221-4.stderr
src/test/ui/resolve/issue-3907.stderr
src/test/ui/resolve/privacy-struct-ctor.stderr
src/test/ui/resolve/use_suggestion_placement.rs [new file with mode: 0644]
src/test/ui/resolve/use_suggestion_placement.stderr [new file with mode: 0644]
src/test/ui/span/visibility-ty-params.stderr

index 2502f04ee6aef9594a8b00ecbed4fc2ff0365320..500639dfde69962ff5d069b61d32c711f4b7fd42 100644 (file)
@@ -581,6 +581,53 @@ fn index_mut(&mut self, ns: Namespace) -> &mut T {
     }
 }
 
+struct UsePlacementFinder {
+    target_module: NodeId,
+    span: Option<Span>,
+}
+
+impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
+    fn visit_mod(
+        &mut self,
+        module: &'tcx ast::Mod,
+        _: Span,
+        _: &[ast::Attribute],
+        node_id: NodeId,
+    ) {
+        if self.span.is_some() {
+            return;
+        }
+        if node_id != self.target_module {
+            visit::walk_mod(self, module);
+            return;
+        }
+        // find a use statement
+        for item in &module.items {
+            match item.node {
+                ItemKind::Use(..) => {
+                    // don't suggest placing a use before the prelude
+                    // import or other generated ones
+                    if item.span == DUMMY_SP {
+                        let mut span = item.span;
+                        span.hi = span.lo;
+                        self.span = Some(span);
+                        return;
+                    }
+                },
+                // don't place use before extern crate
+                ItemKind::ExternCrate(_) => {}
+                // but place them before the first other item
+                _ => if self.span.map_or(true, |span| item.span < span ) {
+                    let mut span = item.span;
+                    span.hi = span.lo;
+                    self.span = Some(span);
+                },
+            }
+        }
+        assert!(self.span.is_some(), "a file can't have no items and emit suggestions");
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
     fn visit_item(&mut self, item: &'tcx Item) {
         self.resolve_item(item);
@@ -990,6 +1037,16 @@ enum NameBindingKind<'a> {
 
 struct PrivacyError<'a>(Span, Name, &'a NameBinding<'a>);
 
+struct UseError<'a> {
+    err: DiagnosticBuilder<'a>,
+    /// Attach `use` statements for these candidates
+    candidates: Vec<ImportSuggestion>,
+    /// The node id of the module to place the use statements in
+    node_id: NodeId,
+    /// Whether the diagnostic should state that it's "better"
+    better: bool,
+}
+
 struct AmbiguityError<'a> {
     span: Span,
     name: Name,
@@ -1190,15 +1247,20 @@ pub struct Resolver<'a> {
     extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
 
     pub make_glob_map: bool,
-    // Maps imports to the names of items actually imported (this actually maps
-    // all imports, but only glob imports are actually interesting).
+    /// Maps imports to the names of items actually imported (this actually maps
+    /// all imports, but only glob imports are actually interesting).
     pub glob_map: GlobMap,
 
     used_imports: FxHashSet<(NodeId, Namespace)>,
     pub maybe_unused_trait_imports: NodeSet,
 
+    /// privacy errors are delayed until the end in order to deduplicate them
     privacy_errors: Vec<PrivacyError<'a>>,
+    /// ambiguity errors are delayed for deduplication
     ambiguity_errors: Vec<AmbiguityError<'a>>,
+    /// `use` injections are delayed for better placement and deduplication
+    use_injections: Vec<UseError<'a>>,
+
     gated_errors: FxHashSet<Span>,
     disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
 
@@ -1401,6 +1463,7 @@ pub fn new(session: &'a Session,
 
             privacy_errors: Vec::new(),
             ambiguity_errors: Vec::new(),
+            use_injections: Vec::new(),
             gated_errors: FxHashSet(),
             disallowed_shadowing: Vec::new(),
 
@@ -1465,10 +1528,11 @@ pub fn resolve_crate(&mut self, krate: &Crate) {
         ImportResolver { resolver: self }.finalize_imports();
         self.current_module = self.graph_root;
         self.finalize_current_module_macro_resolutions();
+
         visit::walk_crate(self, krate);
 
         check_unused::check_crate(self, krate);
-        self.report_errors();
+        self.report_errors(krate);
         self.crate_loader.postprocess(krate);
     }
 
@@ -2413,25 +2477,20 @@ fn smart_resolve_path_fragment(&mut self,
                 __diagnostic_used!(E0411);
                 err.code("E0411".into());
                 err.span_label(span, "`Self` is only available in traits and impls");
-                return err;
+                return (err, Vec::new());
             }
             if is_self_value(path, ns) {
                 __diagnostic_used!(E0424);
                 err.code("E0424".into());
                 err.span_label(span, format!("`self` value is only available in \
                                                methods with `self` parameter"));
-                return err;
+                return (err, Vec::new());
             }
 
             // Try to lookup the name in more relaxed fashion for better error reporting.
             let ident = *path.last().unwrap();
             let candidates = this.lookup_import_candidates(ident.node.name, ns, is_expected);
-            if !candidates.is_empty() {
-                let mut module_span = this.current_module.span;
-                module_span.hi = module_span.lo;
-                // Report import candidates as help and proceed searching for labels.
-                show_candidates(&mut err, module_span, &candidates, def.is_some());
-            } else if is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
+            if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
                 let enum_candidates =
                     this.lookup_import_candidates(ident.node.name, ns, is_enum_variant);
                 let mut enum_candidates = enum_candidates.iter()
@@ -2471,7 +2530,7 @@ fn smart_resolve_path_fragment(&mut self,
                                                 format!("Self::{}", path_str));
                         }
                     }
-                    return err;
+                    return (err, candidates);
                 }
             }
 
@@ -2488,22 +2547,22 @@ fn smart_resolve_path_fragment(&mut self,
                 match (def, source) {
                     (Def::Macro(..), _) => {
                         err.span_label(span, format!("did you mean `{}!(...)`?", path_str));
-                        return err;
+                        return (err, candidates);
                     }
                     (Def::TyAlias(..), PathSource::Trait) => {
                         err.span_label(span, "type aliases cannot be used for traits");
-                        return err;
+                        return (err, candidates);
                     }
                     (Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
                         ExprKind::Field(_, ident) => {
                             err.span_label(parent.span, format!("did you mean `{}::{}`?",
                                                                  path_str, ident.node));
-                            return err;
+                            return (err, candidates);
                         }
                         ExprKind::MethodCall(ref segment, ..) => {
                             err.span_label(parent.span, format!("did you mean `{}::{}(...)`?",
                                                                  path_str, segment.identifier));
-                            return err;
+                            return (err, candidates);
                         }
                         _ => {}
                     },
@@ -2519,7 +2578,7 @@ fn smart_resolve_path_fragment(&mut self,
                         }
                         err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
                                                        path_str));
-                        return err;
+                        return (err, candidates);
                     }
                     _ => {}
                 }
@@ -2530,10 +2589,14 @@ fn smart_resolve_path_fragment(&mut self,
                 err.span_label(base_span, fallback_label);
                 this.type_ascription_suggestion(&mut err, base_span);
             }
-            err
+            (err, candidates)
         };
         let report_errors = |this: &mut Self, def: Option<Def>| {
-            report_errors(this, def).emit();
+            let (err, candidates) = report_errors(this, def);
+            let def_id = this.current_module.normal_ancestor_id;
+            let node_id = this.definitions.as_local_node_id(def_id).unwrap();
+            let better = def.is_some();
+            this.use_injections.push(UseError { err, candidates, node_id, better });
             err_path_resolution()
         };
 
@@ -3458,8 +3521,9 @@ fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool {
         vis.is_accessible_from(module.normal_ancestor_id, self)
     }
 
-    fn report_errors(&mut self) {
+    fn report_errors(&mut self, krate: &Crate) {
         self.report_shadowing_errors();
+        self.report_with_use_injections(krate);
         let mut reported_spans = FxHashSet();
 
         for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
@@ -3507,6 +3571,21 @@ fn report_errors(&mut self) {
         }
     }
 
+    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,
+            };
+            visit::walk_crate(&mut finder, krate);
+            if !candidates.is_empty() {
+                let span = finder.span.expect("did not find module");
+                show_candidates(&mut err, span, &candidates, better);
+            }
+            err.emit();
+        }
+    }
+
     fn report_shadowing_errors(&mut self) {
         for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) {
             self.resolve_legacy_scope(scope, ident, true);
index 4b32ecff2fbdf7afcd91422b4e6b651ea98c771b..a401861274debf02aa8383b9fc93a5cac9b362d7 100644 (file)
@@ -6,7 +6,7 @@ error[E0425]: cannot find value `A` in module `namespaced_enums`
    |
 help: possible candidate is found in another module, you can import it into scope
    |
-12 | use namespaced_enums::Foo::A;
+14 | use namespaced_enums::Foo::A;
    |
 
 error[E0425]: cannot find function `B` in module `namespaced_enums`
@@ -17,7 +17,7 @@ error[E0425]: cannot find function `B` in module `namespaced_enums`
    |
 help: possible candidate is found in another module, you can import it into scope
    |
-12 | use namespaced_enums::Foo::B;
+14 | use namespaced_enums::Foo::B;
    |
 
 error[E0422]: cannot find struct, variant or union type `C` in module `namespaced_enums`
@@ -28,7 +28,7 @@ error[E0422]: cannot find struct, variant or union type `C` in module `namespace
    |
 help: possible candidate is found in another module, you can import it into scope
    |
-12 | use namespaced_enums::Foo::C;
+14 | use namespaced_enums::Foo::C;
    |
 
 error: aborting due to 3 previous errors
index f4d183192b6cb583551d3e6e6c60cbf1a041f069..da849ecc71ab4f6b93e3e14ba0a4f6a2151f3297 100644 (file)
@@ -6,7 +6,7 @@ error[E0405]: cannot find trait `OuterTrait` in this scope
    |
 help: possible candidate is found in another module, you can import it into scope
    |
-16 | use issue_21221_3::outer::OuterTrait;
+18 | use issue_21221_3::outer::OuterTrait;
    |
 
 error: cannot continue compilation due to previous error
index eb71ee893ce710a3f9e33c252d4f455c9e6fe537..78059ed37bee86401c71d43966cbda76a243eb5b 100644 (file)
@@ -6,7 +6,7 @@ error[E0405]: cannot find trait `T` in this scope
    |
 help: possible candidate is found in another module, you can import it into scope
    |
-16 | use issue_21221_4::T;
+18 | use issue_21221_4::T;
    |
 
 error: cannot continue compilation due to previous error
index 70b4599dbf8cbbd84d619995bd4aca845c55596a..7a4d0ca698e6dec0d58b1e0ee6ec15bddb3d0bad 100644 (file)
@@ -6,7 +6,7 @@ error[E0404]: expected trait, found type alias `Foo`
    |
 help: possible better candidate is found in another module, you can import it into scope
    |
-12 | use issue_3907::Foo;
+14 | use issue_3907::Foo;
    |
 
 error: cannot continue compilation due to previous error
index 47141eda33c5531ae25da7dd546ed619d1a5eddd..ee1481ec6f2b0087d4964dac21d037bfbf26950b 100644 (file)
@@ -10,7 +10,7 @@ error[E0423]: expected value, found struct `Z`
    |
 help: possible better candidate is found in another module, you can import it into scope
    |
-15 | use m::n::Z;
+16 |     use m::n::Z;
    |
 
 error[E0423]: expected value, found struct `S`
@@ -24,7 +24,7 @@ error[E0423]: expected value, found struct `S`
    |
 help: possible better candidate is found in another module, you can import it into scope
    |
-13 | use m::S;
+15 | use m::S;
    |
 
 error[E0423]: expected value, found struct `xcrate::S`
@@ -38,7 +38,7 @@ error[E0423]: expected value, found struct `xcrate::S`
    |
 help: possible better candidate is found in another module, you can import it into scope
    |
-13 | use m::S;
+15 | use m::S;
    |
 
 error[E0603]: tuple struct `Z` is private
diff --git a/src/test/ui/resolve/use_suggestion_placement.rs b/src/test/ui/resolve/use_suggestion_placement.rs
new file mode 100644 (file)
index 0000000..e0027fe
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! y {
+    () => {}
+}
+
+mod m {
+    pub const A: i32 = 0;
+}
+
+fn main() {
+    y!();
+    let _ = A;
+    foo();
+}
+
+fn foo() {
+    type Dict<K, V> = HashMap<K, V>;
+}
diff --git a/src/test/ui/resolve/use_suggestion_placement.stderr b/src/test/ui/resolve/use_suggestion_placement.stderr
new file mode 100644 (file)
index 0000000..5c74d8b
--- /dev/null
@@ -0,0 +1,38 @@
+error[E0425]: cannot find value `A` in this scope
+  --> $DIR/use_suggestion_placement.rs:21:13
+   |
+21 |     let _ = A;
+   |             ^ not found in this scope
+   |
+help: possible candidate is found in another module, you can import it into scope
+   |
+11 | use m::A;
+   |
+
+error[E0412]: cannot find type `HashMap` in this scope
+  --> $DIR/use_suggestion_placement.rs:26:23
+   |
+26 |     type Dict<K, V> = HashMap<K, V>;
+   |                       ^^^^^^^ not found in this scope
+   |
+help: possible candidates are found in other modules, you can import them into scope
+   |
+11 | use std::collections::HashMap;
+   |
+11 | use std::collections::hash_map::HashMap;
+   |
+
+error[E0091]: type parameter `K` is unused
+  --> $DIR/use_suggestion_placement.rs:26:15
+   |
+26 |     type Dict<K, V> = HashMap<K, V>;
+   |               ^ unused type parameter
+
+error[E0091]: type parameter `V` is unused
+  --> $DIR/use_suggestion_placement.rs:26:18
+   |
+26 |     type Dict<K, V> = HashMap<K, V>;
+   |                  ^ unused type parameter
+
+error: aborting due to 4 previous errors
+
index 0460b7ca025a67fecfb5e6588082d46f8787dbc9..344cf69748eccf9f3ac688be384b1eb786b9bb96 100644 (file)
@@ -1,11 +1,3 @@
-error[E0577]: expected module, found struct `S`
-  --> $DIR/visibility-ty-params.rs:16:5
-   |
-16 | m!{ S<u8> } //~ ERROR generic arguments in visibility path
-   |     -^^^^
-   |     |
-   |     did you mean `m`?
-
 error: generic arguments in visibility path
   --> $DIR/visibility-ty-params.rs:16:6
    |
@@ -18,5 +10,13 @@ error: generic arguments in visibility path
 20 |     m!{ m<> } //~ ERROR generic arguments in visibility path
    |          ^^
 
+error[E0577]: expected module, found struct `S`
+  --> $DIR/visibility-ty-params.rs:16:5
+   |
+16 | m!{ S<u8> } //~ ERROR generic arguments in visibility path
+   |     -^^^^
+   |     |
+   |     did you mean `m`?
+
 error: aborting due to 3 previous errors