Rollup merge of #65242 - estebank:contrain-trait-sugg, r=varkor

Fix suggestion to constrain trait for method to be found

Fix #65044.
This commit is contained in:
Tyler Mandry 2019-10-15 16:07:44 -07:00 committed by GitHub
commit ef9d6ee8ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 191 additions and 43 deletions

View file

@ -777,7 +777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
"items from traits can only be used if the trait is implemented and in scope"
});
let mut msg = format!(
let message = |action| format!(
"the following {traits_define} an item `{name}`, perhaps you need to {action} \
{one_of_them}:",
traits_define = if candidates.len() == 1 {
@ -785,11 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
"traits define"
},
action = if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
"implement".to_string()
},
action = action,
one_of_them = if candidates.len() == 1 {
"it"
} else {
@ -809,50 +805,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Get the `hir::Param` to verify whether it already has any bounds.
// We do this to avoid suggesting code that ends up as `T: FooBar`,
// instead we suggest `T: Foo + Bar` in that case.
let mut has_bounds = None;
let mut impl_trait = false;
if let Node::GenericParam(ref param) = hir.get(id) {
let kind = &param.kind;
if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = kind {
// We've found `fn foo(x: impl Trait)` instead of
// `fn foo<T>(x: T)`. We want to suggest the correct
// `fn foo(x: impl Trait + TraitBound)` instead of
// `fn foo<T: TraitBound>(x: T)`. (See #63706.)
impl_trait = true;
has_bounds = param.bounds.get(1);
} else {
has_bounds = param.bounds.get(0);
match hir.get(id) {
Node::GenericParam(ref param) => {
let mut impl_trait = false;
let has_bounds = if let hir::GenericParamKind::Type {
synthetic: Some(_), ..
} = &param.kind {
// We've found `fn foo(x: impl Trait)` instead of
// `fn foo<T>(x: T)`. We want to suggest the correct
// `fn foo(x: impl Trait + TraitBound)` instead of
// `fn foo<T: TraitBound>(x: T)`. (#63706)
impl_trait = true;
param.bounds.get(1)
} else {
param.bounds.get(0)
};
let sp = hir.span(id);
let sp = if let Some(first_bound) = has_bounds {
// `sp` only covers `T`, change it so that it covers
// `T:` when appropriate
sp.until(first_bound.span())
} else {
sp
};
// FIXME: contrast `t.def_id` against `param.bounds` to not suggest
// traits already there. That can happen when the cause is that
// we're in a const scope or associated function used as a method.
err.span_suggestions(
sp,
&message(format!(
"restrict type parameter `{}` with",
param.name.ident().as_str(),
)),
candidates.iter().map(|t| format!(
"{}{} {}{}",
param.name.ident().as_str(),
if impl_trait { " +" } else { ":" },
self.tcx.def_path_str(t.def_id),
if has_bounds.is_some() { " + "} else { "" },
)),
Applicability::MaybeIncorrect,
);
suggested = true;
}
Node::Item(hir::Item {
kind: hir::ItemKind::Trait(.., bounds, _), ident, ..
}) => {
let (sp, sep, article) = if bounds.is_empty() {
(ident.span.shrink_to_hi(), ":", "a")
} else {
(bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
};
err.span_suggestions(
sp,
&message(format!("add {} supertrait for", article)),
candidates.iter().map(|t| format!(
"{} {}",
sep,
self.tcx.def_path_str(t.def_id),
)),
Applicability::MaybeIncorrect,
);
suggested = true;
}
_ => {}
}
let sp = hir.span(id);
// `sp` only covers `T`, change it so that it covers `T:` when appropriate.
let sp = if let Some(first_bound) = has_bounds {
sp.until(first_bound.span())
} else {
sp
};
// FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
// already there. That can happen when the cause is that we're in a const
// scope or associated function used as a method.
err.span_suggestions(
sp,
&msg[..],
candidates.iter().map(|t| format!(
"{}{} {}{}",
param,
if impl_trait { " +" } else { ":" },
self.tcx.def_path_str(t.def_id),
if has_bounds.is_some() { " + " } else { "" },
)),
Applicability::MaybeIncorrect,
);
suggested = true;
}
};
}
if !suggested {
let mut msg = message(if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
"implement".to_string()
});
for (i, trait_info) in candidates.iter().enumerate() {
msg.push_str(&format!(
"\ncandidate #{}: `{}`",

View file

@ -0,0 +1,47 @@
// run-rustfix
// check-only
#[derive(Debug)]
struct Demo {
a: String
}
trait GetString {
fn get_a(&self) -> &String;
}
trait UseString: std::fmt::Debug + GetString {
fn use_string(&self) {
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
}
}
trait UseString2: GetString {
fn use_string(&self) {
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
}
}
impl GetString for Demo {
fn get_a(&self) -> &String {
&self.a
}
}
impl UseString for Demo {}
impl UseString2 for Demo {}
#[cfg(test)]
mod tests {
use crate::{Demo, UseString};
#[test]
fn it_works() {
let d = Demo { a: "test".to_string() };
d.use_string();
}
}
fn main() {}

View file

@ -0,0 +1,47 @@
// run-rustfix
// check-only
#[derive(Debug)]
struct Demo {
a: String
}
trait GetString {
fn get_a(&self) -> &String;
}
trait UseString: std::fmt::Debug {
fn use_string(&self) {
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
}
}
trait UseString2 {
fn use_string(&self) {
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
}
}
impl GetString for Demo {
fn get_a(&self) -> &String {
&self.a
}
}
impl UseString for Demo {}
impl UseString2 for Demo {}
#[cfg(test)]
mod tests {
use crate::{Demo, UseString};
#[test]
fn it_works() {
let d = Demo { a: "test".to_string() };
d.use_string();
}
}
fn main() {}

View file

@ -0,0 +1,27 @@
error[E0599]: no method named `get_a` found for type `&Self` in the current scope
--> $DIR/constrain-trait.rs:15:31
|
LL | println!("{:?}", self.get_a());
| ^^^^^ method not found in `&Self`
|
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `get_a`, perhaps you need to add another supertrait for it:
|
LL | trait UseString: std::fmt::Debug + GetString {
| ^^^^^^^^^^^
error[E0599]: no method named `get_a` found for type `&Self` in the current scope
--> $DIR/constrain-trait.rs:21:31
|
LL | println!("{:?}", self.get_a());
| ^^^^^ method not found in `&Self`
|
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `get_a`, perhaps you need to add a supertrait for it:
|
LL | trait UseString2: GetString {
| ^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0599`.