Skip to content

Commit 06bf94a

Browse files
committed
Auto merge of #43929 - oli-obk:use_placement, r=nrc
Improve placement of `use` suggestions r? @nrc cc @estebank @Mark-Simulacrum fixes #42835 fixes #42548 fixes #43769
2 parents cf56cf3 + 8f56322 commit 06bf94a

10 files changed

+198
-41
lines changed

src/librustc_errors/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,10 @@ impl CodeSuggestion {
217217
if !buf.ends_with('\n') {
218218
push_trailing(buf, prev_line.as_ref(), &prev_hi, None);
219219
}
220-
// remove trailing newline
221-
buf.pop();
220+
// remove trailing newlines
221+
while buf.ends_with('\n') {
222+
buf.pop();
223+
}
222224
}
223225
bufs
224226
}

src/librustc_resolve/lib.rs

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

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

9911040
struct PrivacyError<'a>(Span, Name, &'a NameBinding<'a>);
9921041

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

11921251
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).
1252+
/// Maps imports to the names of items actually imported (this actually maps
1253+
/// all imports, but only glob imports are actually interesting).
11951254
pub glob_map: GlobMap,
11961255

11971256
used_imports: FxHashSet<(NodeId, Namespace)>,
11981257
pub maybe_unused_trait_imports: NodeSet,
11991258

1259+
/// privacy errors are delayed until the end in order to deduplicate them
12001260
privacy_errors: Vec<PrivacyError<'a>>,
1261+
/// ambiguity errors are delayed for deduplication
12011262
ambiguity_errors: Vec<AmbiguityError<'a>>,
1263+
/// `use` injections are delayed for better placement and deduplication
1264+
use_injections: Vec<UseError<'a>>,
1265+
12021266
gated_errors: FxHashSet<Span>,
12031267
disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
12041268

@@ -1401,6 +1465,7 @@ impl<'a> Resolver<'a> {
14011465

14021466
privacy_errors: Vec::new(),
14031467
ambiguity_errors: Vec::new(),
1468+
use_injections: Vec::new(),
14041469
gated_errors: FxHashSet(),
14051470
disallowed_shadowing: Vec::new(),
14061471

@@ -1465,10 +1530,11 @@ impl<'a> Resolver<'a> {
14651530
ImportResolver { resolver: self }.finalize_imports();
14661531
self.current_module = self.graph_root;
14671532
self.finalize_current_module_macro_resolutions();
1533+
14681534
visit::walk_crate(self, krate);
14691535

14701536
check_unused::check_crate(self, krate);
1471-
self.report_errors();
1537+
self.report_errors(krate);
14721538
self.crate_loader.postprocess(krate);
14731539
}
14741540

@@ -2413,25 +2479,20 @@ impl<'a> Resolver<'a> {
24132479
__diagnostic_used!(E0411);
24142480
err.code("E0411".into());
24152481
err.span_label(span, "`Self` is only available in traits and impls");
2416-
return err;
2482+
return (err, Vec::new());
24172483
}
24182484
if is_self_value(path, ns) {
24192485
__diagnostic_used!(E0424);
24202486
err.code("E0424".into());
24212487
err.span_label(span, format!("`self` value is only available in \
24222488
methods with `self` parameter"));
2423-
return err;
2489+
return (err, Vec::new());
24242490
}
24252491

24262492
// Try to lookup the name in more relaxed fashion for better error reporting.
24272493
let ident = *path.last().unwrap();
24282494
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))) {
2495+
if candidates.is_empty() && is_expected(Def::Enum(DefId::local(CRATE_DEF_INDEX))) {
24352496
let enum_candidates =
24362497
this.lookup_import_candidates(ident.node.name, ns, is_enum_variant);
24372498
let mut enum_candidates = enum_candidates.iter()
@@ -2471,7 +2532,7 @@ impl<'a> Resolver<'a> {
24712532
format!("Self::{}", path_str));
24722533
}
24732534
}
2474-
return err;
2535+
return (err, candidates);
24752536
}
24762537
}
24772538

@@ -2488,22 +2549,22 @@ impl<'a> Resolver<'a> {
24882549
match (def, source) {
24892550
(Def::Macro(..), _) => {
24902551
err.span_label(span, format!("did you mean `{}!(...)`?", path_str));
2491-
return err;
2552+
return (err, candidates);
24922553
}
24932554
(Def::TyAlias(..), PathSource::Trait) => {
24942555
err.span_label(span, "type aliases cannot be used for traits");
2495-
return err;
2556+
return (err, candidates);
24962557
}
24972558
(Def::Mod(..), PathSource::Expr(Some(parent))) => match parent.node {
24982559
ExprKind::Field(_, ident) => {
24992560
err.span_label(parent.span, format!("did you mean `{}::{}`?",
25002561
path_str, ident.node));
2501-
return err;
2562+
return (err, candidates);
25022563
}
25032564
ExprKind::MethodCall(ref segment, ..) => {
25042565
err.span_label(parent.span, format!("did you mean `{}::{}(...)`?",
25052566
path_str, segment.identifier));
2506-
return err;
2567+
return (err, candidates);
25072568
}
25082569
_ => {}
25092570
},
@@ -2519,7 +2580,7 @@ impl<'a> Resolver<'a> {
25192580
}
25202581
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?",
25212582
path_str));
2522-
return err;
2583+
return (err, candidates);
25232584
}
25242585
_ => {}
25252586
}
@@ -2530,10 +2591,14 @@ impl<'a> Resolver<'a> {
25302591
err.span_label(base_span, fallback_label);
25312592
this.type_ascription_suggestion(&mut err, base_span);
25322593
}
2533-
err
2594+
(err, candidates)
25342595
};
25352596
let report_errors = |this: &mut Self, def: Option<Def>| {
2536-
report_errors(this, def).emit();
2597+
let (err, candidates) = report_errors(this, def);
2598+
let def_id = this.current_module.normal_ancestor_id;
2599+
let node_id = this.definitions.as_local_node_id(def_id).unwrap();
2600+
let better = def.is_some();
2601+
this.use_injections.push(UseError { err, candidates, node_id, better });
25372602
err_path_resolution()
25382603
};
25392604

@@ -3458,8 +3523,9 @@ impl<'a> Resolver<'a> {
34583523
vis.is_accessible_from(module.normal_ancestor_id, self)
34593524
}
34603525

3461-
fn report_errors(&mut self) {
3526+
fn report_errors(&mut self, krate: &Crate) {
34623527
self.report_shadowing_errors();
3528+
self.report_with_use_injections(krate);
34633529
let mut reported_spans = FxHashSet();
34643530

34653531
for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
@@ -3507,6 +3573,22 @@ impl<'a> Resolver<'a> {
35073573
}
35083574
}
35093575

3576+
fn report_with_use_injections(&mut self, krate: &Crate) {
3577+
for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) {
3578+
let mut finder = UsePlacementFinder {
3579+
target_module: node_id,
3580+
span: None,
3581+
found_use: false,
3582+
};
3583+
visit::walk_crate(&mut finder, krate);
3584+
if !candidates.is_empty() {
3585+
let span = finder.span.expect("did not find module");
3586+
show_candidates(&mut err, span, &candidates, better, finder.found_use);
3587+
}
3588+
err.emit();
3589+
}
3590+
}
3591+
35103592
fn report_shadowing_errors(&mut self) {
35113593
for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) {
35123594
self.resolve_legacy_scope(scope, ident, true);
@@ -3697,7 +3779,8 @@ fn import_candidate_to_paths(suggestion: &ImportSuggestion) -> (Span, String, St
36973779
fn show_candidates(err: &mut DiagnosticBuilder,
36983780
span: Span,
36993781
candidates: &[ImportSuggestion],
3700-
better: bool) {
3782+
better: bool,
3783+
found_use: bool) {
37013784

37023785
// we want consistent results across executions, but candidates are produced
37033786
// by iterating through a hash map, so make sure they are ordered:
@@ -3713,7 +3796,14 @@ fn show_candidates(err: &mut DiagnosticBuilder,
37133796
let msg = format!("possible {}candidate{} into scope", better, msg_diff);
37143797

37153798
for candidate in &mut path_strings {
3716-
*candidate = format!("use {};\n", candidate);
3799+
// produce an additional newline to separate the new use statement
3800+
// from the directly following item.
3801+
let additional_newline = if found_use {
3802+
""
3803+
} else {
3804+
"\n"
3805+
};
3806+
*candidate = format!("use {};\n{}", candidate, additional_newline);
37173807
}
37183808

37193809
err.span_suggestions(span, &msg, path_strings);

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+
}

0 commit comments

Comments
 (0)