|
| 1 | +use crate::infer::error_reporting::hir::Path; |
1 | 2 | use hir::def::CtorKind;
|
2 | 3 | use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
| 4 | +use hir::{Local, QPath}; |
3 | 5 | use rustc_data_structures::fx::FxIndexSet;
|
4 | 6 | use rustc_errors::{Applicability, Diag};
|
5 | 7 | use rustc_hir as hir;
|
| 8 | +use rustc_hir::def::Res; |
| 9 | +use rustc_hir::MatchSource; |
| 10 | +use rustc_hir::Node; |
6 | 11 | use rustc_middle::traits::{
|
7 | 12 | IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
8 | 13 | StatementAsExpression,
|
@@ -293,6 +298,97 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
293 | 298 | }
|
294 | 299 | }
|
295 | 300 |
|
| 301 | + pub(super) fn suggest_turning_stmt_into_expr( |
| 302 | + &self, |
| 303 | + cause: &ObligationCause<'tcx>, |
| 304 | + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, |
| 305 | + diag: &mut Diag<'_>, |
| 306 | + ) { |
| 307 | + let ty::error::ExpectedFound { expected, found } = exp_found; |
| 308 | + if !found.peel_refs().is_unit() { |
| 309 | + return; |
| 310 | + } |
| 311 | + |
| 312 | + let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code() |
| 313 | + else { |
| 314 | + return; |
| 315 | + }; |
| 316 | + |
| 317 | + let node = self.tcx.hir_node(*hir_id); |
| 318 | + let mut blocks = vec![]; |
| 319 | + if let hir::Node::Block(block) = node |
| 320 | + && let Some(expr) = block.expr |
| 321 | + && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind |
| 322 | + && let Res::Local(local) = res |
| 323 | + && let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local) |
| 324 | + { |
| 325 | + fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { |
| 326 | + match expr.kind { |
| 327 | + // `blk1` and `blk2` must be have the same types, it will be reported before reaching here |
| 328 | + hir::ExprKind::If(_, blk1, Some(blk2)) => { |
| 329 | + collect_blocks(blk1, blocks); |
| 330 | + collect_blocks(blk2, blocks); |
| 331 | + } |
| 332 | + hir::ExprKind::Match(_, arms, _) => { |
| 333 | + // all arms must have same types |
| 334 | + for arm in arms.iter() { |
| 335 | + collect_blocks(arm.body, blocks); |
| 336 | + } |
| 337 | + } |
| 338 | + hir::ExprKind::Block(blk, _) => { |
| 339 | + blocks.push(blk); |
| 340 | + } |
| 341 | + _ => {} |
| 342 | + } |
| 343 | + } |
| 344 | + collect_blocks(init, &mut blocks); |
| 345 | + } |
| 346 | + |
| 347 | + let expected_inner: Ty<'_> = expected.peel_refs(); |
| 348 | + for block in blocks.iter() { |
| 349 | + self.consider_removing_semicolon(block, expected_inner, diag); |
| 350 | + } |
| 351 | + } |
| 352 | + |
| 353 | + /// A common error is to add an extra semicolon: |
| 354 | + /// |
| 355 | + /// ```compile_fail,E0308 |
| 356 | + /// fn foo() -> usize { |
| 357 | + /// 22; |
| 358 | + /// } |
| 359 | + /// ``` |
| 360 | + /// |
| 361 | + /// This routine checks if the final statement in a block is an |
| 362 | + /// expression with an explicit semicolon whose type is compatible |
| 363 | + /// with `expected_ty`. If so, it suggests removing the semicolon. |
| 364 | + pub fn consider_removing_semicolon( |
| 365 | + &self, |
| 366 | + blk: &'tcx hir::Block<'tcx>, |
| 367 | + expected_ty: Ty<'tcx>, |
| 368 | + diag: &mut Diag<'_>, |
| 369 | + ) -> bool { |
| 370 | + if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { |
| 371 | + if let StatementAsExpression::NeedsBoxing = boxed { |
| 372 | + diag.span_suggestion_verbose( |
| 373 | + span_semi, |
| 374 | + "consider removing this semicolon and boxing the expression", |
| 375 | + "", |
| 376 | + Applicability::HasPlaceholders, |
| 377 | + ); |
| 378 | + } else { |
| 379 | + diag.span_suggestion_short( |
| 380 | + span_semi, |
| 381 | + "remove this semicolon to return this value", |
| 382 | + "", |
| 383 | + Applicability::MachineApplicable, |
| 384 | + ); |
| 385 | + } |
| 386 | + true |
| 387 | + } else { |
| 388 | + false |
| 389 | + } |
| 390 | + } |
| 391 | + |
296 | 392 | pub(super) fn suggest_function_pointers(
|
297 | 393 | &self,
|
298 | 394 | cause: &ObligationCause<'tcx>,
|
|
0 commit comments