Skip to content

Commit

Permalink
Improve placement of use suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Aug 17, 2017
1 parent 85eadf8 commit e0ba29c
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 37 deletions.
119 changes: 99 additions & 20 deletions src/librustc_resolve/lib.rs
Expand Up @@ -581,6 +581,53 @@ impl<T> ::std::ops::IndexMut<Namespace> for PerNS<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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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>>,

Expand Down Expand Up @@ -1401,6 +1463,7 @@ impl<'a> Resolver<'a> {

privacy_errors: Vec::new(),
ambiguity_errors: Vec::new(),
use_injections: Vec::new(),
gated_errors: FxHashSet(),
disallowed_shadowing: Vec::new(),

Expand Down Expand Up @@ -1465,10 +1528,11 @@ impl<'a> Resolver<'a> {
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);
}

Expand Down Expand Up @@ -2413,25 +2477,20 @@ impl<'a> Resolver<'a> {
__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()
Expand Down Expand Up @@ -2471,7 +2530,7 @@ impl<'a> Resolver<'a> {
format!("Self::{}", path_str));
}
}
return err;
return (err, candidates);
}
}

Expand All @@ -2488,22 +2547,22 @@ impl<'a> Resolver<'a> {
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);
}
_ => {}
},
Expand All @@ -2519,7 +2578,7 @@ impl<'a> Resolver<'a> {
}
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
path_str));
return err;
return (err, candidates);
}
_ => {}
}
Expand All @@ -2530,10 +2589,14 @@ impl<'a> Resolver<'a> {
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()
};

Expand Down Expand Up @@ -3458,8 +3521,9 @@ impl<'a> Resolver<'a> {
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 {
Expand Down Expand Up @@ -3507,6 +3571,21 @@ 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,
};
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);
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/resolve/enums-are-namespaced-xc.stderr
Expand Up @@ -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`
Expand All @@ -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`
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/resolve/issue-21221-3.stderr
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/resolve/issue-21221-4.stderr
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/resolve/issue-3907.stderr
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/resolve/privacy-struct-ctor.stderr
Expand Up @@ -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`
Expand All @@ -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`
Expand All @@ -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
Expand Down
27 changes: 27 additions & 0 deletions src/test/ui/resolve/use_suggestion_placement.rs
@@ -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>;
}
38 changes: 38 additions & 0 deletions src/test/ui/resolve/use_suggestion_placement.stderr
@@ -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

0 comments on commit e0ba29c

Please sign in to comment.