diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 629bd30d88e8e..16b0e96cde0fa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -555,6 +555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { def_id: _, generic_args, have_turbofish, + hir_id, } => { let generics = self.tcx.generics_of(generics_def_id); let is_type = term.as_type().is_some(); @@ -577,7 +578,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(generics_def_id) { - "Vec<_>".to_string() + if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(hir_id) + && let hir::ExprKind::Call(expr, _args) = expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind + && let Res::Def(DefKind::AssocFn, def_id) = path.res + && let Some(try_trait) = self.tcx.lang_items().try_trait() + && try_trait == self.tcx.parent(def_id) + && let DefKind::Fn | DefKind::AssocFn = + self.tcx.def_kind(body_def_id.to_def_id()) + && let ret = self + .tcx + .fn_sig(body_def_id.to_def_id()) + .instantiate_identity() + .skip_binder() + .output() + && let ty::Adt(adt, _args) = ret.kind() + && let Some(sym::Option | sym::Result) = + self.tcx.get_diagnostic_name(adt.did()) + { + if let Some(sym::Option) = self.tcx.get_diagnostic_name(adt.did()) { + "Option<_>".to_string() + } else { + "Result<_, _>".to_string() + } + } else { + "Vec<_>".to_string() + } } else { let mut p = fmt_printer(self, Namespace::TypeNS); p.comma_sep(generic_args.iter().copied().map(|arg| { @@ -710,6 +736,7 @@ enum InferSourceKind<'tcx> { def_id: DefId, generic_args: &'tcx [GenericArg<'tcx>], have_turbofish: bool, + hir_id: HirId, }, FullyQualifiedMethodCall { receiver: &'tcx Expr<'tcx>, @@ -1188,19 +1215,45 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { if let Some(ty) = self.opt_node_type(local.hir_id) { if self.generic_arg_contains_target(ty.into()) { - match local.source { - LocalSource::Normal if local.ty.is_none() => { - self.update_infer_source(InferSource { - span: local.pat.span, - kind: InferSourceKind::LetBinding { - insert_span: local.pat.span.shrink_to_hi(), - pattern_name: local.pat.simple_ident(), - ty, - def_id: None, - }, - }) + fn get_did( + typeck_results: &TypeckResults<'_>, + expr: &hir::Expr<'_>, + ) -> Option { + match expr.kind { + hir::ExprKind::Match(expr, _, hir::MatchSource::TryDesugar(_)) + if let hir::ExprKind::Call(_, [expr]) = expr.kind => + { + get_did(typeck_results, expr) + } + hir::ExprKind::Call(base, _args) + if let hir::ExprKind::Path(path) = base.kind + && let hir::QPath::Resolved(_, path) = path + && let Res::Def(_, did) = path.res => + { + Some(did) + } + hir::ExprKind::MethodCall(..) + if let Some(did) = + typeck_results.type_dependent_def_id(expr.hir_id) => + { + Some(did) + } + _ => None, } - _ => {} + } + + if let LocalSource::Normal = local.source + && local.ty.is_none() + { + self.update_infer_source(InferSource { + span: local.pat.span, + kind: InferSourceKind::LetBinding { + insert_span: local.pat.span.shrink_to_hi(), + pattern_name: local.pat.simple_ident(), + ty, + def_id: local.init.and_then(|expr| get_did(self.typeck_results, expr)), + }, + }); } } } @@ -1285,6 +1338,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { def_id, generic_args, have_turbofish, + hir_id: expr.hir_id, }, }); } diff --git a/tests/ui/inference/question-mark-type-infer.rs b/tests/ui/inference/question-mark-type-infer.rs index 10560f85ed480..b04868c5324ff 100644 --- a/tests/ui/inference/question-mark-type-infer.rs +++ b/tests/ui/inference/question-mark-type-infer.rs @@ -10,6 +10,20 @@ fn g() -> Result, ()> { l.iter().map(f).collect()? //~^ ERROR type annotations needed } +fn h() -> Result<(), ()> { + let l = [1, 2, 3, 4]; + // The resulting binding doesn't have a type, so we need to guess the `Ok` type too. + let x = l.iter().map(f).collect()?; + //~^ ERROR type annotations needed + Ok(()) +} +fn i() -> Result<(), ()> { + let l = [1, 2, 3, 4]; + // The resulting binding already has a type, so we don't need to specify the `Ok` type. + let x: Vec = l.iter().map(f).collect()?; + //~^ ERROR type annotations needed + Ok(()) +} fn main() { g(); diff --git a/tests/ui/inference/question-mark-type-infer.stderr b/tests/ui/inference/question-mark-type-infer.stderr index 45303a2c87ff0..9e1232cb4a6fa 100644 --- a/tests/ui/inference/question-mark-type-infer.stderr +++ b/tests/ui/inference/question-mark-type-infer.stderr @@ -9,9 +9,37 @@ 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::>()? - | ++++++++++ +LL | l.iter().map(f).collect::>()? + | ++++++++++++++++ -error: aborting due to 1 previous error +error[E0283]: type annotations needed + --> $DIR/question-mark-type-infer.rs:16:29 + | +LL | let x = l.iter().map(f).collect()?; + | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` + | + = note: cannot satisfy `_: FromIterator>` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider specifying the generic argument + | +LL | let x = l.iter().map(f).collect::>()?; + | ++++++++++++++++ + +error[E0283]: type annotations needed + --> $DIR/question-mark-type-infer.rs:23:39 + | +LL | let x: Vec = l.iter().map(f).collect()?; + | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` + | + = note: cannot satisfy `_: FromIterator>` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider specifying the generic argument + | +LL | let x: Vec = l.iter().map(f).collect::>()?; + | ++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.rs b/tests/ui/inference/question-mark-type-inference-in-chain.rs new file mode 100644 index 0000000000000..47c41e52b9acc --- /dev/null +++ b/tests/ui/inference/question-mark-type-inference-in-chain.rs @@ -0,0 +1,84 @@ +// #129269 +use std::fmt::Display; + +#[derive(Debug)] +struct AnotherError; +type Result = core::result::Result; + +#[derive(Debug)] +pub struct Error; + +impl From for Error { + fn from(_: AnotherError) -> Self { Error } +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +#[derive(Ord, PartialEq, PartialOrd, Eq)] +pub struct Version {} + +fn parse(_s: &str) -> std::result::Result { + todo!() +} + +pub fn error1(lines: &[&str]) -> Result> { + let mut tags = lines.iter().map(|e| parse(e)).collect()?; + //~^ ERROR: type annotations needed + //~| HELP: consider giving `tags` an explicit type + + tags.sort(); //~ NOTE: type must be known at this point + + Ok(tags) +} + +pub fn error2(lines: &[&str]) -> Result> { + let mut tags: Vec = lines.iter().map(|e| parse(e)).collect()?; + //~^ ERROR: type annotations needed + //~| NOTE: cannot infer type of the type parameter `B` + //~| NOTE: cannot satisfy `_: FromIterator>` + //~| NOTE: required by a bound in `collect` + //~| HELP: consider specifying the generic argument + tags.sort(); + + Ok(tags) +} + +pub fn error3(lines: &[&str]) -> Result> { + let mut tags = lines.iter().map(|e| parse(e)).collect::>()?; + //~^ ERROR: the `?` operator can only be applied to values that implement `Try` + //~| NOTE: the `?` operator cannot be applied to type `Vec>` + //~| HELP: the nightly-only, unstable trait `Try` is not implemented + //~| NOTE: in this expansion of desugaring of operator `?` + //~| NOTE: in this expansion of desugaring of operator `?` + tags.sort(); + + Ok(tags) +} + +pub fn error4(lines: &[&str]) -> Result> { + let mut tags = lines + //~^ NOTE: this expression has type `&[&str]` + .iter() + //~^ NOTE: `Iterator::Item` is `&&str` here + .map(|e| parse(e)) + //~^ NOTE: the method call chain might not have had the expected associated types + //~| NOTE: `Iterator::Item` changed to `Result` here + .collect::>>()?; + //~^ ERROR: a value of type `std::result::Result, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result` + //~| NOTE: value of type `std::result::Result, AnotherError>` cannot be built from `std::iter::Iterator>` + //~| NOTE: required by a bound introduced by this call + //~| HELP: the trait + //~| HELP: for that trait implementation, expected `AnotherError`, found `Error` + //~| NOTE: required by a bound in `collect` + tags.sort(); + + Ok(tags) +} + +fn main() {} diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.stderr b/tests/ui/inference/question-mark-type-inference-in-chain.stderr new file mode 100644 index 0000000000000..0c11cdccd174f --- /dev/null +++ b/tests/ui/inference/question-mark-type-inference-in-chain.stderr @@ -0,0 +1,66 @@ +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()?; + | ++++++++ + +error[E0283]: type annotations needed + --> $DIR/question-mark-type-inference-in-chain.rs:41:65 + | +LL | let mut tags: Vec = lines.iter().map(|e| parse(e)).collect()?; + | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` + | + = note: cannot satisfy `_: FromIterator>` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider specifying the generic argument + | +LL | let mut tags: Vec = lines.iter().map(|e| parse(e)).collect::>()?; + | ++++++++++++++++ + +error[E0277]: the `?` operator can only be applied to values that implement `Try` + --> $DIR/question-mark-type-inference-in-chain.rs:53:20 + | +LL | let mut tags = lines.iter().map(|e| parse(e)).collect::>()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `Vec>` + | + = help: the nightly-only, unstable trait `Try` is not implemented for `Vec>` + +error[E0277]: a value of type `std::result::Result, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result` + --> $DIR/question-mark-type-inference-in-chain.rs:72:20 + | +LL | .collect::>>()?; + | ------- ^^^^^^^^^^^^^^^^^^^^ value of type `std::result::Result, AnotherError>` cannot be built from `std::iter::Iterator>` + | | + | required by a bound introduced by this call + | +help: the trait `FromIterator>` is not implemented for `std::result::Result, AnotherError>` + but trait `FromIterator>` is implemented for it + --> $SRC_DIR/core/src/result.rs:LL:COL + = help: for that trait implementation, expected `AnotherError`, found `Error` +note: the method call chain might not have had the expected associated types + --> $DIR/question-mark-type-inference-in-chain.rs:69:10 + | +LL | let mut tags = lines + | ----- this expression has type `&[&str]` +LL | +LL | .iter() + | ------ `Iterator::Item` is `&&str` here +LL | +LL | .map(|e| parse(e)) + | ^^^^^^^^^^^^^^^^^ `Iterator::Item` changed to `Result` here +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0282, E0283. +For more information about an error, try `rustc --explain E0277`.