diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index bad74588d0c..58ea7a0edd5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1049,39 +1049,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), ); - let mut annotate_expr = |span: Span, prev_ty: Ty<'tcx>, self_ty: Ty<'tcx>| -> bool { - // We always look at the `E` type, because that's the only one affected by `?`. If the - // incorrect `Result` is because of the `T`, we'll get an E0308 on the whole - // expression, after the `?` has "unwrapped" the `T`. + + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let get_e_type = |prev_ty: Ty<'tcx>| -> Option> { let ty::Adt(def, args) = prev_ty.kind() else { - return false; + return None; }; let Some(arg) = args.get(1) else { - return false; + return None; }; if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { - return false; + return None; } - let can = if self - .infcx - .type_implements_trait( - self.tcx.get_diagnostic_item(sym::From).unwrap(), - [self_ty.into(), *arg], - obligation.param_env, - ) - .must_apply_modulo_regions() - { - "can" - } else { - "can't" - }; - err.span_label( - span, - format!("this {can} be annotated with `?` because it has type `{prev_ty}`"), - ); - true + Some(arg.as_type()?) }; + let mut chain = vec![]; + // The following logic is simlar to `point_at_chain`, but that's focused on associated types let mut expr = expr; while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { @@ -1089,9 +1075,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // let foo = bar.iter().map(mapper)?; // ------ ----------- expr = rcvr_expr; - if !annotate_expr(span, prev_ty, self_ty) { - break; - } + chain.push((span, prev_ty)); prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), @@ -1121,7 +1105,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), ); - annotate_expr(expr.span, prev_ty, self_ty); + chain.push((expr.span, prev_ty)); + + let mut prev = None; + for (span, err_ty) in chain.into_iter().rev() { + let err_ty = get_e_type(err_ty); + let err_ty = match (err_ty, prev) { + (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { + err_ty + } + (Some(err_ty), None) => err_ty, + _ => { + prev = err_ty; + continue; + } + }; + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, err_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } else { + err.span_label( + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); + } + prev = Some(err_ty); + } } fn report_const_param_not_wf( diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index 7b364580858..e5ccca2e5f7 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -3,18 +3,17 @@ fn foo() -> Result { //~ NOTE expected `String` because of this let x = test .split_whitespace() .next() - .ok_or_else(|| { //~ NOTE this can be annotated with `?` because it has type `Result<&str, &str>` + .ok_or_else(|| { //~ NOTE this has type `Result<_, &str>` "Couldn't split the test string" }); let one = x - .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), &str>` - .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + .map(|s| ()) + .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String` //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result` to implement `FromResidual>` @@ -22,15 +21,15 @@ fn foo() -> Result { //~ NOTE expected `String` because of this } fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this - let x = foo(); //~ NOTE this can be annotated with `?` because it has type `Result` + let x = foo(); //~ NOTE this has type `Result<_, String>` let one = x - .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), String>` + .map(|s| ()) .map_err(|_| ())?; //~ ERROR `?` couldn't convert the error to `String` //~^ NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` //~| NOTE in this expansion of desugaring of operator `?` - //~| NOTE this can't be annotated with `?` because it has type `Result<(), ()>` + //~| NOTE this can't be annotated with `?` because it has type `Result<_, ()>` //~| NOTE the trait `From<()>` is not implemented for `String` //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait //~| NOTE required for `Result<(), String>` to implement `FromResidual>` @@ -42,7 +41,7 @@ fn baz() -> Result { //~ NOTE expected `String` because of this let one = test .split_whitespace() .next() - .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<&str, ()>` + .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` "Couldn't split the test string"; })?; //~^ ERROR `?` couldn't convert the error to `String` diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index f6acbc6dd07..fc3b2e6b46b 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -8,16 +8,12 @@ LL | .ok_or_else(|| { | __________- LL | | "Couldn't split the test string" LL | | }); - | |__________- this can be annotated with `?` because it has type `Result<&str, &str>` -LL | let one = x -LL | .map(|s| ()) - | ----------- this can be annotated with `?` because it has type `Result<(), &str>` + | |__________- this has type `Result<_, &str>` +... LL | .map_err(|_| ()) - | --------------- this can't be annotated with `?` because it has type `Result<(), ()>` + | --------------- this can't be annotated with `?` because it has type `Result<_, ()>` LL | .map(|()| "")?; - | ------------^ the trait `From<()>` is not implemented for `String` - | | - | this can't be annotated with `?` because it has type `Result<&str, ()>` + | ^ the trait `From<()>` is not implemented for `String` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: @@ -30,19 +26,17 @@ LL | .map(|()| "")?; = note: required for `Result` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:28:25 + --> $DIR/question-mark-result-err-mismatch.rs:27:25 | LL | fn bar() -> Result<(), String> { | ------------------ expected `String` because of this LL | let x = foo(); - | ----- this can be annotated with `?` because it has type `Result` -LL | let one = x -LL | .map(|s| ()) - | ----------- this can be annotated with `?` because it has type `Result<(), String>` + | ----- this has type `Result<_, String>` +... LL | .map_err(|_| ())?; | ---------------^ the trait `From<()>` is not implemented for `String` | | - | this can't be annotated with `?` because it has type `Result<(), ()>` + | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: @@ -55,7 +49,7 @@ LL | .map_err(|_| ())?; = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` - --> $DIR/question-mark-result-err-mismatch.rs:47:11 + --> $DIR/question-mark-result-err-mismatch.rs:46:11 | LL | fn baz() -> Result { | ---------------------- expected `String` because of this @@ -66,7 +60,7 @@ LL | | "Couldn't split the test string"; LL | | })?; | | -^ the trait `From<()>` is not implemented for `String` | |__________| - | this can't be annotated with `?` because it has type `Result<&str, ()>` + | this can't be annotated with `?` because it has type `Result<_, ()>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: