about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-08-14 22:56:18 +0200
committerGitHub <noreply@github.com>2019-08-14 22:56:18 +0200
commit7178cf5f970a4f32afd260dda4f87223d8a9ba96 (patch)
tree1663a71b9c9cf82ff994b7ce35eaf1d52d170c27 /src
parentc43d03a19f326f4a323569328cc501e86eb6d22e (diff)
parent76a134524295a04ed5f336265239ef5f39d089de (diff)
downloadrust-7178cf5f970a4f32afd260dda4f87223d8a9ba96.tar.gz
rust-7178cf5f970a4f32afd260dda4f87223d8a9ba96.zip
Rollup merge of #62984 - nathanwhit:extra_semi_lint, r=varkor
Add lint for excess trailing semicolons

Closes #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;;
   |                ^

```
Diffstat (limited to 'src')
-rw-r--r--src/librustc_lint/lib.rs3
-rw-r--r--src/librustc_lint/redundant_semicolon.rs52
-rw-r--r--src/libsyntax/parse/parser/stmt.rs17
-rw-r--r--src/test/ui/block-expr-precedence.stderr8
-rw-r--r--src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr5
-rw-r--r--src/test/ui/parser/doc-before-semi.rs2
-rw-r--r--src/test/ui/parser/doc-before-semi.stderr8
-rw-r--r--src/test/ui/proc-macro/span-preservation.rs2
-rw-r--r--src/test/ui/proc-macro/span-preservation.stderr2
9 files changed, 95 insertions, 4 deletions
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 3a540fdf4b9..fc416be8eeb 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -24,6 +24,7 @@ extern crate rustc;
 
 mod error_codes;
 mod nonstandard_style;
+mod redundant_semicolon;
 pub mod builtin;
 mod types;
 mod unused;
@@ -55,6 +56,7 @@ use session::Session;
 use lint::LintId;
 use lint::FutureIncompatibleInfo;
 
+use redundant_semicolon::*;
 use nonstandard_style::*;
 use builtin::*;
 use types::*;
@@ -98,6 +100,7 @@ macro_rules! early_lint_passes {
             WhileTrue: WhileTrue,
             NonAsciiIdents: NonAsciiIdents,
             IncompleteFeatures: IncompleteFeatures,
+            RedundantSemicolon: RedundantSemicolon,
         ]);
     )
 }
diff --git a/src/librustc_lint/redundant_semicolon.rs b/src/librustc_lint/redundant_semicolon.rs
new file mode 100644
index 00000000000..7c9df3578b5
--- /dev/null
+++ b/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();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs
index f182edcbff4..750d8fbbddc 100644
--- a/src/libsyntax/parse/parser/stmt.rs
+++ b/src/libsyntax/parse/parser/stmt.rs
@@ -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) {
diff --git a/src/test/ui/block-expr-precedence.stderr b/src/test/ui/block-expr-precedence.stderr
new file mode 100644
index 00000000000..1307b5d6ddd
--- /dev/null
+++ b/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
+
diff --git a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
index f1c93d54637..f5edbe2a3af 100644
--- a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
+++ b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
@@ -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
diff --git a/src/test/ui/parser/doc-before-semi.rs b/src/test/ui/parser/doc-before-semi.rs
index 405a7e1e2a3..c3f478fe420 100644
--- a/src/test/ui/parser/doc-before-semi.rs
+++ b/src/test/ui/parser/doc-before-semi.rs
@@ -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
 }
diff --git a/src/test/ui/parser/doc-before-semi.stderr b/src/test/ui/parser/doc-before-semi.stderr
index e6bade18d0a..b9ac30b09b2 100644
--- a/src/test/ui/parser/doc-before-semi.stderr
+++ b/src/test/ui/parser/doc-before-semi.stderr
@@ -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`.
diff --git a/src/test/ui/proc-macro/span-preservation.rs b/src/test/ui/proc-macro/span-preservation.rs
index 0a82d28e9e5..55835cb88f4 100644
--- a/src/test/ui/proc-macro/span-preservation.rs
+++ b/src/test/ui/proc-macro/span-preservation.rs
@@ -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]
diff --git a/src/test/ui/proc-macro/span-preservation.stderr b/src/test/ui/proc-macro/span-preservation.stderr
index cf03deee7e4..0290f4b2cc9 100644
--- a/src/test/ui/proc-macro/span-preservation.stderr
+++ b/src/test/ui/proc-macro/span-preservation.stderr
@@ -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`