Add structured suggestions for trait imports

上级 ef94d5c1
......@@ -588,6 +588,18 @@ struct UsePlacementFinder {
found_use: bool,
}
impl UsePlacementFinder {
fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
let mut finder = UsePlacementFinder {
target_module,
span: None,
found_use: false,
};
visit::walk_crate(&mut finder, krate);
(finder.span, finder.found_use)
}
}
impl<'tcx> Visitor<'tcx> for UsePlacementFinder {
fn visit_mod(
&mut self,
......@@ -3588,14 +3600,9 @@ fn report_errors(&mut self, krate: &Crate) {
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,
found_use: false,
};
visit::walk_crate(&mut finder, krate);
let (span, found_use) = UsePlacementFinder::check(krate, node_id);
if !candidates.is_empty() {
show_candidates(&mut err, finder.span, &candidates, better, finder.found_use);
show_candidates(&mut err, span, &candidates, better, found_use);
}
err.emit();
}
......
......@@ -340,16 +340,35 @@ fn suggest_use_candidates(&self,
err: &mut DiagnosticBuilder,
mut msg: String,
candidates: Vec<DefId>) {
let limit = if candidates.len() == 5 { 5 } else { 4 };
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
msg.push_str(&format!("\ncandidate #{}: `use {};`",
i + 1,
self.tcx.item_path_str(*trait_did)));
}
if candidates.len() > limit {
msg.push_str(&format!("\nand {} others", candidates.len() - limit));
let module_did = self.tcx.hir.get_module_parent(self.body_id);
let module_id = self.tcx.hir.as_local_node_id(module_did).unwrap();
let krate = self.tcx.hir.krate();
let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id);
if let Some(span) = span {
let path_strings = candidates.iter().map(|did| {
// produce an additional newline to separate the new use statement
// from the directly following item.
let additional_newline = if found_use {
""
} else {
"\n"
};
format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline)
}).collect();
err.span_suggestions(span, &msg, path_strings);
} else {
let limit = if candidates.len() == 5 { 5 } else { 4 };
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
msg.push_str(&format!("\ncandidate #{}: `use {};`",
i + 1,
self.tcx.item_path_str(*trait_did)));
}
if candidates.len() > limit {
msg.push_str(&format!("\nand {} others", candidates.len() - limit));
}
err.note(&msg[..]);
}
err.note(&msg[..]);
}
fn suggest_valid_traits(&self,
......@@ -604,3 +623,83 @@ fn next(&mut self) -> Option<TraitInfo> {
})
}
}
struct UsePlacementFinder<'a, 'tcx: 'a, 'gcx: 'tcx> {
target_module: ast::NodeId,
span: Option<Span>,
found_use: bool,
tcx: TyCtxt<'a, 'gcx, 'tcx>
}
impl<'a, 'tcx, 'gcx> UsePlacementFinder<'a, 'tcx, 'gcx> {
fn check(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
krate: &'tcx hir::Crate,
target_module: ast::NodeId,
) -> (Option<Span>, bool) {
let mut finder = UsePlacementFinder {
target_module,
span: None,
found_use: false,
tcx,
};
hir::intravisit::walk_crate(&mut finder, krate);
(finder.span, finder.found_use)
}
}
impl<'a, 'tcx, 'gcx> hir::intravisit::Visitor<'tcx> for UsePlacementFinder<'a, 'tcx, 'gcx> {
fn visit_mod(
&mut self,
module: &'tcx hir::Mod,
_: Span,
node_id: ast::NodeId,
) {
if self.span.is_some() {
return;
}
if node_id != self.target_module {
hir::intravisit::walk_mod(self, module, node_id);
return;
}
// find a use statement
for item_id in &module.item_ids {
let item = self.tcx.hir.expect_item(item_id.id);
match item.node {
hir::ItemUse(..) => {
// don't suggest placing a use before the prelude
// import or other generated ones
if item.span.ctxt().outer().expn_info().is_none() {
self.span = Some(item.span.with_hi(item.span.lo()));
self.found_use = true;
return;
}
},
// don't place use before extern crate
hir::ItemExternCrate(_) => {}
// but place them before the first other item
_ => if self.span.map_or(true, |span| item.span < span ) {
if item.span.ctxt().outer().expn_info().is_none() {
// don't insert between attributes and an item
if item.attrs.is_empty() {
self.span = Some(item.span.with_hi(item.span.lo()));
} else {
// find the first attribute on the item
for attr in &item.attrs {
if self.span.map_or(true, |span| attr.span < span) {
self.span = Some(attr.span.with_hi(attr.span.lo()));
}
}
}
}
},
}
}
}
fn nested_visit_map<'this>(
&'this mut self
) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
hir::intravisit::NestedVisitorMap::None
}
}
......@@ -5,11 +5,16 @@ error[E0599]: no method named `method` found for type `u32` in the current scope
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
candidate #1: `use foo::Bar;`
candidate #2: `use no_method_suggested_traits::foo::PubPub;`
candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
candidate #4: `use no_method_suggested_traits::Reexported;`
help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
|
14 | use foo::Bar;
|
14 | use no_method_suggested_traits::foo::PubPub;
|
14 | use no_method_suggested_traits::qux::PrivPub;
|
14 | use no_method_suggested_traits::Reexported;
|
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&u32>>` in the current scope
--> $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
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
candidate #1: `use foo::Bar;`
candidate #2: `use no_method_suggested_traits::foo::PubPub;`
candidate #3: `use no_method_suggested_traits::qux::PrivPub;`
candidate #4: `use no_method_suggested_traits::Reexported;`
help: the following traits are implemented but not in scope, perhaps add a `use` for one of them:
|
14 | use foo::Bar;
|
14 | use no_method_suggested_traits::foo::PubPub;
|
14 | use no_method_suggested_traits::qux::PrivPub;
|
14 | use no_method_suggested_traits::Reexported;
|
error[E0599]: no method named `method` found for type `char` in the current scope
--> $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
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
candidate #1: `use foo::Bar;`
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
14 | use foo::Bar;
|
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
--> $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
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
candidate #1: `use foo::Bar;`
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
14 | use foo::Bar;
|
error[E0599]: no method named `method` found for type `i32` in the current scope
--> $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
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
candidate #1: `use no_method_suggested_traits::foo::PubPub;`
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
14 | use no_method_suggested_traits::foo::PubPub;
|
error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&i32>>` in the current scope
--> $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
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
candidate #1: `use no_method_suggested_traits::foo::PubPub;`
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
14 | use no_method_suggested_traits::foo::PubPub;
|
error[E0599]: no method named `method` found for type `Foo` in the current scope
--> $DIR/no-method-suggested-traits.rs:62:9
......
......@@ -4,8 +4,10 @@ error: the `wait` method cannot be invoked on a trait object
24 | arg.wait();
| ^^^^
|
= note: another candidate was found in the following trait, perhaps add a `use` for it:
candidate #1: `use private::Future;`
help: another candidate was found in the following trait, perhaps add a `use` for it:
|
11 | use private::Future;
|
error: aborting due to previous error
......@@ -5,8 +5,10 @@ error[E0624]: method `method` is private
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
candidate #1: `use inner::Bar;`
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
11 | use inner::Bar;
|
error: aborting due to previous error
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册