Skip to content

Commit e0ba29c

Browse files
committed
Improve placement of use suggestions
1 parent 85eadf8 commit e0ba29c

9 files changed

+181
-37
lines changed

src/librustc_resolve/lib.rs

+99-20
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,53 @@ impl<T> ::std::ops::IndexMut<Namespace> for PerNS<T> {
581581
}
582582
}
583583

584+
struct UsePlacementFinder {
585+
target_module: NodeId,
586+
span: Option<Span>,
587+
}
588+
589+
impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
590+
fn visit_mod(
591+
&mut self,
592+
module: &'tcx ast::Mod,
593+
_: Span,
594+
_: &[ast::Attribute],
595+
node_id: NodeId,
596+
) {
597+
if self.span.is_some() {
598+
return;
599+
}
600+
if node_id != self.target_module {
601+
visit::walk_mod(self, module);
602+
return;
603+
}
604+
// find a use statement
605+
for item in &module.items {
606+
match item.node {
607+
ItemKind::Use(..) => {
608+
// don't suggest placing a use before the prelude
609+
// import or other generated ones
610+
if item.span == DUMMY_SP {
611+
let mut span = item.span;
612+
span.hi = span.lo;
613+
self.span = Some(span);
614+
return;
615+
}
616+
},
617+
// don't place use before extern crate
618+
ItemKind::ExternCrate(_) => {}
619+
// but place them before the first other item
620+
_ => if self.span.map_or(true, |span| item.span < span ) {
621+
let mut span = item.span;
622+
span.hi = span.lo;
623+
self.span = Some(span);
624+
},
625+
}
626+
}
627+
assert!(self.span.is_some(), "a file can't have no items and emit suggestions");
628+
}
629+
}
630+
584631
impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
585632
fn visit_item(&mut self, item: &'tcx Item) {
586633
self.resolve_item(item);
@@ -990,6 +1037,16 @@ enum NameBindingKind<'a> {
9901037

9911038
struct PrivacyError<'a>(Span, Name, &'a NameBinding<'a>);
9921039

1040+
struct UseError<'a> {
1041+
err: DiagnosticBuilder<'a>,
1042+
/// Attach `use` statements for these candidates
1043+
candidates: Vec<ImportSuggestion>,
1044+
/// The node id of the module to place the use statements in
1045+
node_id: NodeId,
1046+
/// Whether the diagnostic should state that it's "better"
1047+
better: bool,
1048+
}
1049+
9931050
struct AmbiguityError<'a> {
9941051
span: Span,
9951052
name: Name,
@@ -1190,15 +1247,20 @@ pub struct Resolver<'a> {
11901247
extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
11911248

11921249
pub make_glob_map: bool,
1193-
// Maps imports to the names of items actually imported (this actually maps
1194-
// all imports, but only glob imports are actually interesting).
1250+
/// Maps imports to the names of items actually imported (this actually maps
1251+
/// all imports, but only glob imports are actually interesting).
11951252
pub glob_map: GlobMap,
11961253

11971254
used_imports: FxHashSet<(NodeId, Namespace)>,
11981255
pub maybe_unused_trait_imports: NodeSet,
11991256

1257+
/// privacy errors are delayed until the end in order to deduplicate them
12001258
privacy_errors: Vec<PrivacyError<'a>>,
1259+
/// ambiguity errors are delayed for deduplication
12011260
ambiguity_errors: Vec<AmbiguityError<'a>>,
1261+
/// `use` injections are delayed for better placement and deduplication
1262+
use_injections: Vec<UseError<'a>>,
1263+
12021264
gated_errors: FxHashSet<Span>,
12031265
disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
12041266

@@ -1401,6 +1463,7 @@ impl<'a> Resolver<'a> {
14011463

14021464
privacy_errors: Vec::new(),
14031465
ambiguity_errors: Vec::new(),
1466+
use_injections: Vec::new(),
14041467
gated_errors: FxHashSet(),
14051468
disallowed_shadowing: Vec::new(),
14061469

@@ -1465,10 +1528,11 @@ impl<'a> Resolver<'a> {
14651528
ImportResolver { resolver: self }.finalize_imports();
14661529
self.current_module = self.graph_root;
14671530
self.finalize_current_module_macro_resolutions();
1531+
14681532
visit::walk_crate(self, krate);
14691533

14701534
check_unused::check_crate(self, krate);
1471-
self.report_errors();
1535+
self.report_errors(krate);
14721536
self.crate_loader.postprocess(krate);
14731537
}
14741538

@@ -2413,25 +2477,20 @@ impl<'a> Resolver<'a> {
24132477
__diagnostic_used!(E0411);
24142478
err.code("E0411".into());
24152479
err.span_label(span, "`Self` is only available in traits and impls");
2416-
return err;
2480+
return (err, Vec::new());
24172481
}
24182482
if is_self_value(path, ns) {
24192483
__diagnostic_used!(E0424);
24202484
err.code("E0424".into());
24212485
err.span_label(span, format!("`self` value is only available in \
24222486
methods with `self` parameter"));
2423-
return err;
2487+
return (err, Vec::new());
24242488
}
24252489

24262490
// Try to lookup the name in more relaxed fashion for better error reporting.
24272491
let ident = *path.last().unwrap();
24282492
let candidates = this.lookup_import_candidates(ident.node.name, ns, is_expected);
2429-
if !candidates.is_empty() {
2430-
let mut module_span = this.current_module.span;
2431-
module_span.hi = module_span.lo;
2432-
// Report import candidates as help and proceed searching for labels.
2433-
show_candidates(&mut err, module_span, &candidates, def.is_some());
2434-
} else if is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
2493+
if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
24352494
let enum_candidates =
24362495
this.lookup_import_candidates(ident.node.name, ns, is_enum_variant);
24372496
let mut enum_candidates = enum_candidates.iter()
@@ -2471,7 +2530,7 @@ impl<'a> Resolver<'a> {
24712530
format!("Self::{}", path_str));
24722531
}
24732532
}
2474-
return err;
2533+
return (err, candidates);
24752534
}
24762535
}
24772536

@@ -2488,22 +2547,22 @@ impl<'a> Resolver<'a> {
24882547
match (def, source) {
24892548
(Def::Macro(..), _) => {
24902549
err.span_label(span, format!("did you mean `{}!(...)`?", path_str));
2491-
return err;
2550+
return (err, candidates);
24922551
}
24932552
(Def::TyAlias(..), PathSource::Trait) => {
24942553
err.span_label(span, "type aliases cannot be used for traits");
2495-
return err;
2554+
return (err, candidates);
24962555
}
24972556
(Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
24982557
ExprKind::Field(_, ident) => {
24992558
err.span_label(parent.span, format!("did you mean `{}::{}`?",
25002559
path_str, ident.node));
2501-
return err;
2560+
return (err, candidates);
25022561
}
25032562
ExprKind::MethodCall(ref segment, ..) => {
25042563
err.span_label(parent.span, format!("did you mean `{}::{}(...)`?",
25052564
path_str, segment.identifier));
2506-
return err;
2565+
return (err, candidates);
25072566
}
25082567
_ => {}
25092568
},
@@ -2519,7 +2578,7 @@ impl<'a> Resolver<'a> {
25192578
}
25202579
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
25212580
path_str));
2522-
return err;
2581+
return (err, candidates);
25232582
}
25242583
_ => {}
25252584
}
@@ -2530,10 +2589,14 @@ impl<'a> Resolver<'a> {
25302589
err.span_label(base_span, fallback_label);
25312590
this.type_ascription_suggestion(&mut err, base_span);
25322591
}
2533-
err
2592+
(err, candidates)
25342593
};
25352594
let report_errors = |this: &mut Self, def: Option<Def>| {
2536-
report_errors(this, def).emit();
2595+
let (err, candidates) = report_errors(this, def);
2596+
let def_id = this.current_module.normal_ancestor_id;
2597+
let node_id = this.definitions.as_local_node_id(def_id).unwrap();
2598+
let better = def.is_some();
2599+
this.use_injections.push(UseError { err, candidates, node_id, better });
25372600
err_path_resolution()
25382601
};
25392602

@@ -3458,8 +3521,9 @@ impl<'a> Resolver<'a> {
34583521
vis.is_accessible_from(module.normal_ancestor_id, self)
34593522
}
34603523

3461-
fn report_errors(&mut self) {
3524+
fn report_errors(&mut self, krate: &Crate) {
34623525
self.report_shadowing_errors();
3526+
self.report_with_use_injections(krate);
34633527
let mut reported_spans = FxHashSet();
34643528

34653529
for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
@@ -3507,6 +3571,21 @@ impl<'a> Resolver<'a> {
35073571
}
35083572
}
35093573

3574+
fn report_with_use_injections(&mut self, krate: &Crate) {
3575+
for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) {
3576+
let mut finder = UsePlacementFinder {
3577+
target_module: node_id,
3578+
span: None,
3579+
};
3580+
visit::walk_crate(&mut finder, krate);
3581+
if !candidates.is_empty() {
3582+
let span = finder.span.expect("did not find module");
3583+
show_candidates(&mut err, span, &candidates, better);
3584+
}
3585+
err.emit();
3586+
}
3587+
}
3588+
35103589
fn report_shadowing_errors(&mut self) {
35113590
for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) {
35123591
self.resolve_legacy_scope(scope, ident, true);

src/test/ui/resolve/enums-are-namespaced-xc.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error[E0425]: cannot find value `A` in module `namespaced_enums`
66
|
77
help: possible candidate is found in another module, you can import it into scope
88
|
9-
12 | use namespaced_enums::Foo::A;
9+
14 | use namespaced_enums::Foo::A;
1010
|
1111

1212
error[E0425]: cannot find function `B` in module `namespaced_enums`
@@ -17,7 +17,7 @@ error[E0425]: cannot find function `B` in module `namespaced_enums`
1717
|
1818
help: possible candidate is found in another module, you can import it into scope
1919
|
20-
12 | use namespaced_enums::Foo::B;
20+
14 | use namespaced_enums::Foo::B;
2121
|
2222

2323
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
2828
|
2929
help: possible candidate is found in another module, you can import it into scope
3030
|
31-
12 | use namespaced_enums::Foo::C;
31+
14 | use namespaced_enums::Foo::C;
3232
|
3333

3434
error: aborting due to 3 previous errors

src/test/ui/resolve/issue-21221-3.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error[E0405]: cannot find trait `OuterTrait` in this scope
66
|
77
help: possible candidate is found in another module, you can import it into scope
88
|
9-
16 | use issue_21221_3::outer::OuterTrait;
9+
18 | use issue_21221_3::outer::OuterTrait;
1010
|
1111

1212
error: cannot continue compilation due to previous error

src/test/ui/resolve/issue-21221-4.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error[E0405]: cannot find trait `T` in this scope
66
|
77
help: possible candidate is found in another module, you can import it into scope
88
|
9-
16 | use issue_21221_4::T;
9+
18 | use issue_21221_4::T;
1010
|
1111

1212
error: cannot continue compilation due to previous error

src/test/ui/resolve/issue-3907.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error[E0404]: expected trait, found type alias `Foo`
66
|
77
help: possible better candidate is found in another module, you can import it into scope
88
|
9-
12 | use issue_3907::Foo;
9+
14 | use issue_3907::Foo;
1010
|
1111

1212
error: cannot continue compilation due to previous error

src/test/ui/resolve/privacy-struct-ctor.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error[E0423]: expected value, found struct `Z`
1010
|
1111
help: possible better candidate is found in another module, you can import it into scope
1212
|
13-
15 | use m::n::Z;
13+
16 | use m::n::Z;
1414
|
1515

1616
error[E0423]: expected value, found struct `S`
@@ -24,7 +24,7 @@ error[E0423]: expected value, found struct `S`
2424
|
2525
help: possible better candidate is found in another module, you can import it into scope
2626
|
27-
13 | use m::S;
27+
15 | use m::S;
2828
|
2929

3030
error[E0423]: expected value, found struct `xcrate::S`
@@ -38,7 +38,7 @@ error[E0423]: expected value, found struct `xcrate::S`
3838
|
3939
help: possible better candidate is found in another module, you can import it into scope
4040
|
41-
13 | use m::S;
41+
15 | use m::S;
4242
|
4343

4444
error[E0603]: tuple struct `Z` is private
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! y {
12+
() => {}
13+
}
14+
15+
mod m {
16+
pub const A: i32 = 0;
17+
}
18+
19+
fn main() {
20+
y!();
21+
let _ = A;
22+
foo();
23+
}
24+
25+
fn foo() {
26+
type Dict<K, V> = HashMap<K, V>;
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error[E0425]: cannot find value `A` in this scope
2+
--> $DIR/use_suggestion_placement.rs:21:13
3+
|
4+
21 | let _ = A;
5+
| ^ not found in this scope
6+
|
7+
help: possible candidate is found in another module, you can import it into scope
8+
|
9+
11 | use m::A;
10+
|
11+
12+
error[E0412]: cannot find type `HashMap` in this scope
13+
--> $DIR/use_suggestion_placement.rs:26:23
14+
|
15+
26 | type Dict<K, V> = HashMap<K, V>;
16+
| ^^^^^^^ not found in this scope
17+
|
18+
help: possible candidates are found in other modules, you can import them into scope
19+
|
20+
11 | use std::collections::HashMap;
21+
|
22+
11 | use std::collections::hash_map::HashMap;
23+
|
24+
25+
error[E0091]: type parameter `K` is unused
26+
--> $DIR/use_suggestion_placement.rs:26:15
27+
|
28+
26 | type Dict<K, V> = HashMap<K, V>;
29+
| ^ unused type parameter
30+
31+
error[E0091]: type parameter `V` is unused
32+
--> $DIR/use_suggestion_placement.rs:26:18
33+
|
34+
26 | type Dict<K, V> = HashMap<K, V>;
35+
| ^ unused type parameter
36+
37+
error: aborting due to 4 previous errors
38+

0 commit comments

Comments
 (0)