|
1 | 1 | use crate::check::coercion::{AsCoercionSite, CoerceMany};
|
2 | 2 | use crate::check::{Diverges, Expectation, FnCtxt, Needs};
|
| 3 | +use rustc_errors::{Applicability, DiagnosticBuilder}; |
3 | 4 | use rustc_hir::{self as hir, ExprKind};
|
4 | 5 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
5 | 6 | use rustc_infer::traits::Obligation;
|
6 | 7 | use rustc_middle::ty::{self, ToPredicate, Ty, TyS};
|
7 |
| -use rustc_span::Span; |
| 8 | +use rustc_span::{MultiSpan, Span}; |
8 | 9 | use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
9 | 10 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
10 | 11 | use rustc_trait_selection::traits::{
|
@@ -206,7 +207,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
206 | 207 | ),
|
207 | 208 | };
|
208 | 209 | let cause = self.cause(span, code);
|
209 |
| - coercion.coerce(self, &cause, &arm.body, arm_ty); |
| 210 | + let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { |
| 211 | + Some(ret_coercion) if self.in_tail_expr => { |
| 212 | + let ret_ty = ret_coercion.borrow().expected_ty(); |
| 213 | + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); |
| 214 | + self.can_coerce(arm_ty, ret_ty) |
| 215 | + && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty)) |
| 216 | + // The match arms need to unify for the case of `impl Trait`. |
| 217 | + && !matches!(ret_ty.kind(), ty::Opaque(..)) |
| 218 | + } |
| 219 | + _ => false, |
| 220 | + }; |
| 221 | + |
| 222 | + // This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`. |
| 223 | + // We use it this way to be able to expand on the potential error and detect when a |
| 224 | + // `match` tail statement could be a tail expression instead. If so, we suggest |
| 225 | + // removing the stray semicolon. |
| 226 | + coercion.coerce_inner( |
| 227 | + self, |
| 228 | + &cause, |
| 229 | + Some(&arm.body), |
| 230 | + arm_ty, |
| 231 | + Some(&mut |err: &mut DiagnosticBuilder<'_>| { |
| 232 | + if let (Expectation::IsLast(stmt), Some(ret), true) = |
| 233 | + (orig_expected, self.ret_type_span, can_coerce_to_return_ty) |
| 234 | + { |
| 235 | + let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); |
| 236 | + let mut ret_span: MultiSpan = semi_span.into(); |
| 237 | + ret_span.push_span_label( |
| 238 | + expr.span, |
| 239 | + "this could be implicitly returned but it is a statement, not a \ |
| 240 | + tail expression" |
| 241 | + .to_owned(), |
| 242 | + ); |
| 243 | + ret_span.push_span_label( |
| 244 | + ret, |
| 245 | + "the `match` arms can conform to this return type".to_owned(), |
| 246 | + ); |
| 247 | + ret_span.push_span_label( |
| 248 | + semi_span, |
| 249 | + "the `match` is a statement because of this semicolon, consider \ |
| 250 | + removing it" |
| 251 | + .to_owned(), |
| 252 | + ); |
| 253 | + err.span_note( |
| 254 | + ret_span, |
| 255 | + "you might have meant to return the `match` expression", |
| 256 | + ); |
| 257 | + err.tool_only_span_suggestion( |
| 258 | + semi_span, |
| 259 | + "remove this semicolon", |
| 260 | + String::new(), |
| 261 | + Applicability::MaybeIncorrect, |
| 262 | + ); |
| 263 | + } |
| 264 | + }), |
| 265 | + false, |
| 266 | + ); |
| 267 | + |
210 | 268 | other_arms.push(arm_span);
|
211 | 269 | if other_arms.len() > 5 {
|
212 | 270 | other_arms.remove(0);
|
|
0 commit comments