Skip to content

Commit 176fe3f

Browse files
committed
encode ; stmt w/o expr as StmtKind::Empty
1 parent beac68a commit 176fe3f

19 files changed

+73
-102
lines changed

src/librustc_ast/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,8 @@ pub enum StmtKind {
913913
Expr(P<Expr>),
914914
/// Expr with a trailing semi-colon.
915915
Semi(P<Expr>),
916+
/// Just a trailing semi-colon.
917+
Empty,
916918
/// Macro.
917919
Mac(P<(Mac, MacStmtStyle, AttrVec)>),
918920
}

src/librustc_ast/attr/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,8 @@ impl HasAttrs for StmtKind {
674674
fn attrs(&self) -> &[Attribute] {
675675
match *self {
676676
StmtKind::Local(ref local) => local.attrs(),
677-
StmtKind::Item(..) => &[],
678677
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
678+
StmtKind::Empty | StmtKind::Item(..) => &[],
679679
StmtKind::Mac(ref mac) => {
680680
let (_, _, ref attrs) = **mac;
681681
attrs.attrs()
@@ -686,9 +686,8 @@ impl HasAttrs for StmtKind {
686686
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
687687
match self {
688688
StmtKind::Local(local) => local.visit_attrs(f),
689-
StmtKind::Item(..) => {}
690-
StmtKind::Expr(expr) => expr.visit_attrs(f),
691-
StmtKind::Semi(expr) => expr.visit_attrs(f),
689+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
690+
StmtKind::Empty | StmtKind::Item(..) => {}
692691
StmtKind::Mac(mac) => {
693692
let (_mac, _style, attrs) = mac.deref_mut();
694693
attrs.visit_attrs(f);

src/librustc_ast/mut_visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
12651265
StmtKind::Item(item) => vis.flat_map_item(item).into_iter().map(StmtKind::Item).collect(),
12661266
StmtKind::Expr(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Expr).collect(),
12671267
StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
1268+
StmtKind::Empty => smallvec![StmtKind::Empty],
12681269
StmtKind::Mac(mut mac) => {
12691270
let (mac_, _semi, attrs) = mac.deref_mut();
12701271
vis.visit_mac(mac_);

src/librustc_ast/visit.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -669,9 +669,8 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
669669
match statement.kind {
670670
StmtKind::Local(ref local) => visitor.visit_local(local),
671671
StmtKind::Item(ref item) => visitor.visit_item(item),
672-
StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
673-
visitor.visit_expr(expression)
674-
}
672+
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
673+
StmtKind::Empty => {}
675674
StmtKind::Mac(ref mac) => {
676675
let (ref mac, _, ref attrs) = **mac;
677676
visitor.visit_mac(mac);

src/librustc_ast_lowering/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22812281
}
22822282
StmtKind::Expr(ref e) => hir::StmtKind::Expr(self.lower_expr(e)),
22832283
StmtKind::Semi(ref e) => hir::StmtKind::Semi(self.lower_expr(e)),
2284+
StmtKind::Empty => return smallvec![],
22842285
StmtKind::Mac(..) => panic!("shouldn't exist here"),
22852286
};
22862287
smallvec![hir::Stmt { hir_id: self.lower_node_id(s.id), kind, span: s.span }]

src/librustc_ast_pretty/pprust.rs

+7-13
Original file line numberDiff line numberDiff line change
@@ -1446,19 +1446,13 @@ impl<'a> State<'a> {
14461446
}
14471447
}
14481448
ast::StmtKind::Semi(ref expr) => {
1449-
match expr.kind {
1450-
// Filter out empty `Tup` exprs created for the `redundant_semicolon`
1451-
// lint, as they shouldn't be visible and interact poorly
1452-
// with proc macros.
1453-
ast::ExprKind::Tup(ref exprs) if exprs.is_empty() && expr.attrs.is_empty() => {
1454-
()
1455-
}
1456-
_ => {
1457-
self.space_if_not_bol();
1458-
self.print_expr_outer_attr_style(expr, false);
1459-
self.s.word(";");
1460-
}
1461-
}
1449+
self.space_if_not_bol();
1450+
self.print_expr_outer_attr_style(expr, false);
1451+
self.s.word(";");
1452+
}
1453+
ast::StmtKind::Empty => {
1454+
self.space_if_not_bol();
1455+
self.s.word(";");
14621456
}
14631457
ast::StmtKind::Mac(ref mac) => {
14641458
let (ref mac, style, ref attrs) = **mac;

src/librustc_lint/builtin.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,10 @@ impl EarlyLintPass for UnusedDocComment {
775775
ast::StmtKind::Local(..) => "statements",
776776
ast::StmtKind::Item(..) => "inner items",
777777
// expressions will be reported by `check_expr`.
778-
ast::StmtKind::Semi(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Mac(..) => return,
778+
ast::StmtKind::Empty
779+
| ast::StmtKind::Semi(_)
780+
| ast::StmtKind::Expr(_)
781+
| ast::StmtKind::Mac(_) => return,
779782
};
780783

781784
warn_if_doc(cx, stmt.span, kind, stmt.kind.attrs());

src/librustc_lint/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ macro_rules! early_lint_passes {
113113
WhileTrue: WhileTrue,
114114
NonAsciiIdents: NonAsciiIdents,
115115
IncompleteFeatures: IncompleteFeatures,
116-
RedundantSemicolon: RedundantSemicolon,
116+
RedundantSemicolons: RedundantSemicolons,
117117
UnusedDocComment: UnusedDocComment,
118118
]
119119
);
@@ -274,7 +274,8 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
274274
UNUSED_EXTERN_CRATES,
275275
UNUSED_FEATURES,
276276
UNUSED_LABELS,
277-
UNUSED_PARENS
277+
UNUSED_PARENS,
278+
REDUNDANT_SEMICOLONS
278279
);
279280

280281
add_lint_group!(
@@ -307,6 +308,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
307308
store.register_renamed("unused_doc_comment", "unused_doc_comments");
308309
store.register_renamed("async_idents", "keyword_idents");
309310
store.register_renamed("exceeding_bitshifts", "arithmetic_overflow");
311+
store.register_renamed("redundant_semicolon", "redundant_semicolons");
310312
store.register_removed("unknown_features", "replaced by an error");
311313
store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
312314
store.register_removed("negate_unsigned", "cast a signed value instead");
+28-37
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,41 @@
11
use crate::{EarlyContext, EarlyLintPass, LintContext};
2-
use rustc_ast::ast::{ExprKind, Stmt, StmtKind};
2+
use rustc_ast::ast::{Block, StmtKind};
33
use rustc_errors::Applicability;
4+
use rustc_span::Span;
45

56
declare_lint! {
6-
pub REDUNDANT_SEMICOLON,
7+
pub REDUNDANT_SEMICOLONS,
78
Warn,
89
"detects unnecessary trailing semicolons"
910
}
1011

11-
declare_lint_pass!(RedundantSemicolon => [REDUNDANT_SEMICOLON]);
12+
declare_lint_pass!(RedundantSemicolons => [REDUNDANT_SEMICOLONS]);
1213

13-
impl EarlyLintPass for RedundantSemicolon {
14-
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
15-
if let StmtKind::Semi(expr) = &stmt.kind {
16-
if let ExprKind::Tup(ref v) = &expr.kind {
17-
if v.is_empty() {
18-
// Strings of excess semicolons are encoded as empty tuple expressions
19-
// during the parsing stage, so we check for empty tuple expressions
20-
// which span only semicolons
21-
if let Ok(source_str) = cx.sess().source_map().span_to_snippet(stmt.span) {
22-
if source_str.chars().all(|c| c == ';') {
23-
let multiple = (stmt.span.hi() - stmt.span.lo()).0 > 1;
24-
let msg = if multiple {
25-
"unnecessary trailing semicolons"
26-
} else {
27-
"unnecessary trailing semicolon"
28-
};
29-
cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, |lint| {
30-
let mut err = lint.build(&msg);
31-
let suggest_msg = if multiple {
32-
"remove these semicolons"
33-
} else {
34-
"remove this semicolon"
35-
};
36-
err.span_suggestion(
37-
stmt.span,
38-
&suggest_msg,
39-
String::new(),
40-
Applicability::MaybeIncorrect,
41-
);
42-
err.emit();
43-
});
44-
}
45-
}
46-
}
14+
impl EarlyLintPass for RedundantSemicolons {
15+
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
16+
let mut seq = None;
17+
for stmt in block.stmts.iter() {
18+
match (&stmt.kind, &mut seq) {
19+
(StmtKind::Empty, None) => seq = Some((stmt.span, false)),
20+
(StmtKind::Empty, Some(seq)) => *seq = (seq.0.to(stmt.span), true),
21+
(_, seq) => maybe_lint_redundant_semis(cx, seq),
4722
}
4823
}
24+
maybe_lint_redundant_semis(cx, &mut seq);
25+
}
26+
}
27+
28+
fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) {
29+
if let Some((span, multiple)) = seq.take() {
30+
cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| {
31+
let (msg, rem) = if multiple {
32+
("unnecessary trailing semicolons", "remove these semicolons")
33+
} else {
34+
("unnecessary trailing semicolon", "remove this semicolon")
35+
};
36+
lint.build(msg)
37+
.span_suggestion(span, rem, String::new(), Applicability::MaybeIncorrect)
38+
.emit();
39+
});
4940
}
5041
}

src/librustc_parse/parser/stmt.rs

+7-20
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,10 @@ impl<'a> Parser<'a> {
5959
} else if let Some(item) = self.parse_stmt_item(attrs.clone())? {
6060
// FIXME: Bad copy of attrs
6161
self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
62-
} else if self.token == token::Semi {
62+
} else if self.eat(&token::Semi) {
6363
// Do not attempt to parse an expression if we're done here.
6464
self.error_outer_attrs(&attrs);
65-
self.bump();
66-
let mut last_semi = lo;
67-
while self.token == token::Semi {
68-
last_semi = self.token.span;
69-
self.bump();
70-
}
71-
// We are encoding a string of semicolons as an an empty tuple that spans
72-
// the excess semicolons to preserve this info until the lint stage.
73-
let kind = StmtKind::Semi(self.mk_expr(
74-
lo.to(last_semi),
75-
ExprKind::Tup(Vec::new()),
76-
AttrVec::new(),
77-
));
78-
self.mk_stmt(lo.to(last_semi), kind)
65+
self.mk_stmt(lo, StmtKind::Empty)
7966
} else if self.token != token::CloseDelim(token::Brace) {
8067
// Remainder are line-expr stmts.
8168
let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?;
@@ -144,12 +131,11 @@ impl<'a> Parser<'a> {
144131
/// Error on outer attributes in this context.
145132
/// Also error if the previous token was a doc comment.
146133
fn error_outer_attrs(&self, attrs: &[Attribute]) {
147-
if !attrs.is_empty() {
148-
if matches!(self.prev_token.kind, TokenKind::DocComment(..)) {
149-
self.span_fatal_err(self.prev_token.span, Error::UselessDocComment).emit();
134+
if let [.., last] = attrs {
135+
if last.is_doc_comment() {
136+
self.span_fatal_err(last.span, Error::UselessDocComment).emit();
150137
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
151-
self.struct_span_err(self.token.span, "expected statement after outer attribute")
152-
.emit();
138+
self.struct_span_err(last.span, "expected statement after outer attribute").emit();
153139
}
154140
}
155141
}
@@ -401,6 +387,7 @@ impl<'a> Parser<'a> {
401387
self.expect_semi()?;
402388
eat_semi = false;
403389
}
390+
StmtKind::Empty => eat_semi = false,
404391
_ => {}
405392
}
406393

src/test/ui/block-expr-precedence.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ warning: unnecessary trailing semicolons
44
LL | if (true) { 12; };;; -num;
55
| ^^ help: remove these semicolons
66
|
7-
= note: `#[warn(redundant_semicolon)]` on by default
7+
= note: `#[warn(redundant_semicolons)]` on by default
88

src/test/ui/consts/const_let_eq.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ struct Bar<T> { x: T }
55
struct W(u32);
66
struct A { a: u32 }
77

8+
#[allow(redundant_semicolons)]
89
const fn basics((a,): (u32,)) -> u32 {
910
// Deferred assignment:
1011
let b: u32;

src/test/ui/consts/const_let_eq_float.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ struct Bar<T> { x: T }
77
struct W(f32);
88
struct A { a: f32 }
99

10+
#[allow(redundant_semicolons)]
1011
const fn basics((a,): (f32,)) -> f32 {
1112
// Deferred assignment:
1213
let b: f32;

src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// aux-build:redundant-semi-proc-macro-def.rs
22

3-
#![deny(redundant_semicolon)]
3+
#![deny(redundant_semicolons)]
44
extern crate redundant_semi_proc_macro;
55
use redundant_semi_proc_macro::should_preserve_spans;
66

src/test/ui/lint/redundant-semicolon/redundant-semi-proc-macro.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
TokenStream [Ident { ident: "fn", span: #0 bytes(197..199) }, Ident { ident: "span_preservation", span: #0 bytes(200..217) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(217..219) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(227..230) }, Ident { ident: "tst", span: #0 bytes(231..234) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(235..236) }, Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(237), hi: BytePos(240), ctxt: #0 } }, Punct { ch: ';', spacing: Joint, span: #0 bytes(240..241) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(241..242) }, Ident { ident: "match", span: #0 bytes(288..293) }, Ident { ident: "tst", span: #0 bytes(294..297) }, Group { delimiter: Brace, stream: TokenStream [Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(482), hi: BytePos(485), ctxt: #0 } }, Punct { ch: '=', spacing: Joint, span: #0 bytes(486..488) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(486..488) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(489..491) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(491..492) }, Ident { ident: "_", span: #0 bytes(501..502) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(503..505) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(503..505) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(506..508) }], span: #0 bytes(298..514) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(514..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(516..517) }], span: #0 bytes(221..561) }]
1+
TokenStream [Ident { ident: "fn", span: #0 bytes(198..200) }, Ident { ident: "span_preservation", span: #0 bytes(201..218) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(218..220) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "let", span: #0 bytes(228..231) }, Ident { ident: "tst", span: #0 bytes(232..235) }, Punct { ch: '=', spacing: Alone, span: #0 bytes(236..237) }, Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(238), hi: BytePos(241), ctxt: #0 } }, Punct { ch: ';', spacing: Joint, span: #0 bytes(241..242) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(242..243) }, Ident { ident: "match", span: #0 bytes(289..294) }, Ident { ident: "tst", span: #0 bytes(295..298) }, Group { delimiter: Brace, stream: TokenStream [Literal { lit: Lit { kind: Integer, symbol: "123", suffix: None }, span: Span { lo: BytePos(483), hi: BytePos(486), ctxt: #0 } }, Punct { ch: '=', spacing: Joint, span: #0 bytes(487..489) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(487..489) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(490..492) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(492..493) }, Ident { ident: "_", span: #0 bytes(502..503) }, Punct { ch: '=', spacing: Joint, span: #0 bytes(504..506) }, Punct { ch: '>', spacing: Alone, span: #0 bytes(504..506) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(507..509) }], span: #0 bytes(299..515) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(515..516) }, Punct { ch: ';', spacing: Joint, span: #0 bytes(516..517) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(517..518) }], span: #0 bytes(222..562) }]
22
error: unnecessary trailing semicolon
33
--> $DIR/redundant-semi-proc-macro.rs:9:19
44
|
@@ -8,8 +8,8 @@ LL | let tst = 123;;
88
note: the lint level is defined here
99
--> $DIR/redundant-semi-proc-macro.rs:3:9
1010
|
11-
LL | #![deny(redundant_semicolon)]
12-
| ^^^^^^^^^^^^^^^^^^^
11+
LL | #![deny(redundant_semicolons)]
12+
| ^^^^^^^^^^^^^^^^^^^^
1313

1414
error: unnecessary trailing semicolons
1515
--> $DIR/redundant-semi-proc-macro.rs:16:7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: expected statement after outer attribute
2-
--> $DIR/attr-dangling-in-fn.rs:5:1
2+
--> $DIR/attr-dangling-in-fn.rs:4:3
33
|
4-
LL | }
5-
| ^
4+
LL | #[foo = "bar"]
5+
| ^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

src/test/ui/parser/attr-stmt-expr-attr-bad.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -411,16 +411,16 @@ LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
411411
| ^ expected one of `.`, `;`, `?`, or an operator
412412

413413
error: expected statement after outer attribute
414-
--> $DIR/attr-stmt-expr-attr-bad.rs:114:44
414+
--> $DIR/attr-stmt-expr-attr-bad.rs:114:37
415415
|
416416
LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } }
417-
| ^
417+
| ^^^^^^^
418418

419419
error: expected statement after outer attribute
420-
--> $DIR/attr-stmt-expr-attr-bad.rs:116:45
420+
--> $DIR/attr-stmt-expr-attr-bad.rs:116:37
421421
|
422422
LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } }
423-
| ^
423+
| ^^^^^^^
424424

425425
error: aborting due to 57 previous errors
426426

src/test/ui/parser/doc-before-semi.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,4 @@ fn main() {
33
//~^ ERROR found a documentation comment that doesn't document anything
44
//~| HELP maybe a comment was intended
55
;
6-
//~^ WARNING unnecessary trailing semicolon
7-
//~| HELP remove this semicolon
86
}

src/test/ui/parser/doc-before-semi.stderr

-8
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ LL | /// hi
66
|
77
= help: doc comments must come before what they document, maybe a comment was intended with `//`?
88

9-
warning: unnecessary trailing semicolon
10-
--> $DIR/doc-before-semi.rs:5:5
11-
|
12-
LL | ;
13-
| ^ help: remove this semicolon
14-
|
15-
= note: `#[warn(redundant_semicolon)]` on by default
16-
179
error: aborting due to previous error
1810

1911
For more information about this error, try `rustc --explain E0585`.

0 commit comments

Comments
 (0)