Skip to content

Commit 45594d5

Browse files
committed
Auto merge of #46035 - oli-obk:use_suggestions, r=petrochenkov
Add structured suggestions for various "use" suggestions r? @petrochenkov
2 parents 1dc0b57 + 2c2891b commit 45594d5

File tree

6 files changed

+207
-43
lines changed

6 files changed

+207
-43
lines changed

src/librustc_resolve/lib.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,18 @@ struct UsePlacementFinder {
588588
found_use: bool,
589589
}
590590

591+
impl UsePlacementFinder {
592+
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
593+
let mut finder = UsePlacementFinder {
594+
target_module,
595+
span: None,
596+
found_use: false,
597+
};
598+
visit::walk_crate(&mut finder, krate);
599+
(finder.span, finder.found_use)
600+
}
601+
}
602+
591603
impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
592604
fn visit_mod(
593605
&mut self,
@@ -1278,6 +1290,8 @@ pub struct Resolver<'a> {
12781290
ambiguity_errors: Vec<AmbiguityError<'a>>,
12791291
/// `use` injections are delayed for better placement and deduplication
12801292
use_injections: Vec<UseError<'a>>,
1293+
/// `use` injections for proc macros wrongly imported with #[macro_use]
1294+
proc_mac_errors: Vec<macros::ProcMacError>,
12811295

12821296
gated_errors: FxHashSet<Span>,
12831297
disallowed_shadowing: Vec<&'a LegacyBinding<'a>>,
@@ -1486,6 +1500,7 @@ impl<'a> Resolver<'a> {
14861500
privacy_errors: Vec::new(),
14871501
ambiguity_errors: Vec::new(),
14881502
use_injections: Vec::new(),
1503+
proc_mac_errors: Vec::new(),
14891504
gated_errors: FxHashSet(),
14901505
disallowed_shadowing: Vec::new(),
14911506

@@ -3569,6 +3584,7 @@ impl<'a> Resolver<'a> {
35693584
fn report_errors(&mut self, krate: &Crate) {
35703585
self.report_shadowing_errors();
35713586
self.report_with_use_injections(krate);
3587+
self.report_proc_macro_import(krate);
35723588
let mut reported_spans = FxHashSet();
35733589

35743590
for &AmbiguityError { span, name, b1, b2, lexical, legacy } in &self.ambiguity_errors {
@@ -3618,14 +3634,9 @@ impl<'a> Resolver<'a> {
36183634

36193635
fn report_with_use_injections(&mut self, krate: &Crate) {
36203636
for UseError { mut err, candidates, node_id, better } in self.use_injections.drain(..) {
3621-
let mut finder = UsePlacementFinder {
3622-
target_module: node_id,
3623-
span: None,
3624-
found_use: false,
3625-
};
3626-
visit::walk_crate(&mut finder, krate);
3637+
let (span, found_use) = UsePlacementFinder::check(krate, node_id);
36273638
if !candidates.is_empty() {
3628-
show_candidates(&mut err, finder.span, &candidates, better, finder.found_use);
3639+
show_candidates(&mut err, span, &candidates, better, found_use);
36293640
}
36303641
err.emit();
36313642
}

src/librustc_resolve/macros.rs

+38-5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ pub struct LegacyBinding<'a> {
8383
pub span: Span,
8484
}
8585

86+
pub struct ProcMacError {
87+
crate_name: Symbol,
88+
name: Symbol,
89+
module: ast::NodeId,
90+
use_span: Span,
91+
warn_msg: &'static str,
92+
}
93+
8694
#[derive(Copy, Clone)]
8795
pub enum MacroBinding<'a> {
8896
Legacy(&'a LegacyBinding<'a>),
@@ -779,12 +787,37 @@ impl<'a> Resolver<'a> {
779787
_ => return,
780788
};
781789

782-
let crate_name = self.cstore.crate_name_untracked(krate);
790+
let def_id = self.current_module.normal_ancestor_id;
791+
let node_id = self.definitions.as_local_node_id(def_id).unwrap();
792+
793+
self.proc_mac_errors.push(ProcMacError {
794+
crate_name: self.cstore.crate_name_untracked(krate),
795+
name,
796+
module: node_id,
797+
use_span,
798+
warn_msg,
799+
});
800+
}
801+
802+
pub fn report_proc_macro_import(&mut self, krate: &ast::Crate) {
803+
for err in self.proc_mac_errors.drain(..) {
804+
let (span, found_use) = ::UsePlacementFinder::check(krate, err.module);
783805

784-
self.session.struct_span_err(use_span, warn_msg)
785-
.help(&format!("instead, import the procedural macro like any other item: \
786-
`use {}::{};`", crate_name, name))
787-
.emit();
806+
if let Some(span) = span {
807+
let found_use = if found_use { "" } else { "\n" };
808+
self.session.struct_span_err(err.use_span, err.warn_msg)
809+
.span_suggestion(
810+
span,
811+
"instead, import the procedural macro like any other item",
812+
format!("use {}::{};{}", err.crate_name, err.name, found_use),
813+
).emit();
814+
} else {
815+
self.session.struct_span_err(err.use_span, err.warn_msg)
816+
.help(&format!("instead, import the procedural macro like any other item: \
817+
`use {}::{};`", err.crate_name, err.name))
818+
.emit();
819+
}
820+
}
788821
}
789822

790823
fn gate_legacy_custom_derive(&mut self, name: Symbol, span: Span) {

src/librustc_typeck/check/method/suggest.rs

+108-9
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
340340
err: &mut DiagnosticBuilder,
341341
mut msg: String,
342342
candidates: Vec<DefId>) {
343-
let limit = if candidates.len() == 5 { 5 } else { 4 };
344-
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
345-
msg.push_str(&format!("\ncandidate #{}: `use {};`",
346-
i + 1,
347-
self.tcx.item_path_str(*trait_did)));
348-
}
349-
if candidates.len() > limit {
350-
msg.push_str(&format!("\nand {} others", candidates.len() - limit));
343+
let module_did = self.tcx.hir.get_module_parent(self.body_id);
344+
let module_id = self.tcx.hir.as_local_node_id(module_did).unwrap();
345+
let krate = self.tcx.hir.krate();
346+
let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id);
347+
if let Some(span) = span {
348+
let path_strings = candidates.iter().map(|did| {
349+
// produce an additional newline to separate the new use statement
350+
// from the directly following item.
351+
let additional_newline = if found_use {
352+
""
353+
} else {
354+
"\n"
355+
};
356+
format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline)
357+
}).collect();
358+
359+
err.span_suggestions(span, &msg, path_strings);
360+
} else {
361+
let limit = if candidates.len() == 5 { 5 } else { 4 };
362+
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
363+
msg.push_str(&format!("\ncandidate #{}: `use {};`",
364+
i + 1,
365+
self.tcx.item_path_str(*trait_did)));
366+
}
367+
if candidates.len() > limit {
368+
msg.push_str(&format!("\nand {} others", candidates.len() - limit));
369+
}
370+
err.note(&msg[..]);
351371
}
352-
err.note(&msg[..]);
353372
}
354373

355374
fn suggest_valid_traits(&self,
@@ -604,3 +623,83 @@ impl<'a> Iterator for AllTraits<'a> {
604623
})
605624
}
606625
}
626+
627+
628+
struct UsePlacementFinder<'a, 'tcx: 'a, 'gcx: 'tcx> {
629+
target_module: ast::NodeId,
630+
span: Option<Span>,
631+
found_use: bool,
632+
tcx: TyCtxt<'a, 'gcx, 'tcx>
633+
}
634+
635+
impl<'a, 'tcx, 'gcx> UsePlacementFinder<'a, 'tcx, 'gcx> {
636+
fn check(
637+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
638+
krate: &'tcx hir::Crate,
639+
target_module: ast::NodeId,
640+
) -> (Option<Span>, bool) {
641+
let mut finder = UsePlacementFinder {
642+
target_module,
643+
span: None,
644+
found_use: false,
645+
tcx,
646+
};
647+
hir::intravisit::walk_crate(&mut finder, krate);
648+
(finder.span, finder.found_use)
649+
}
650+
}
651+
652+
impl<'a, 'tcx, 'gcx> hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'a, 'tcx, 'gcx> {
653+
fn visit_mod(
654+
&mut self,
655+
module: &'tcx hir::Mod,
656+
_: Span,
657+
node_id: ast::NodeId,
658+
) {
659+
if self.span.is_some() {
660+
return;
661+
}
662+
if node_id != self.target_module {
663+
hir::intravisit::walk_mod(self, module, node_id);
664+
return;
665+
}
666+
// find a use statement
667+
for item_id in &module.item_ids {
668+
let item = self.tcx.hir.expect_item(item_id.id);
669+
match item.node {
670+
hir::ItemUse(..) => {
671+
// don't suggest placing a use before the prelude
672+
// import or other generated ones
673+
if item.span.ctxt().outer().expn_info().is_none() {
674+
self.span = Some(item.span.with_hi(item.span.lo()));
675+
self.found_use = true;
676+
return;
677+
}
678+
},
679+
// don't place use before extern crate
680+
hir::ItemExternCrate(_) => {}
681+
// but place them before the first other item
682+
_ => if self.span.map_or(true, |span| item.span < span ) {
683+
if item.span.ctxt().outer().expn_info().is_none() {
684+
// don't insert between attributes and an item
685+
if item.attrs.is_empty() {
686+
self.span = Some(item.span.with_hi(item.span.lo()));
687+
} else {
688+
// find the first attribute on the item
689+
for attr in &item.attrs {
690+
if self.span.map_or(true, |span| attr.span < span) {
691+
self.span = Some(attr.span.with_hi(attr.span.lo()));
692+
}
693+
}
694+
}
695+
}
696+
},
697+
}
698+
}
699+
}
700+
fn nested_visit_map<'this>(
701+
&'this mut self
702+
) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
703+
hir::intravisit::NestedVisitorMap::None
704+
}
705+
}

src/test/ui/impl-trait/no-method-suggested-traits.stderr

+36-18
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ error[E0599]: no method named `method` found for type `u32` in the current scope
55
| ^^^^^^
66
|
77
= help: items from traits can only be used if the trait is in scope
8-
= note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
9-
candidate #1: `use foo::Bar;`
10-
candidate #2: `use no_method_suggested_traits::foo::PubPub;`
11-
candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
12-
candidate #4: `use no_method_suggested_traits::Reexported;`
8+
help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
9+
|
10+
14 | use foo::Bar;
11+
|
12+
14 | use no_method_suggested_traits::foo::PubPub;
13+
|
14+
14 | use no_method_suggested_traits::qux::PrivPub;
15+
|
16+
14 | use no_method_suggested_traits::Reexported;
17+
|
1318

1419
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope
1520
--> $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
1823
| ^^^^^^
1924
|
2025
= help: items from traits can only be used if the trait is in scope
21-
= note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
22-
candidate #1: `use foo::Bar;`
23-
candidate #2: `use no_method_suggested_traits::foo::PubPub;`
24-
candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
25-
candidate #4: `use no_method_suggested_traits::Reexported;`
26+
help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
27+
|
28+
14 | use foo::Bar;
29+
|
30+
14 | use no_method_suggested_traits::foo::PubPub;
31+
|
32+
14 | use no_method_suggested_traits::qux::PrivPub;
33+
|
34+
14 | use no_method_suggested_traits::Reexported;
35+
|
2636

2737
error[E0599]: no method named `method` found for type `char` in the current scope
2838
--> $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
3141
| ^^^^^^
3242
|
3343
= help: items from traits can only be used if the trait is in scope
34-
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
35-
candidate #1: `use foo::Bar;`
44+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
45+
|
46+
14 | use foo::Bar;
47+
|
3648

3749
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
3850
--> $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
4153
| ^^^^^^
4254
|
4355
= help: items from traits can only be used if the trait is in scope
44-
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
45-
candidate #1: `use foo::Bar;`
56+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
57+
|
58+
14 | use foo::Bar;
59+
|
4660

4761
error[E0599]: no method named `method` found for type `i32` in the current scope
4862
--> $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
5165
| ^^^^^^
5266
|
5367
= help: items from traits can only be used if the trait is in scope
54-
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
55-
candidate #1: `use no_method_suggested_traits::foo::PubPub;`
68+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
69+
|
70+
14 | use no_method_suggested_traits::foo::PubPub;
71+
|
5672

5773
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope
5874
--> $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
6177
| ^^^^^^
6278
|
6379
= help: items from traits can only be used if the trait is in scope
64-
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
65-
candidate #1: `use no_method_suggested_traits::foo::PubPub;`
80+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
81+
|
82+
14 | use no_method_suggested_traits::foo::PubPub;
83+
|
6684

6785
error[E0599]: no method named `method` found for type `Foo` in the current scope
6886
--> $DIR/no-method-suggested-traits.rs:62:9

src/test/ui/issue-35976.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ error: the `wait` method cannot be invoked on a trait object
33
|
44
24 | arg.wait();
55
| ^^^^
6+
help: another candidate was found in the following trait, perhaps add a `use` for it:
7+
|
8+
11 | use private::Future;
69
|
7-
= note: another candidate was found in the following trait, perhaps add a `use` for it:
8-
candidate #1: `use private::Future;`
910

1011
error: aborting due to previous error
1112

src/test/ui/trait-method-private.stderr

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ error[E0624]: method `method` is private
55
| ^^^^^^
66
|
77
= help: items from traits can only be used if the trait is in scope
8-
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
9-
candidate #1: `use inner::Bar;`
8+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
9+
|
10+
11 | use inner::Bar;
11+
|
1012

1113
error: aborting due to previous error
1214

0 commit comments

Comments
 (0)