Skip to content

Commit 60e72f4

Browse files
committed
Make suggestions machine-applicable
1 parent fcf8261 commit 60e72f4

File tree

18 files changed

+204
-62
lines changed

18 files changed

+204
-62
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,15 +1096,53 @@ pub struct Arm {
10961096
pub attrs: AttrVec,
10971097
/// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`
10981098
pub pat: P<Pat>,
1099-
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
1100-
pub guard: Option<P<Expr>>,
1099+
/// Match arm guard, e.g. `if n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
1100+
pub guard: Option<ArmGuard>,
11011101
/// Match arm body. Omitted if the pattern is a never pattern.
1102-
pub body: Option<P<Expr>>,
1102+
pub body: Option<ArmBody>,
11031103
pub span: Span,
11041104
pub id: NodeId,
11051105
pub is_placeholder: bool,
11061106
}
11071107

1108+
/// A guard of a `match` arm.
1109+
///
1110+
/// E.g., `if x > 10` in
1111+
///
1112+
/// ```
1113+
/// match None {
1114+
/// Some(x) if x > 10 => { println!("match!") },
1115+
/// _ => { println!("no match!") },
1116+
/// }
1117+
/// ```
1118+
#[derive(Clone, Encodable, Decodable, Debug)]
1119+
pub struct ArmGuard {
1120+
/// The condition of the guard, e.g. `Some(x) = foo()` in `if Some(x) = foo()`.
1121+
pub cond: P<Expr>,
1122+
/// The span of the guard, including the leading `if`.
1123+
pub span: Span,
1124+
}
1125+
1126+
/// A body of a `match` arm.
1127+
///
1128+
/// E.g., `=> { println!("match!") }` in
1129+
///
1130+
/// ```
1131+
/// match None {
1132+
/// Some(x) if x > 10 => { println!("match!") },
1133+
/// _ => { println!("no match!") },
1134+
/// }
1135+
/// ```
1136+
#[derive(Clone, Encodable, Decodable, Debug)]
1137+
pub struct ArmBody {
1138+
/// The expression of the body.
1139+
pub expr: P<Expr>,
1140+
/// The span of the body, including the leading `=>` but not a trailing `,`.
1141+
pub span: Span,
1142+
/// Whether the body expression is followed by a comma.
1143+
pub trailing_comma: bool,
1144+
}
1145+
11081146
/// A single field in a struct expression, e.g. `x: value` and `y` in `Foo { x: value, y }`.
11091147
#[derive(Clone, Encodable, Decodable, Debug)]
11101148
pub struct ExprField {

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,14 @@ pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[
452452
visit_attrs(attrs, vis);
453453
vis.visit_id(id);
454454
vis.visit_pat(pat);
455-
visit_opt(guard, |guard| vis.visit_expr(guard));
456-
visit_opt(body, |body| vis.visit_expr(body));
455+
visit_opt(guard, |guard| {
456+
vis.visit_expr(&mut guard.cond);
457+
vis.visit_span(&mut guard.span);
458+
});
459+
visit_opt(body, |body| {
460+
vis.visit_expr(&mut body.expr);
461+
vis.visit_span(&mut body.span);
462+
});
457463
vis.visit_span(span);
458464
smallvec![arm]
459465
}

compiler/rustc_ast/src/visit.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -950,8 +950,12 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
950950

951951
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
952952
visitor.visit_pat(&arm.pat);
953-
walk_list!(visitor, visit_expr, &arm.guard);
954-
walk_list!(visitor, visit_expr, &arm.body);
953+
if let Some(guard) = &arm.guard {
954+
visitor.visit_expr(&guard.cond);
955+
}
956+
if let Some(body) = &arm.body {
957+
visitor.visit_expr(&body.expr);
958+
}
955959
walk_list!(visitor, visit_attribute, &arm.attrs);
956960
}
957961

compiler/rustc_ast_lowering/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ ast_lowering_misplaced_relax_trait_bound =
111111
ast_lowering_never_pattern_with_body =
112112
a never pattern is always unreachable
113113
.label = this will never be executed
114-
.suggestion = remove this expression
114+
.suggestion = keep only the pattern
115115
116116
ast_lowering_never_pattern_with_guard =
117117
a guard on a never pattern will never be run

compiler/rustc_ast_lowering/src/errors.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,16 +354,19 @@ pub struct MatchArmWithNoBody {
354354
pub struct NeverPatternWithBody {
355355
#[primary_span]
356356
#[label]
357-
#[suggestion(code = "", applicability = "maybe-incorrect")]
358357
pub span: Span,
358+
#[suggestion(code = "{suggestion}", applicability = "machine-applicable")]
359+
pub suggestion_span: Span,
360+
pub suggestion: &'static str,
359361
}
360362

361363
#[derive(Diagnostic)]
362364
#[diag(ast_lowering_never_pattern_with_guard)]
363365
pub struct NeverPatternWithGuard {
364366
#[primary_span]
365-
#[suggestion(code = "", applicability = "maybe-incorrect")]
366367
pub span: Span,
368+
#[suggestion(code = "", applicability = "machine-applicable")]
369+
pub suggestion: Span,
367370
}
368371

369372
#[derive(Diagnostic, Clone, Copy)]

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
551551

552552
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
553553
let pat = self.lower_pat(&arm.pat);
554-
let mut guard = arm.guard.as_ref().map(|cond| {
555-
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
554+
let mut guard = arm.guard.as_ref().map(|guard| {
555+
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &guard.cond.kind {
556556
hir::Guard::IfLet(self.arena.alloc(hir::Let {
557557
hir_id: self.next_id(),
558558
span: self.lower_span(*span),
@@ -562,7 +562,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
562562
is_recovered: *is_recovered,
563563
}))
564564
} else {
565-
hir::Guard::If(self.lower_expr(cond))
565+
hir::Guard::If(self.lower_expr(&guard.cond))
566566
}
567567
});
568568
let hir_id = self.next_id();
@@ -572,17 +572,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
572572
let body = if let Some(body) = &arm.body
573573
&& !is_never_pattern
574574
{
575-
self.lower_expr(body)
575+
self.lower_expr(&body.expr)
576576
} else {
577577
// Either `body.is_none()` or `is_never_pattern` here.
578578
if !is_never_pattern {
579579
let suggestion = span.shrink_to_hi();
580580
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
581581
} else if let Some(body) = &arm.body {
582-
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
582+
let suggestion = if body.trailing_comma { "" } else { "," };
583+
let mut suggestion_span = body.span;
584+
if let Some(g) = &arm.guard {
585+
suggestion_span = g.span.to(suggestion_span);
586+
}
587+
self.tcx.sess.emit_err(NeverPatternWithBody {
588+
span: body.expr.span,
589+
suggestion_span,
590+
suggestion,
591+
});
583592
guard = None;
584593
} else if let Some(g) = &arm.guard {
585-
self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
594+
self.tcx
595+
.sess
596+
.emit_err(NeverPatternWithGuard { span: g.cond.span, suggestion: g.span });
586597
guard = None;
587598
}
588599

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,14 @@ impl<'a> State<'a> {
628628
self.space();
629629
if let Some(e) = &arm.guard {
630630
self.word_space("if");
631-
self.print_expr(e);
631+
self.print_expr(&e.cond);
632632
self.space();
633633
}
634634

635635
if let Some(body) = &arm.body {
636636
self.word_space("=>");
637637

638-
match &body.kind {
638+
match &body.expr.kind {
639639
ast::ExprKind::Block(blk, opt_label) => {
640640
if let Some(label) = opt_label {
641641
self.print_ident(label.ident);
@@ -652,7 +652,7 @@ impl<'a> State<'a> {
652652
}
653653
_ => {
654654
self.end(); // Close the ibox for the pattern.
655-
self.print_expr(body);
655+
self.print_expr(&body.expr);
656656
self.word(",");
657657
}
658658
}

compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::{path_std, pathvec_std};
4-
use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind};
4+
use rustc_ast::{ArmBody, ExprKind, ItemKind, MetaItem, PatKind};
55
use rustc_expand::base::{Annotatable, ExtCtxt};
66
use rustc_span::symbol::{sym, Ident};
77
use rustc_span::Span;
@@ -136,7 +136,8 @@ fn cs_partial_cmp(
136136
&& let Some(last) = arms.last_mut()
137137
&& let PatKind::Wild = last.pat.kind
138138
{
139-
last.body = Some(expr2);
139+
last.body =
140+
Some(ArmBody { span: expr2.span, expr: expr2, trailing_comma: false });
140141
expr1
141142
} else {
142143
let eq_arm = cx.arm(

compiler/rustc_expand/src/build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::base::ExtCtxt;
2+
use ast::ArmBody;
23
use rustc_ast::ptr::P;
34
use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp};
45
use rustc_ast::{attr, token, util::literal};
@@ -505,7 +506,7 @@ impl<'a> ExtCtxt<'a> {
505506
attrs: AttrVec::new(),
506507
pat,
507508
guard: None,
508-
body: Some(expr),
509+
body: Some(ArmBody { expr, span, trailing_comma: false }),
509510
span,
510511
id: ast::DUMMY_NODE_ID,
511512
is_placeholder: false,

compiler/rustc_expand/src/placeholders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub fn placeholder(
119119
}]),
120120
AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
121121
attrs: Default::default(),
122-
body: Some(expr_placeholder()),
122+
body: Some(ast::ArmBody { expr: expr_placeholder(), span, trailing_comma: false }),
123123
guard: None,
124124
id,
125125
pat: pat(),

compiler/rustc_lint/src/unused.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ impl EarlyLintPass for UnusedParens {
11161116
if let Some(body) = &a.body {
11171117
self.check_unused_delims_expr(
11181118
cx,
1119-
body,
1119+
&body.expr,
11201120
UnusedDelimsCtx::MatchArmExpr,
11211121
false,
11221122
None,

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use super::{
1010
use crate::errors;
1111
use crate::maybe_recover_from_interpolated_ty_qpath;
1212
use ast::mut_visit::{noop_visit_expr, MutVisitor};
13-
use ast::{GenBlockKind, Pat, Path, PathSegment};
13+
use ast::{ArmBody, GenBlockKind, Pat, Path, PathSegment};
1414
use core::mem;
1515
use rustc_ast::ptr::P;
1616
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@@ -22,7 +22,7 @@ use rustc_ast::visit::Visitor;
2222
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
2323
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
2424
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
25-
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
25+
use rustc_ast::{ArmGuard, ClosureBinder, MetaItemLit, StmtKind};
2626
use rustc_ast_pretty::pprust;
2727
use rustc_data_structures::stack::ensure_sufficient_stack;
2828
use rustc_errors::{
@@ -2938,7 +2938,7 @@ impl<'a> Parser<'a> {
29382938
let arrow_span = this.prev_token.span;
29392939
let arm_start_span = this.token.span;
29402940

2941-
let expr =
2941+
let mut expr =
29422942
this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| {
29432943
err.span_label(arrow_span, "while parsing the `match` arm starting here");
29442944
err
@@ -2947,16 +2947,18 @@ impl<'a> Parser<'a> {
29472947
let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
29482948
&& this.token != token::CloseDelim(Delimiter::Brace);
29492949

2950-
if !require_comma {
2951-
arm_body = Some(expr);
2952-
this.eat(&token::Comma);
2950+
let mut trailing_comma = false;
2951+
let mut hi = this.prev_token.span;
2952+
let result = if !require_comma {
2953+
trailing_comma = this.eat(&token::Comma);
29532954
Ok(false)
29542955
} else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) {
2955-
arm_body = Some(body);
2956+
hi = this.prev_token.span;
2957+
expr = body;
29562958
Ok(true)
29572959
} else {
29582960
let expr_span = expr.span;
2959-
arm_body = Some(expr);
2961+
trailing_comma = this.check(&token::Comma);
29602962
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
29612963
.map_err(|mut err| {
29622964
if this.token == token::FatArrow {
@@ -2993,7 +2995,9 @@ impl<'a> Parser<'a> {
29932995
}
29942996
err
29952997
})
2996-
}
2998+
};
2999+
arm_body = Some(ArmBody { expr, span: arrow_span.to(hi), trailing_comma });
3000+
result
29973001
};
29983002

29993003
let hi_span = arm_body.as_ref().map_or(span_before_body, |body| body.span);
@@ -3056,7 +3060,7 @@ impl<'a> Parser<'a> {
30563060
})
30573061
}
30583062

3059-
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<P<Expr>>> {
3063+
fn parse_match_arm_guard(&mut self) -> PResult<'a, Option<ArmGuard>> {
30603064
// Used to check the `let_chains` and `if_let_guard` features mostly by scanning
30613065
// `&&` tokens.
30623066
fn check_let_expr(expr: &Expr) -> (bool, bool) {
@@ -3081,18 +3085,18 @@ impl<'a> Parser<'a> {
30813085
CondChecker::new(self).visit_expr(&mut cond);
30823086

30833087
let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
3088+
let span = if_span.to(cond.span);
30843089
if has_let_expr {
30853090
if does_not_have_bin_op {
30863091
// Remove the last feature gating of a `let` expression since it's stable.
30873092
self.sess.gated_spans.ungate_last(sym::let_chains, cond.span);
30883093
}
3089-
let span = if_span.to(cond.span);
30903094
self.sess.gated_spans.gate(sym::if_let_guard, span);
30913095
}
3092-
Ok(Some(cond))
3096+
Ok(Some(ArmGuard { cond, span }))
30933097
}
30943098

3095-
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
3099+
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<ArmGuard>)> {
30963100
if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
30973101
// Detect and recover from `($pat if $cond) => $arm`.
30983102
let left = self.token.span;
@@ -3107,6 +3111,7 @@ impl<'a> Parser<'a> {
31073111
if let prev_sp = self.prev_token.span
31083112
&& let true = self.eat_keyword(kw::If) =>
31093113
{
3114+
let if_span = self.prev_token.span;
31103115
// We know for certain we've found `($pat if` so far.
31113116
let mut cond = match self.parse_match_guard_condition() {
31123117
Ok(cond) => cond,
@@ -3124,7 +3129,12 @@ impl<'a> Parser<'a> {
31243129
span: vec![left, right],
31253130
sugg: errors::ParenthesesInMatchPatSugg { left, right },
31263131
});
3127-
Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond)))
3132+
let pat_span = left.to(prev_sp);
3133+
let guard_span = if_span.to(cond.span);
3134+
Ok((
3135+
self.mk_pat(pat_span, ast::PatKind::Wild),
3136+
Some(ArmGuard { cond, span: guard_span }),
3137+
))
31283138
}
31293139
Err(err) => Err(err),
31303140
}

compiler/rustc_resolve/src/late.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,8 +3293,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32933293
fn resolve_arm(&mut self, arm: &'ast Arm) {
32943294
self.with_rib(ValueNS, RibKind::Normal, |this| {
32953295
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
3296-
walk_list!(this, visit_expr, &arm.guard);
3297-
walk_list!(this, visit_expr, &arm.body);
3296+
if let Some(guard) = &arm.guard {
3297+
this.visit_expr(&guard.cond);
3298+
}
3299+
if let Some(body) = &arm.body {
3300+
this.visit_expr(&body.expr);
3301+
}
32983302
});
32993303
}
33003304

0 commit comments

Comments
 (0)