Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ef9d6ee

Browse files
authoredOct 15, 2019
Rollup merge of rust-lang#65242 - estebank:contrain-trait-sugg, r=varkor
Fix suggestion to constrain trait for method to be found Fix rust-lang#65044.
2 parents bbf4eb3 + dee53d7 commit ef9d6ee

File tree

4 files changed

+191
-43
lines changed

4 files changed

+191
-43
lines changed
 

‎src/librustc_typeck/check/method/suggest.rs

+70-43
Original file line numberDiff line numberDiff line change
@@ -777,19 +777,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
777777
} else {
778778
"items from traits can only be used if the trait is implemented and in scope"
779779
});
780-
let mut msg = format!(
780+
let message = |action| format!(
781781
"the following {traits_define} an item `{name}`, perhaps you need to {action} \
782782
{one_of_them}:",
783783
traits_define = if candidates.len() == 1 {
784784
"trait defines"
785785
} else {
786786
"traits define"
787787
},
788-
action = if let Some(param) = param_type {
789-
format!("restrict type parameter `{}` with", param)
790-
} else {
791-
"implement".to_string()
792-
},
788+
action = action,
793789
one_of_them = if candidates.len() == 1 {
794790
"it"
795791
} else {
@@ -809,50 +805,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
809805
// Get the `hir::Param` to verify whether it already has any bounds.
810806
// We do this to avoid suggesting code that ends up as `T: FooBar`,
811807
// instead we suggest `T: Foo + Bar` in that case.
812-
let mut has_bounds = None;
813-
let mut impl_trait = false;
814-
if let Node::GenericParam(ref param) = hir.get(id) {
815-
let kind = &param.kind;
816-
if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = kind {
817-
// We've found `fn foo(x: impl Trait)` instead of
818-
// `fn foo<T>(x: T)`. We want to suggest the correct
819-
// `fn foo(x: impl Trait + TraitBound)` instead of
820-
// `fn foo<T: TraitBound>(x: T)`. (See #63706.)
821-
impl_trait = true;
822-
has_bounds = param.bounds.get(1);
823-
} else {
824-
has_bounds = param.bounds.get(0);
808+
match hir.get(id) {
809+
Node::GenericParam(ref param) => {
810+
let mut impl_trait = false;
811+
let has_bounds = if let hir::GenericParamKind::Type {
812+
synthetic: Some(_), ..
813+
} = &param.kind {
814+
// We've found `fn foo(x: impl Trait)` instead of
815+
// `fn foo<T>(x: T)`. We want to suggest the correct
816+
// `fn foo(x: impl Trait + TraitBound)` instead of
817+
// `fn foo<T: TraitBound>(x: T)`. (#63706)
818+
impl_trait = true;
819+
param.bounds.get(1)
820+
} else {
821+
param.bounds.get(0)
822+
};
823+
let sp = hir.span(id);
824+
let sp = if let Some(first_bound) = has_bounds {
825+
// `sp` only covers `T`, change it so that it covers
826+
// `T:` when appropriate
827+
sp.until(first_bound.span())
828+
} else {
829+
sp
830+
};
831+
// FIXME: contrast `t.def_id` against `param.bounds` to not suggest
832+
// traits already there. That can happen when the cause is that
833+
// we're in a const scope or associated function used as a method.
834+
err.span_suggestions(
835+
sp,
836+
&message(format!(
837+
"restrict type parameter `{}` with",
838+
param.name.ident().as_str(),
839+
)),
840+
candidates.iter().map(|t| format!(
841+
"{}{} {}{}",
842+
param.name.ident().as_str(),
843+
if impl_trait { " +" } else { ":" },
844+
self.tcx.def_path_str(t.def_id),
845+
if has_bounds.is_some() { " + "} else { "" },
846+
)),
847+
Applicability::MaybeIncorrect,
848+
);
849+
suggested = true;
850+
}
851+
Node::Item(hir::Item {
852+
kind: hir::ItemKind::Trait(.., bounds, _), ident, ..
853+
}) => {
854+
let (sp, sep, article) = if bounds.is_empty() {
855+
(ident.span.shrink_to_hi(), ":", "a")
856+
} else {
857+
(bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
858+
};
859+
err.span_suggestions(
860+
sp,
861+
&message(format!("add {} supertrait for", article)),
862+
candidates.iter().map(|t| format!(
863+
"{} {}",
864+
sep,
865+
self.tcx.def_path_str(t.def_id),
866+
)),
867+
Applicability::MaybeIncorrect,
868+
);
869+
suggested = true;
825870
}
871+
_ => {}
826872
}
827-
let sp = hir.span(id);
828-
// `sp` only covers `T`, change it so that it covers `T:` when appropriate.
829-
let sp = if let Some(first_bound) = has_bounds {
830-
sp.until(first_bound.span())
831-
} else {
832-
sp
833-
};
834-
835-
// FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
836-
// already there. That can happen when the cause is that we're in a const
837-
// scope or associated function used as a method.
838-
err.span_suggestions(
839-
sp,
840-
&msg[..],
841-
candidates.iter().map(|t| format!(
842-
"{}{} {}{}",
843-
param,
844-
if impl_trait { " +" } else { ":" },
845-
self.tcx.def_path_str(t.def_id),
846-
if has_bounds.is_some() { " + " } else { "" },
847-
)),
848-
Applicability::MaybeIncorrect,
849-
);
850-
suggested = true;
851873
}
852874
};
853875
}
854876

855877
if !suggested {
878+
let mut msg = message(if let Some(param) = param_type {
879+
format!("restrict type parameter `{}` with", param)
880+
} else {
881+
"implement".to_string()
882+
});
856883
for (i, trait_info) in candidates.iter().enumerate() {
857884
msg.push_str(&format!(
858885
"\ncandidate #{}: `{}`",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// run-rustfix
2+
// check-only
3+
4+
#[derive(Debug)]
5+
struct Demo {
6+
a: String
7+
}
8+
9+
trait GetString {
10+
fn get_a(&self) -> &String;
11+
}
12+
13+
trait UseString: std::fmt::Debug + GetString {
14+
fn use_string(&self) {
15+
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
16+
}
17+
}
18+
19+
trait UseString2: GetString {
20+
fn use_string(&self) {
21+
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
22+
}
23+
}
24+
25+
impl GetString for Demo {
26+
fn get_a(&self) -> &String {
27+
&self.a
28+
}
29+
}
30+
31+
impl UseString for Demo {}
32+
impl UseString2 for Demo {}
33+
34+
35+
#[cfg(test)]
36+
mod tests {
37+
use crate::{Demo, UseString};
38+
39+
#[test]
40+
fn it_works() {
41+
let d = Demo { a: "test".to_string() };
42+
d.use_string();
43+
}
44+
}
45+
46+
47+
fn main() {}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// run-rustfix
2+
// check-only
3+
4+
#[derive(Debug)]
5+
struct Demo {
6+
a: String
7+
}
8+
9+
trait GetString {
10+
fn get_a(&self) -> &String;
11+
}
12+
13+
trait UseString: std::fmt::Debug {
14+
fn use_string(&self) {
15+
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
16+
}
17+
}
18+
19+
trait UseString2 {
20+
fn use_string(&self) {
21+
println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
22+
}
23+
}
24+
25+
impl GetString for Demo {
26+
fn get_a(&self) -> &String {
27+
&self.a
28+
}
29+
}
30+
31+
impl UseString for Demo {}
32+
impl UseString2 for Demo {}
33+
34+
35+
#[cfg(test)]
36+
mod tests {
37+
use crate::{Demo, UseString};
38+
39+
#[test]
40+
fn it_works() {
41+
let d = Demo { a: "test".to_string() };
42+
d.use_string();
43+
}
44+
}
45+
46+
47+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0599]: no method named `get_a` found for type `&Self` in the current scope
2+
--> $DIR/constrain-trait.rs:15:31
3+
|
4+
LL | println!("{:?}", self.get_a());
5+
| ^^^^^ method not found in `&Self`
6+
|
7+
= help: items from traits can only be used if the type parameter is bounded by the trait
8+
help: the following trait defines an item `get_a`, perhaps you need to add another supertrait for it:
9+
|
10+
LL | trait UseString: std::fmt::Debug + GetString {
11+
| ^^^^^^^^^^^
12+
13+
error[E0599]: no method named `get_a` found for type `&Self` in the current scope
14+
--> $DIR/constrain-trait.rs:21:31
15+
|
16+
LL | println!("{:?}", self.get_a());
17+
| ^^^^^ method not found in `&Self`
18+
|
19+
= help: items from traits can only be used if the type parameter is bounded by the trait
20+
help: the following trait defines an item `get_a`, perhaps you need to add a supertrait for it:
21+
|
22+
LL | trait UseString2: GetString {
23+
| ^^^^^^^^^^^
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)
Please sign in to comment.