Skip to content

Commit

Permalink
Rollup merge of rust-lang#62984 - nathanwhit:extra_semi_lint, r=varkor
Browse files Browse the repository at this point in the history
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;;
   |                ^

```
  • Loading branch information
Centril committed Aug 14, 2019
2 parents c43d03a + 76a1345 commit 7178cf5
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/librustc_lint/lib.rs
Expand Up @@ -24,6 +24,7 @@ extern crate rustc;

mod error_codes;
mod nonstandard_style;
mod redundant_semicolon;
pub mod builtin;
mod types;
mod unused;
Expand Down Expand Up @@ -55,6 +56,7 @@ use session::Session;
use lint::LintId;
use lint::FutureIncompatibleInfo;

use redundant_semicolon::*;
use nonstandard_style::*;
use builtin::*;
use types::*;
Expand Down Expand Up @@ -98,6 +100,7 @@ macro_rules! early_lint_passes {
WhileTrue: WhileTrue,
NonAsciiIdents: NonAsciiIdents,
IncompleteFeatures: IncompleteFeatures,
RedundantSemicolon: RedundantSemicolon,
]);
)
}
Expand Down
52 changes: 52 additions & 0 deletions src/librustc_lint/redundant_semicolon.rs
@@ -0,0 +1,52 @@
use crate::lint::{EarlyLintPass, LintPass, EarlyContext, LintArray, LintContext};
use syntax::ast::{Stmt, StmtKind, ExprKind};
use syntax::errors::Applicability;

declare_lint! {
pub REDUNDANT_SEMICOLON,
Warn,
"detects unnecessary trailing semicolons"
}

declare_lint_pass!(RedundantSemicolon => [REDUNDANT_SEMICOLON]);

impl EarlyLintPass for RedundantSemicolon {
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
if let StmtKind::Semi(expr) = &stmt.node {
if let ExprKind::Tup(ref v) = &expr.node {
if v.is_empty() {
// Strings of excess semicolons are encoded as empty tuple expressions
// during the parsing stage, so we check for empty tuple expressions
// which span only semicolons
if let Ok(source_str) = cx.sess().source_map().span_to_snippet(stmt.span) {
if source_str.chars().all(|c| c == ';') {
let multiple = (stmt.span.hi() - stmt.span.lo()).0 > 1;
let msg = if multiple {
"unnecessary trailing semicolons"
} else {
"unnecessary trailing semicolon"
};
let mut err = cx.struct_span_lint(
REDUNDANT_SEMICOLON,
stmt.span,
&msg
);
let suggest_msg = if multiple {
"remove these semicolons"
} else {
"remove this semicolon"
};
err.span_suggestion(
stmt.span,
&suggest_msg,
String::new(),
Applicability::MaybeIncorrect
);
err.emit();
}
}
}
}
}
}
}
17 changes: 16 additions & 1 deletion src/libsyntax/parse/parser/stmt.rs
Expand Up @@ -167,7 +167,22 @@ impl<'a> Parser<'a> {
if self.token == token::Semi {
unused_attrs(&attrs, self);
self.bump();
return Ok(None);
let mut last_semi = lo;
while self.token == token::Semi {
last_semi = self.token.span;
self.bump();
}
// We are encoding a string of semicolons as an
// an empty tuple that spans the excess semicolons
// to preserve this info until the lint stage
return Ok(Some(Stmt {
id: ast::DUMMY_NODE_ID,
span: lo.to(last_semi),
node: StmtKind::Semi(self.mk_expr(lo.to(last_semi),
ExprKind::Tup(Vec::new()),
ThinVec::new()
)),
}));
}

if self.token == token::CloseDelim(token::Brace) {
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/block-expr-precedence.stderr
@@ -0,0 +1,8 @@
warning: unnecessary trailing semicolons
--> $DIR/block-expr-precedence.rs:60:21
|
LL | if (true) { 12; };;; -num;
| ^^ help: remove these semicolons
|
= note: `#[warn(redundant_semicolon)]` on by default

Expand Up @@ -28,7 +28,10 @@ error: expected `{`, found `;`
LL | if not // lack of braces is [sic]
| -- this `if` statement has a condition, but no block
LL | println!("Then when?");
| ^ expected `{`
| ^
| |
| expected `{`
| help: try placing this code inside a block: `{ ; }`

error: unexpected `2` after identifier
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:26:24
Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/parser/doc-before-semi.rs
Expand Up @@ -3,4 +3,6 @@ fn main() {
//~^ ERROR found a documentation comment that doesn't document anything
//~| HELP maybe a comment was intended
;
//~^ WARNING unnecessary trailing semicolon
//~| HELP remove this semicolon
}
8 changes: 8 additions & 0 deletions src/test/ui/parser/doc-before-semi.stderr
Expand Up @@ -6,6 +6,14 @@ LL | /// hi
|
= help: doc comments must come before what they document, maybe a comment was intended with `//`?

warning: unnecessary trailing semicolon
--> $DIR/doc-before-semi.rs:5:5
|
LL | ;
| ^ help: remove this semicolon
|
= note: `#[warn(redundant_semicolon)]` on by default

error: aborting due to previous error

For more information about this error, try `rustc --explain E0585`.
2 changes: 1 addition & 1 deletion src/test/ui/proc-macro/span-preservation.rs
Expand Up @@ -9,7 +9,7 @@ extern crate test_macros;

#[recollect_attr]
fn a() {
let x: usize = "hello";;;;; //~ ERROR mismatched types
let x: usize = "hello"; //~ ERROR mismatched types
}

#[recollect_attr]
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/proc-macro/span-preservation.stderr
Expand Up @@ -6,7 +6,7 @@ error[E0308]: mismatched types
error[E0308]: mismatched types
--> $DIR/span-preservation.rs:12:20
|
LL | let x: usize = "hello";;;;;
LL | let x: usize = "hello";
| ^^^^^^^ expected usize, found reference
|
= note: expected type `usize`
Expand Down

0 comments on commit 7178cf5

Please sign in to comment.