|
| 1 | +// ignore-tidy-filelength |
1 | 2 | use core::cmp::min;
|
2 | 3 | use core::iter;
|
3 | 4 |
|
@@ -766,56 +767,121 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
766 | 767 | needs_block: bool,
|
767 | 768 | parent_is_closure: bool,
|
768 | 769 | ) {
|
769 |
| - if expected.is_unit() { |
770 |
| - // `BlockTailExpression` only relevant if the tail expr would be |
771 |
| - // useful on its own. |
772 |
| - match expression.kind { |
773 |
| - ExprKind::Call(..) |
774 |
| - | ExprKind::MethodCall(..) |
775 |
| - | ExprKind::Loop(..) |
776 |
| - | ExprKind::If(..) |
777 |
| - | ExprKind::Match(..) |
778 |
| - | ExprKind::Block(..) |
779 |
| - if expression.can_have_side_effects() |
780 |
| - // If the expression is from an external macro, then do not suggest |
781 |
| - // adding a semicolon, because there's nowhere to put it. |
782 |
| - // See issue #81943. |
783 |
| - && !expression.span.in_external_macro(self.tcx.sess.source_map()) => |
| 770 | + if !expected.is_unit() { |
| 771 | + return; |
| 772 | + } |
| 773 | + // `BlockTailExpression` only relevant if the tail expr would be |
| 774 | + // useful on its own. |
| 775 | + match expression.kind { |
| 776 | + ExprKind::Call(..) |
| 777 | + | ExprKind::MethodCall(..) |
| 778 | + | ExprKind::Loop(..) |
| 779 | + | ExprKind::If(..) |
| 780 | + | ExprKind::Match(..) |
| 781 | + | ExprKind::Block(..) |
| 782 | + if expression.can_have_side_effects() |
| 783 | + // If the expression is from an external macro, then do not suggest |
| 784 | + // adding a semicolon, because there's nowhere to put it. |
| 785 | + // See issue #81943. |
| 786 | + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => |
| 787 | + { |
| 788 | + if needs_block { |
| 789 | + err.multipart_suggestion( |
| 790 | + "consider using a semicolon here", |
| 791 | + vec![ |
| 792 | + (expression.span.shrink_to_lo(), "{ ".to_owned()), |
| 793 | + (expression.span.shrink_to_hi(), "; }".to_owned()), |
| 794 | + ], |
| 795 | + Applicability::MachineApplicable, |
| 796 | + ); |
| 797 | + } else if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id) |
| 798 | + && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id) |
| 799 | + && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id) |
| 800 | + && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind |
| 801 | + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id) |
| 802 | + && let hir::StmtKind::Expr(_) = stmt.kind |
| 803 | + && self.is_next_stmt_expr_continuation(stmt.hir_id) |
784 | 804 | {
|
785 |
| - if needs_block { |
786 |
| - err.multipart_suggestion( |
787 |
| - "consider using a semicolon here", |
788 |
| - vec![ |
789 |
| - (expression.span.shrink_to_lo(), "{ ".to_owned()), |
790 |
| - (expression.span.shrink_to_hi(), "; }".to_owned()), |
791 |
| - ], |
792 |
| - Applicability::MachineApplicable, |
793 |
| - ); |
794 |
| - } else { |
795 |
| - err.span_suggestion( |
796 |
| - expression.span.shrink_to_hi(), |
797 |
| - "consider using a semicolon here", |
798 |
| - ";", |
799 |
| - Applicability::MachineApplicable, |
800 |
| - ); |
801 |
| - } |
| 805 | + err.multipart_suggestion( |
| 806 | + "parentheses are required to parse this as an expression", |
| 807 | + vec![ |
| 808 | + (stmt.span.shrink_to_lo(), "(".to_string()), |
| 809 | + (stmt.span.shrink_to_hi(), ")".to_string()), |
| 810 | + ], |
| 811 | + Applicability::MachineApplicable, |
| 812 | + ); |
| 813 | + } else { |
| 814 | + err.span_suggestion( |
| 815 | + expression.span.shrink_to_hi(), |
| 816 | + "consider using a semicolon here", |
| 817 | + ";", |
| 818 | + Applicability::MachineApplicable, |
| 819 | + ); |
802 | 820 | }
|
803 |
| - ExprKind::Path(..) | ExprKind::Lit(_) |
804 |
| - if parent_is_closure |
805 |
| - && !expression.span.in_external_macro(self.tcx.sess.source_map()) => |
| 821 | + } |
| 822 | + ExprKind::Path(..) | ExprKind::Lit(_) |
| 823 | + if parent_is_closure |
| 824 | + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => |
| 825 | + { |
| 826 | + err.span_suggestion_verbose( |
| 827 | + expression.span.shrink_to_lo(), |
| 828 | + "consider ignoring the value", |
| 829 | + "_ = ", |
| 830 | + Applicability::MachineApplicable, |
| 831 | + ); |
| 832 | + } |
| 833 | + _ => { |
| 834 | + if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id) |
| 835 | + && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id) |
| 836 | + && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id) |
| 837 | + && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind |
| 838 | + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id) |
| 839 | + && let hir::StmtKind::Expr(_) = stmt.kind |
| 840 | + && self.is_next_stmt_expr_continuation(stmt.hir_id) |
806 | 841 | {
|
807 |
| - err.span_suggestion_verbose( |
808 |
| - expression.span.shrink_to_lo(), |
809 |
| - "consider ignoring the value", |
810 |
| - "_ = ", |
| 842 | + // The error is pointing at an arm of an if-expression, and we want to get the |
| 843 | + // `Span` of the whole if-expression for the suggestion. This only works for a |
| 844 | + // single level of nesting, which is fine. |
| 845 | + // We have something like `if true { false } else { true } && true`. Suggest |
| 846 | + // wrapping in parentheses. We find the statement or expression following the |
| 847 | + // `if` (`&& true`) and see if it is something that can reasonably be |
| 848 | + // interpreted as a binop following an expression. |
| 849 | + err.multipart_suggestion( |
| 850 | + "parentheses are required to parse this as an expression", |
| 851 | + vec![ |
| 852 | + (stmt.span.shrink_to_lo(), "(".to_string()), |
| 853 | + (stmt.span.shrink_to_hi(), ")".to_string()), |
| 854 | + ], |
811 | 855 | Applicability::MachineApplicable,
|
812 | 856 | );
|
813 | 857 | }
|
814 |
| - _ => (), |
815 | 858 | }
|
816 | 859 | }
|
817 | 860 | }
|
818 | 861 |
|
| 862 | + pub(crate) fn is_next_stmt_expr_continuation(&self, hir_id: HirId) -> bool { |
| 863 | + if let hir::Node::Block(b) = self.tcx.parent_hir_node(hir_id) |
| 864 | + && let mut stmts = b.stmts.iter().skip_while(|s| s.hir_id != hir_id) |
| 865 | + && let Some(_) = stmts.next() // The statement the statement that was passed in |
| 866 | + && let Some(next) = match (stmts.next(), b.expr) { // The following statement |
| 867 | + (Some(next), _) => match next.kind { |
| 868 | + hir::StmtKind::Expr(next) | hir::StmtKind::Semi(next) => Some(next), |
| 869 | + _ => None, |
| 870 | + }, |
| 871 | + (None, Some(next)) => Some(next), |
| 872 | + _ => None, |
| 873 | + } |
| 874 | + && let hir::ExprKind::AddrOf(..) // prev_stmt && next |
| 875 | + | hir::ExprKind::Unary(..) // prev_stmt * next |
| 876 | + | hir::ExprKind::Err(_) = next.kind |
| 877 | + // prev_stmt + next |
| 878 | + { |
| 879 | + true |
| 880 | + } else { |
| 881 | + false |
| 882 | + } |
| 883 | + } |
| 884 | + |
819 | 885 | /// A possible error is to forget to add a return type that is needed:
|
820 | 886 | ///
|
821 | 887 | /// ```compile_fail,E0308
|
|
0 commit comments