about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--clippy_lints/src/forced_return.rs106
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--tests/ui/forced_return.rs55
-rw-r--r--tests/ui/forced_return.stderr46
5 files changed, 211 insertions, 1 deletions
diff --git a/README.md b/README.md
index 0d83224e3f6..92bb4586688 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are 289 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are 290 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
 
diff --git a/clippy_lints/src/forced_return.rs b/clippy_lints/src/forced_return.rs
new file mode 100644
index 00000000000..ee30bd0ab1e
--- /dev/null
+++ b/clippy_lints/src/forced_return.rs
@@ -0,0 +1,106 @@
+// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl};
+use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
+use crate::rustc::{declare_tool_lint, lint_array};
+use crate::rustc_errors::Applicability;
+use crate::syntax::{ast::NodeId, source_map::Span};
+use crate::utils::{snippet_opt, span_lint_and_then};
+
+/// **What it does:** Checks for missing return statements at the end of a block.
+///
+/// **Why is this bad?** Actually it is idiomatic Rust code. Programmers coming
+/// from other languages might prefer the expressiveness of `return`.
+///
+/// **Known problems:** None.
+///
+/// **Example:**
+/// ```rust
+/// fn foo(x: usize) {
+///     x
+/// }
+/// ```
+/// add return
+/// ```rust
+/// fn foo(x: usize) {
+///     return x;
+/// }
+/// ```
+declare_clippy_lint! {
+    pub FORCED_RETURN,
+    restriction,
+    "use a return statement like `return expr` instead of an expression"
+}
+
+pub struct ForcedReturnPass;
+
+impl ForcedReturnPass {
+    fn show_suggestion(cx: &LateContext<'_, '_>, span: syntax_pos::Span) {
+        span_lint_and_then(cx, FORCED_RETURN, span, "missing return statement", |db| {
+            if let Some(snippet) = snippet_opt(cx, span) {
+                db.span_suggestion_with_applicability(
+                    span,
+                    "add `return` as shown",
+                    format!("return {}", snippet),
+                    Applicability::MachineApplicable,
+                );
+            }
+        });
+    }
+
+    fn expr_match(cx: &LateContext<'_, '_>, kind: &ExprKind) {
+        match kind {
+            ExprKind::Block(ref block, ..) => {
+                if let Some(ref expr) = block.expr {
+                    Self::expr_match(cx, &expr.node);
+                }
+            },
+            ExprKind::If(.., if_expr, else_expr) => {
+                Self::expr_match(cx, &if_expr.node);
+
+                if let Some(else_expr) = else_expr {
+                    Self::expr_match(cx, &else_expr.node);
+                }
+            },
+            ExprKind::Match(_, arms, ..) => {
+                for arm in arms {
+                    Self::expr_match(cx, &arm.body.node);
+                }
+            },
+            ExprKind::Lit(lit) => Self::show_suggestion(cx, lit.span),
+            _ => (),
+        }
+    }
+}
+
+impl LintPass for ForcedReturnPass {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(FORCED_RETURN)
+    }
+}
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ForcedReturnPass {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'a, 'tcx>,
+        _: FnKind<'tcx>,
+        _: &'tcx FnDecl,
+        body: &'tcx Body,
+        _: Span,
+        _: NodeId,
+    ) {
+        let def_id = cx.tcx.hir.body_owner_def_id(body.id());
+        let mir = cx.tcx.optimized_mir(def_id);
+
+        if !mir.return_ty().is_unit() {
+            Self::expr_match(cx, &body.value.node);
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 326bf884e33..729e3a20c2c 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -185,6 +185,7 @@ pub mod reference;
 pub mod regex;
 pub mod replace_consts;
 pub mod returns;
+pub mod forced_return;
 pub mod serde_api;
 pub mod shadow;
 pub mod slow_vector_initialization;
@@ -371,6 +372,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
     reg.register_late_lint_pass(box unicode::Unicode);
     reg.register_late_lint_pass(box strings::StringAdd);
     reg.register_early_lint_pass(box returns::ReturnPass);
+    reg.register_late_lint_pass(box forced_return::ForcedReturnPass);
     reg.register_late_lint_pass(box methods::Pass);
     reg.register_late_lint_pass(box map_clone::Pass);
     reg.register_late_lint_pass(box shadow::Pass);
@@ -502,6 +504,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         strings::STRING_ADD,
         write::PRINT_STDOUT,
         write::USE_DEBUG,
+        forced_return::FORCED_RETURN,
     ]);
 
     reg.register_lint_group("clippy::pedantic", Some("clippy_pedantic"), vec![
diff --git a/tests/ui/forced_return.rs b/tests/ui/forced_return.rs
new file mode 100644
index 00000000000..5f07d99528e
--- /dev/null
+++ b/tests/ui/forced_return.rs
@@ -0,0 +1,55 @@
+// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+
+
+
+#![warn(clippy::forced_return)]
+
+fn test_end_of_fn() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    true
+}
+
+#[allow(clippy::needless_bool)]
+fn test_if_block() -> bool {
+    if true {
+        true
+    } else {
+        false
+    }
+}
+
+#[allow(clippy::match_bool)]
+fn test_match(x: bool) -> bool {
+    match x {
+        true => false,
+        false => {
+            true
+        }
+    }
+}
+
+fn test_closure() {
+    let _ = || {
+        true
+    };
+    let _ = || true;
+}
+
+fn main() {
+    let _ = test_end_of_fn();
+    let _ = test_if_block();
+    let _ = test_match(true);
+    test_closure();
+}
diff --git a/tests/ui/forced_return.stderr b/tests/ui/forced_return.stderr
new file mode 100644
index 00000000000..0b1dcc4ce33
--- /dev/null
+++ b/tests/ui/forced_return.stderr
@@ -0,0 +1,46 @@
+error: missing return statement
+  --> $DIR/forced_return.rs:21:5
+   |
+21 |     true
+   |     ^^^^ help: add `return` as shown: `return true`
+   |
+   = note: `-D clippy::forced-return` implied by `-D warnings`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:27:9
+   |
+27 |         true
+   |         ^^^^ help: add `return` as shown: `return true`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:29:9
+   |
+29 |         false
+   |         ^^^^^ help: add `return` as shown: `return false`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:36:17
+   |
+36 |         true => false,
+   |                 ^^^^^ help: add `return` as shown: `return false`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:38:13
+   |
+38 |             true
+   |             ^^^^ help: add `return` as shown: `return true`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:45:9
+   |
+45 |         true
+   |         ^^^^ help: add `return` as shown: `return true`
+
+error: missing return statement
+  --> $DIR/forced_return.rs:47:16
+   |
+47 |     let _ = || true;
+   |                ^^^^ help: add `return` as shown: `return true`
+
+error: aborting due to 7 previous errors
+