Skip to content

Commit 902cfac

Browse files
authored
Rollup merge of rust-lang#62984 - nathanwhit:extra_semi_lint, r=varkor
Add lint for excess trailing semicolons Closes rust-lang#60876. A caveat (not necessarily a negative, but something to consider) with this implementation is that excess semicolons after return/continue/break now also cause an 'unreachable statement' warning. For the following example: ``` fn main() { extra_semis(); } fn extra_semis() -> i32 { let mut sum = 0;;; for i in 0..10 { if i == 5 { continue;; } else if i == 9 { break;; } else { sum += i;; } } return sum;; } ``` The output is: ``` warning: unnecessary trailing semicolons --> src/main.rs:5:21 | 5 | let mut sum = 0;;; | ^^ help: remove these semicolons | = note: `#[warn(redundant_semicolon)]` on by default warning: unnecessary trailing semicolon --> src/main.rs:8:22 | 8 | continue;; | ^ help: remove this semicolon warning: unnecessary trailing semicolon --> src/main.rs:10:19 | 10 | break;; | ^ help: remove this semicolon warning: unnecessary trailing semicolon --> src/main.rs:12:22 | 12 | sum += i;; | ^ help: remove this semicolon warning: unnecessary trailing semicolon --> src/main.rs:15:16 | 15 | return sum;; | ^ help: remove this semicolon warning: unreachable statement --> src/main.rs:8:22 | 8 | continue;; | ^ | = note: `#[warn(unreachable_code)]` on by default warning: unreachable statement --> src/main.rs:10:19 | 10 | break;; | ^ warning: unreachable statement --> src/main.rs:15:16 | 15 | return sum;; | ^ ```
2 parents c43d03a + 76a1345 commit 902cfac

9 files changed

+95
-4
lines changed

Diff for: src/librustc_lint/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern crate rustc;
2424

2525
mod error_codes;
2626
mod nonstandard_style;
27+
mod redundant_semicolon;
2728
pub mod builtin;
2829
mod types;
2930
mod unused;
@@ -55,6 +56,7 @@ use session::Session;
5556
use lint::LintId;
5657
use lint::FutureIncompatibleInfo;
5758

59+
use redundant_semicolon::*;
5860
use nonstandard_style::*;
5961
use builtin::*;
6062
use types::*;
@@ -98,6 +100,7 @@ macro_rules! early_lint_passes {
98100
WhileTrue: WhileTrue,
99101
NonAsciiIdents: NonAsciiIdents,
100102
IncompleteFeatures: IncompleteFeatures,
103+
RedundantSemicolon: RedundantSemicolon,
101104
]);
102105
)
103106
}

Diff for: src/librustc_lint/redundant_semicolon.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use crate::lint::{EarlyLintPass, LintPass, EarlyContext, LintArray, LintContext};
2+
use syntax::ast::{Stmt, StmtKind, ExprKind};
3+
use syntax::errors::Applicability;
4+
5+
declare_lint! {
6+
pub REDUNDANT_SEMICOLON,
7+
Warn,
8+
"detects unnecessary trailing semicolons"
9+
}
10+
11+
declare_lint_pass!(RedundantSemicolon => [REDUNDANT_SEMICOLON]);
12+
13+
impl EarlyLintPass for RedundantSemicolon {
14+
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
15+
if let StmtKind::Semi(expr) = &stmt.node {
16+
if let ExprKind::Tup(ref v) = &expr.node {
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+
let mut err = cx.struct_span_lint(
30+
REDUNDANT_SEMICOLON,
31+
stmt.span,
32+
&msg
33+
);
34+
let suggest_msg = if multiple {
35+
"remove these semicolons"
36+
} else {
37+
"remove this semicolon"
38+
};
39+
err.span_suggestion(
40+
stmt.span,
41+
&suggest_msg,
42+
String::new(),
43+
Applicability::MaybeIncorrect
44+
);
45+
err.emit();
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
52+
}

Diff for: src/libsyntax/parse/parser/stmt.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,22 @@ impl<'a> Parser<'a> {
167167
if self.token == token::Semi {
168168
unused_attrs(&attrs, self);
169169
self.bump();
170-
return Ok(None);
170+
let mut last_semi = lo;
171+
while self.token == token::Semi {
172+
last_semi = self.token.span;
173+
self.bump();
174+
}
175+
// We are encoding a string of semicolons as an
176+
// an empty tuple that spans the excess semicolons
177+
// to preserve this info until the lint stage
178+
return Ok(Some(Stmt {
179+
id: ast::DUMMY_NODE_ID,
180+
span: lo.to(last_semi),
181+
node: StmtKind::Semi(self.mk_expr(lo.to(last_semi),
182+
ExprKind::Tup(Vec::new()),
183+
ThinVec::new()
184+
)),
185+
}));
171186
}
172187

173188
if self.token == token::CloseDelim(token::Brace) {

Diff for: src/test/ui/block-expr-precedence.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
warning: unnecessary trailing semicolons
2+
--> $DIR/block-expr-precedence.rs:60:21
3+
|
4+
LL | if (true) { 12; };;; -num;
5+
| ^^ help: remove these semicolons
6+
|
7+
= note: `#[warn(redundant_semicolon)]` on by default
8+

Diff for: src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ error: expected `{`, found `;`
2828
LL | if not // lack of braces is [sic]
2929
| -- this `if` statement has a condition, but no block
3030
LL | println!("Then when?");
31-
| ^ expected `{`
31+
| ^
32+
| |
33+
| expected `{`
34+
| help: try placing this code inside a block: `{ ; }`
3235

3336
error: unexpected `2` after identifier
3437
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:26:24

Diff for: src/test/ui/parser/doc-before-semi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ 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
68
}

Diff for: src/test/ui/parser/doc-before-semi.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ 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+
917
error: aborting due to previous error
1018

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

Diff for: src/test/ui/proc-macro/span-preservation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern crate test_macros;
99

1010
#[recollect_attr]
1111
fn a() {
12-
let x: usize = "hello";;;;; //~ ERROR mismatched types
12+
let x: usize = "hello"; //~ ERROR mismatched types
1313
}
1414

1515
#[recollect_attr]

Diff for: src/test/ui/proc-macro/span-preservation.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error[E0308]: mismatched types
66
error[E0308]: mismatched types
77
--> $DIR/span-preservation.rs:12:20
88
|
9-
LL | let x: usize = "hello";;;;;
9+
LL | let x: usize = "hello";
1010
| ^^^^^^^ expected usize, found reference
1111
|
1212
= note: expected type `usize`

0 commit comments

Comments
 (0)