|
1 | 1 | use crate::coercion::{AsCoercionSite, CoerceMany}; |
2 | 2 | use crate::{Diverges, Expectation, FnCtxt, Needs}; |
3 | | -use rustc_errors::{Applicability, MultiSpan}; |
| 3 | +use rustc_errors::{Applicability, Diagnostic, MultiSpan}; |
4 | 4 | use rustc_hir::{self as hir, ExprKind}; |
5 | 5 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; |
6 | 6 | use rustc_infer::traits::Obligation; |
@@ -137,55 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
137 | 137 | Some(&arm.body), |
138 | 138 | arm_ty, |
139 | 139 | Some(&mut |err| { |
140 | | - let Some(ret) = self |
141 | | - .tcx |
142 | | - .hir() |
143 | | - .find_by_def_id(self.body_id.owner.def_id) |
144 | | - .and_then(|owner| owner.fn_decl()) |
145 | | - .map(|decl| decl.output.span()) |
146 | | - else { return; }; |
147 | | - let Expectation::IsLast(stmt) = orig_expected else { |
148 | | - return |
149 | | - }; |
150 | | - let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { |
151 | | - Some(ret_coercion) if self.in_tail_expr => { |
152 | | - let ret_ty = ret_coercion.borrow().expected_ty(); |
153 | | - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); |
154 | | - self.can_coerce(arm_ty, ret_ty) |
155 | | - && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) |
156 | | - // The match arms need to unify for the case of `impl Trait`. |
157 | | - && !matches!(ret_ty.kind(), ty::Opaque(..)) |
158 | | - } |
159 | | - _ => false, |
160 | | - }; |
161 | | - if !can_coerce_to_return_ty { |
162 | | - return; |
163 | | - } |
164 | | - |
165 | | - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); |
166 | | - let mut ret_span: MultiSpan = semi_span.into(); |
167 | | - ret_span.push_span_label( |
168 | | - expr.span, |
169 | | - "this could be implicitly returned but it is a statement, not a \ |
170 | | - tail expression", |
171 | | - ); |
172 | | - ret_span |
173 | | - .push_span_label(ret, "the `match` arms can conform to this return type"); |
174 | | - ret_span.push_span_label( |
175 | | - semi_span, |
176 | | - "the `match` is a statement because of this semicolon, consider \ |
177 | | - removing it", |
178 | | - ); |
179 | | - err.span_note( |
180 | | - ret_span, |
181 | | - "you might have meant to return the `match` expression", |
182 | | - ); |
183 | | - err.tool_only_span_suggestion( |
184 | | - semi_span, |
185 | | - "remove this semicolon", |
186 | | - "", |
187 | | - Applicability::MaybeIncorrect, |
188 | | - ); |
| 140 | + self.suggest_removing_semicolon_for_coerce( |
| 141 | + err, |
| 142 | + expr, |
| 143 | + orig_expected, |
| 144 | + arm_ty, |
| 145 | + prior_arm, |
| 146 | + ) |
189 | 147 | }), |
190 | 148 | false, |
191 | 149 | ); |
@@ -219,6 +177,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
219 | 177 | coercion.complete(self) |
220 | 178 | } |
221 | 179 |
|
| 180 | + fn suggest_removing_semicolon_for_coerce( |
| 181 | + &self, |
| 182 | + diag: &mut Diagnostic, |
| 183 | + expr: &hir::Expr<'tcx>, |
| 184 | + expectation: Expectation<'tcx>, |
| 185 | + arm_ty: Ty<'tcx>, |
| 186 | + prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>, |
| 187 | + ) { |
| 188 | + let hir = self.tcx.hir(); |
| 189 | + |
| 190 | + // First, check that we're actually in the tail of a function. |
| 191 | + let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) = |
| 192 | + hir.get(self.body_id) else { return; }; |
| 193 | + let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. }) |
| 194 | + = block.innermost_block().stmts.last() else { return; }; |
| 195 | + if last_expr.hir_id != expr.hir_id { |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + // Next, make sure that we have no type expectation. |
| 200 | + let Some(ret) = hir |
| 201 | + .find_by_def_id(self.body_id.owner.def_id) |
| 202 | + .and_then(|owner| owner.fn_decl()) |
| 203 | + .map(|decl| decl.output.span()) else { return; }; |
| 204 | + let Expectation::IsLast(stmt) = expectation else { |
| 205 | + return; |
| 206 | + }; |
| 207 | + |
| 208 | + let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { |
| 209 | + Some(ret_coercion) => { |
| 210 | + let ret_ty = ret_coercion.borrow().expected_ty(); |
| 211 | + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); |
| 212 | + self.can_coerce(arm_ty, ret_ty) |
| 213 | + && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) |
| 214 | + // The match arms need to unify for the case of `impl Trait`. |
| 215 | + && !matches!(ret_ty.kind(), ty::Opaque(..)) |
| 216 | + } |
| 217 | + _ => false, |
| 218 | + }; |
| 219 | + if !can_coerce_to_return_ty { |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); |
| 224 | + let mut ret_span: MultiSpan = semi_span.into(); |
| 225 | + ret_span.push_span_label( |
| 226 | + expr.span, |
| 227 | + "this could be implicitly returned but it is a statement, not a \ |
| 228 | + tail expression", |
| 229 | + ); |
| 230 | + ret_span.push_span_label(ret, "the `match` arms can conform to this return type"); |
| 231 | + ret_span.push_span_label( |
| 232 | + semi_span, |
| 233 | + "the `match` is a statement because of this semicolon, consider \ |
| 234 | + removing it", |
| 235 | + ); |
| 236 | + diag.span_note(ret_span, "you might have meant to return the `match` expression"); |
| 237 | + diag.tool_only_span_suggestion( |
| 238 | + semi_span, |
| 239 | + "remove this semicolon", |
| 240 | + "", |
| 241 | + Applicability::MaybeIncorrect, |
| 242 | + ); |
| 243 | + } |
| 244 | + |
222 | 245 | /// When the previously checked expression (the scrutinee) diverges, |
223 | 246 | /// warn the user about the match arms being unreachable. |
224 | 247 | fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { |
|
0 commit comments