Skip to content

Commit 7514258

Browse files
authored
Rollup merge of #153925 - estebank:issue-129269, r=jieyouxu
Provide better suggestions for inference errors on `.collect()?` When encountering an inference error when calling `.collect()` followed by `?`, detect the return type of the enclosing function and suggest `.collect::<Result<_, _>>()?` instead of `.collect::<Vec<_>>()?`: ``` error[E0283]: type annotations needed --> $DIR/question-mark-type-infer.rs:10:21 | LL | l.iter().map(f).collect()? | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` | = note: cannot satisfy `_: FromIterator<Result<i32, ()>>` note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider specifying the generic argument | LL | l.iter().map(f).collect::<Result<_, _>>()? | ++++++++++++++++ ``` On inference error in let binding, account for `?`: ``` error[E0282]: type annotations needed --> $DIR/question-mark-type-inference-in-chain.rs:31:9 | LL | let mut tags = lines.iter().map(|e| parse(e)).collect()?; | ^^^^^^^^ ... LL | tags.sort(); | ---- type must be known at this point | help: consider giving `tags` an explicit type | LL | let mut tags: Vec<_> = lines.iter().map(|e| parse(e)).collect()?; | ++++++++ ``` Address #129269.
2 parents 3a34544 + 774f232 commit 7514258

5 files changed

Lines changed: 262 additions & 16 deletions

File tree

compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
555555
def_id: _,
556556
generic_args,
557557
have_turbofish,
558+
hir_id,
558559
} => {
559560
let generics = self.tcx.generics_of(generics_def_id);
560561
let is_type = term.as_type().is_some();
@@ -577,7 +578,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
577578
let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
578579
== Some(generics_def_id)
579580
{
580-
"Vec<_>".to_string()
581+
if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(hir_id)
582+
&& let hir::ExprKind::Call(expr, _args) = expr.kind
583+
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
584+
&& let Res::Def(DefKind::AssocFn, def_id) = path.res
585+
&& let Some(try_trait) = self.tcx.lang_items().try_trait()
586+
&& try_trait == self.tcx.parent(def_id)
587+
&& let DefKind::Fn | DefKind::AssocFn =
588+
self.tcx.def_kind(body_def_id.to_def_id())
589+
&& let ret = self
590+
.tcx
591+
.fn_sig(body_def_id.to_def_id())
592+
.instantiate_identity()
593+
.skip_binder()
594+
.output()
595+
&& let ty::Adt(adt, _args) = ret.kind()
596+
&& let Some(sym::Option | sym::Result) =
597+
self.tcx.get_diagnostic_name(adt.did())
598+
{
599+
if let Some(sym::Option) = self.tcx.get_diagnostic_name(adt.did()) {
600+
"Option<_>".to_string()
601+
} else {
602+
"Result<_, _>".to_string()
603+
}
604+
} else {
605+
"Vec<_>".to_string()
606+
}
581607
} else {
582608
let mut p = fmt_printer(self, Namespace::TypeNS);
583609
p.comma_sep(generic_args.iter().copied().map(|arg| {
@@ -710,6 +736,7 @@ enum InferSourceKind<'tcx> {
710736
def_id: DefId,
711737
generic_args: &'tcx [GenericArg<'tcx>],
712738
have_turbofish: bool,
739+
hir_id: HirId,
713740
},
714741
FullyQualifiedMethodCall {
715742
receiver: &'tcx Expr<'tcx>,
@@ -1188,19 +1215,45 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
11881215

11891216
if let Some(ty) = self.opt_node_type(local.hir_id) {
11901217
if self.generic_arg_contains_target(ty.into()) {
1191-
match local.source {
1192-
LocalSource::Normal if local.ty.is_none() => {
1193-
self.update_infer_source(InferSource {
1194-
span: local.pat.span,
1195-
kind: InferSourceKind::LetBinding {
1196-
insert_span: local.pat.span.shrink_to_hi(),
1197-
pattern_name: local.pat.simple_ident(),
1198-
ty,
1199-
def_id: None,
1200-
},
1201-
})
1218+
fn get_did(
1219+
typeck_results: &TypeckResults<'_>,
1220+
expr: &hir::Expr<'_>,
1221+
) -> Option<DefId> {
1222+
match expr.kind {
1223+
hir::ExprKind::Match(expr, _, hir::MatchSource::TryDesugar(_))
1224+
if let hir::ExprKind::Call(_, [expr]) = expr.kind =>
1225+
{
1226+
get_did(typeck_results, expr)
1227+
}
1228+
hir::ExprKind::Call(base, _args)
1229+
if let hir::ExprKind::Path(path) = base.kind
1230+
&& let hir::QPath::Resolved(_, path) = path
1231+
&& let Res::Def(_, did) = path.res =>
1232+
{
1233+
Some(did)
1234+
}
1235+
hir::ExprKind::MethodCall(..)
1236+
if let Some(did) =
1237+
typeck_results.type_dependent_def_id(expr.hir_id) =>
1238+
{
1239+
Some(did)
1240+
}
1241+
_ => None,
12021242
}
1203-
_ => {}
1243+
}
1244+
1245+
if let LocalSource::Normal = local.source
1246+
&& local.ty.is_none()
1247+
{
1248+
self.update_infer_source(InferSource {
1249+
span: local.pat.span,
1250+
kind: InferSourceKind::LetBinding {
1251+
insert_span: local.pat.span.shrink_to_hi(),
1252+
pattern_name: local.pat.simple_ident(),
1253+
ty,
1254+
def_id: local.init.and_then(|expr| get_did(self.typeck_results, expr)),
1255+
},
1256+
});
12041257
}
12051258
}
12061259
}
@@ -1285,6 +1338,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
12851338
def_id,
12861339
generic_args,
12871340
have_turbofish,
1341+
hir_id: expr.hir_id,
12881342
},
12891343
});
12901344
}

tests/ui/inference/question-mark-type-infer.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ fn g() -> Result<Vec<i32>, ()> {
1010
l.iter().map(f).collect()?
1111
//~^ ERROR type annotations needed
1212
}
13+
fn h() -> Result<(), ()> {
14+
let l = [1, 2, 3, 4];
15+
// The resulting binding doesn't have a type, so we need to guess the `Ok` type too.
16+
let x = l.iter().map(f).collect()?;
17+
//~^ ERROR type annotations needed
18+
Ok(())
19+
}
20+
fn i() -> Result<(), ()> {
21+
let l = [1, 2, 3, 4];
22+
// The resulting binding already has a type, so we don't need to specify the `Ok` type.
23+
let x: Vec<i32> = l.iter().map(f).collect()?;
24+
//~^ ERROR type annotations needed
25+
Ok(())
26+
}
1327

1428
fn main() {
1529
g();

tests/ui/inference/question-mark-type-infer.stderr

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,37 @@ note: required by a bound in `collect`
99
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
1010
help: consider specifying the generic argument
1111
|
12-
LL | l.iter().map(f).collect::<Vec<_>>()?
13-
| ++++++++++
12+
LL | l.iter().map(f).collect::<Result<_, _>>()?
13+
| ++++++++++++++++
1414

15-
error: aborting due to 1 previous error
15+
error[E0283]: type annotations needed
16+
--> $DIR/question-mark-type-infer.rs:16:29
17+
|
18+
LL | let x = l.iter().map(f).collect()?;
19+
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
20+
|
21+
= note: cannot satisfy `_: FromIterator<Result<i32, ()>>`
22+
note: required by a bound in `collect`
23+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
24+
help: consider specifying the generic argument
25+
|
26+
LL | let x = l.iter().map(f).collect::<Result<_, _>>()?;
27+
| ++++++++++++++++
28+
29+
error[E0283]: type annotations needed
30+
--> $DIR/question-mark-type-infer.rs:23:39
31+
|
32+
LL | let x: Vec<i32> = l.iter().map(f).collect()?;
33+
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
34+
|
35+
= note: cannot satisfy `_: FromIterator<Result<i32, ()>>`
36+
note: required by a bound in `collect`
37+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
38+
help: consider specifying the generic argument
39+
|
40+
LL | let x: Vec<i32> = l.iter().map(f).collect::<Result<_, _>>()?;
41+
| ++++++++++++++++
42+
43+
error: aborting due to 3 previous errors
1644

1745
For more information about this error, try `rustc --explain E0283`.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// #129269
2+
use std::fmt::Display;
3+
4+
#[derive(Debug)]
5+
struct AnotherError;
6+
type Result<T, E = AnotherError> = core::result::Result<T, E>;
7+
8+
#[derive(Debug)]
9+
pub struct Error;
10+
11+
impl From<AnotherError> for Error {
12+
fn from(_: AnotherError) -> Self { Error }
13+
}
14+
15+
impl std::error::Error for Error {}
16+
17+
impl Display for Error {
18+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19+
todo!()
20+
}
21+
}
22+
23+
#[derive(Ord, PartialEq, PartialOrd, Eq)]
24+
pub struct Version {}
25+
26+
fn parse(_s: &str) -> std::result::Result<Version, Error> {
27+
todo!()
28+
}
29+
30+
pub fn error1(lines: &[&str]) -> Result<Vec<Version>> {
31+
let mut tags = lines.iter().map(|e| parse(e)).collect()?;
32+
//~^ ERROR: type annotations needed
33+
//~| HELP: consider giving `tags` an explicit type
34+
35+
tags.sort(); //~ NOTE: type must be known at this point
36+
37+
Ok(tags)
38+
}
39+
40+
pub fn error2(lines: &[&str]) -> Result<Vec<Version>> {
41+
let mut tags: Vec<Version> = lines.iter().map(|e| parse(e)).collect()?;
42+
//~^ ERROR: type annotations needed
43+
//~| NOTE: cannot infer type of the type parameter `B`
44+
//~| NOTE: cannot satisfy `_: FromIterator<std::result::Result<Version, Error>>`
45+
//~| NOTE: required by a bound in `collect`
46+
//~| HELP: consider specifying the generic argument
47+
tags.sort();
48+
49+
Ok(tags)
50+
}
51+
52+
pub fn error3(lines: &[&str]) -> Result<Vec<Version>> {
53+
let mut tags = lines.iter().map(|e| parse(e)).collect::<Vec<_>>()?;
54+
//~^ ERROR: the `?` operator can only be applied to values that implement `Try`
55+
//~| NOTE: the `?` operator cannot be applied to type `Vec<std::result::Result<Version, Error>>`
56+
//~| HELP: the nightly-only, unstable trait `Try` is not implemented
57+
//~| NOTE: in this expansion of desugaring of operator `?`
58+
//~| NOTE: in this expansion of desugaring of operator `?`
59+
tags.sort();
60+
61+
Ok(tags)
62+
}
63+
64+
pub fn error4(lines: &[&str]) -> Result<Vec<Version>> {
65+
let mut tags = lines
66+
//~^ NOTE: this expression has type `&[&str]`
67+
.iter()
68+
//~^ NOTE: `Iterator::Item` is `&&str` here
69+
.map(|e| parse(e))
70+
//~^ NOTE: the method call chain might not have had the expected associated types
71+
//~| NOTE: `Iterator::Item` changed to `Result<Version, Error>` here
72+
.collect::<Result<Vec<Version>>>()?;
73+
//~^ ERROR: a value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result<Version, Error>`
74+
//~| NOTE: value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from `std::iter::Iterator<Item=std::result::Result<Version, Error>>`
75+
//~| NOTE: required by a bound introduced by this call
76+
//~| HELP: the trait
77+
//~| HELP: for that trait implementation, expected `AnotherError`, found `Error`
78+
//~| NOTE: required by a bound in `collect`
79+
tags.sort();
80+
81+
Ok(tags)
82+
}
83+
84+
fn main() {}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/question-mark-type-inference-in-chain.rs:31:9
3+
|
4+
LL | let mut tags = lines.iter().map(|e| parse(e)).collect()?;
5+
| ^^^^^^^^
6+
...
7+
LL | tags.sort();
8+
| ---- type must be known at this point
9+
|
10+
help: consider giving `tags` an explicit type
11+
|
12+
LL | let mut tags: Vec<_> = lines.iter().map(|e| parse(e)).collect()?;
13+
| ++++++++
14+
15+
error[E0283]: type annotations needed
16+
--> $DIR/question-mark-type-inference-in-chain.rs:41:65
17+
|
18+
LL | let mut tags: Vec<Version> = lines.iter().map(|e| parse(e)).collect()?;
19+
| ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect`
20+
|
21+
= note: cannot satisfy `_: FromIterator<std::result::Result<Version, Error>>`
22+
note: required by a bound in `collect`
23+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
24+
help: consider specifying the generic argument
25+
|
26+
LL | let mut tags: Vec<Version> = lines.iter().map(|e| parse(e)).collect::<Result<_, _>>()?;
27+
| ++++++++++++++++
28+
29+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
30+
--> $DIR/question-mark-type-inference-in-chain.rs:53:20
31+
|
32+
LL | let mut tags = lines.iter().map(|e| parse(e)).collect::<Vec<_>>()?;
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `Vec<std::result::Result<Version, Error>>`
34+
|
35+
= help: the nightly-only, unstable trait `Try` is not implemented for `Vec<std::result::Result<Version, Error>>`
36+
37+
error[E0277]: a value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result<Version, Error>`
38+
--> $DIR/question-mark-type-inference-in-chain.rs:72:20
39+
|
40+
LL | .collect::<Result<Vec<Version>>>()?;
41+
| ------- ^^^^^^^^^^^^^^^^^^^^ value of type `std::result::Result<Vec<Version>, AnotherError>` cannot be built from `std::iter::Iterator<Item=std::result::Result<Version, Error>>`
42+
| |
43+
| required by a bound introduced by this call
44+
|
45+
help: the trait `FromIterator<std::result::Result<_, Error>>` is not implemented for `std::result::Result<Vec<Version>, AnotherError>`
46+
but trait `FromIterator<std::result::Result<_, AnotherError>>` is implemented for it
47+
--> $SRC_DIR/core/src/result.rs:LL:COL
48+
= help: for that trait implementation, expected `AnotherError`, found `Error`
49+
note: the method call chain might not have had the expected associated types
50+
--> $DIR/question-mark-type-inference-in-chain.rs:69:10
51+
|
52+
LL | let mut tags = lines
53+
| ----- this expression has type `&[&str]`
54+
LL |
55+
LL | .iter()
56+
| ------ `Iterator::Item` is `&&str` here
57+
LL |
58+
LL | .map(|e| parse(e))
59+
| ^^^^^^^^^^^^^^^^^ `Iterator::Item` changed to `Result<Version, Error>` here
60+
note: required by a bound in `collect`
61+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
62+
63+
error: aborting due to 4 previous errors
64+
65+
Some errors have detailed explanations: E0277, E0282, E0283.
66+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)