|
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