Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d5d6692

Browse files
committed
Added FORCED_RETURN lint.
1 parent 3f24cdf commit d5d6692

File tree

5 files changed

+211
-1
lines changed

5 files changed

+211
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
99

10-
[There are 289 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
10+
[There are 290 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1111

1212
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1313

clippy_lints/src/forced_return.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
use crate::rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl};
11+
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
12+
use crate::rustc::{declare_tool_lint, lint_array};
13+
use crate::rustc_errors::Applicability;
14+
use crate::syntax::{ast::NodeId, source_map::Span};
15+
use crate::utils::{snippet_opt, span_lint_and_then};
16+
17+
/// **What it does:** Checks for missing return statements at the end of a block.
18+
///
19+
/// **Why is this bad?** Actually it is idiomatic Rust code. Programmers coming
20+
/// from other languages might prefer the expressiveness of `return`.
21+
///
22+
/// **Known problems:** None.
23+
///
24+
/// **Example:**
25+
/// ```rust
26+
/// fn foo(x: usize) {
27+
/// x
28+
/// }
29+
/// ```
30+
/// add return
31+
/// ```rust
32+
/// fn foo(x: usize) {
33+
/// return x;
34+
/// }
35+
/// ```
36+
declare_clippy_lint! {
37+
pub FORCED_RETURN,
38+
restriction,
39+
"use a return statement like `return expr` instead of an expression"
40+
}
41+
42+
pub struct ForcedReturnPass;
43+
44+
impl ForcedReturnPass {
45+
fn show_suggestion(cx: &LateContext<'_, '_>, span: syntax_pos::Span) {
46+
span_lint_and_then(cx, FORCED_RETURN, span, "missing return statement", |db| {
47+
if let Some(snippet) = snippet_opt(cx, span) {
48+
db.span_suggestion_with_applicability(
49+
span,
50+
"add `return` as shown",
51+
format!("return {}", snippet),
52+
Applicability::MachineApplicable,
53+
);
54+
}
55+
});
56+
}
57+
58+
fn expr_match(cx: &LateContext<'_, '_>, kind: &ExprKind) {
59+
match kind {
60+
ExprKind::Block(ref block, ..) => {
61+
if let Some(ref expr) = block.expr {
62+
Self::expr_match(cx, &expr.node);
63+
}
64+
},
65+
ExprKind::If(.., if_expr, else_expr) => {
66+
Self::expr_match(cx, &if_expr.node);
67+
68+
if let Some(else_expr) = else_expr {
69+
Self::expr_match(cx, &else_expr.node);
70+
}
71+
},
72+
ExprKind::Match(_, arms, ..) => {
73+
for arm in arms {
74+
Self::expr_match(cx, &arm.body.node);
75+
}
76+
},
77+
ExprKind::Lit(lit) => Self::show_suggestion(cx, lit.span),
78+
_ => (),
79+
}
80+
}
81+
}
82+
83+
impl LintPass for ForcedReturnPass {
84+
fn get_lints(&self) -> LintArray {
85+
lint_array!(FORCED_RETURN)
86+
}
87+
}
88+
89+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ForcedReturnPass {
90+
fn check_fn(
91+
&mut self,
92+
cx: &LateContext<'a, 'tcx>,
93+
_: FnKind<'tcx>,
94+
_: &'tcx FnDecl,
95+
body: &'tcx Body,
96+
_: Span,
97+
_: NodeId,
98+
) {
99+
let def_id = cx.tcx.hir.body_owner_def_id(body.id());
100+
let mir = cx.tcx.optimized_mir(def_id);
101+
102+
if !mir.return_ty().is_unit() {
103+
Self::expr_match(cx, &body.value.node);
104+
}
105+
}
106+
}

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ pub mod reference;
185185
pub mod regex;
186186
pub mod replace_consts;
187187
pub mod returns;
188+
pub mod forced_return;
188189
pub mod serde_api;
189190
pub mod shadow;
190191
pub mod slow_vector_initialization;
@@ -371,6 +372,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
371372
reg.register_late_lint_pass(box unicode::Unicode);
372373
reg.register_late_lint_pass(box strings::StringAdd);
373374
reg.register_early_lint_pass(box returns::ReturnPass);
375+
reg.register_late_lint_pass(box forced_return::ForcedReturnPass);
374376
reg.register_late_lint_pass(box methods::Pass);
375377
reg.register_late_lint_pass(box map_clone::Pass);
376378
reg.register_late_lint_pass(box shadow::Pass);
@@ -502,6 +504,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
502504
strings::STRING_ADD,
503505
write::PRINT_STDOUT,
504506
write::USE_DEBUG,
507+
forced_return::FORCED_RETURN,
505508
]);
506509

507510
reg.register_lint_group("clippy::pedantic", Some("clippy_pedantic"), vec![

tests/ui/forced_return.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
11+
12+
13+
14+
#![warn(clippy::forced_return)]
15+
16+
fn test_end_of_fn() -> bool {
17+
if true {
18+
// no error!
19+
return true;
20+
}
21+
true
22+
}
23+
24+
#[allow(clippy::needless_bool)]
25+
fn test_if_block() -> bool {
26+
if true {
27+
true
28+
} else {
29+
false
30+
}
31+
}
32+
33+
#[allow(clippy::match_bool)]
34+
fn test_match(x: bool) -> bool {
35+
match x {
36+
true => false,
37+
false => {
38+
true
39+
}
40+
}
41+
}
42+
43+
fn test_closure() {
44+
let _ = || {
45+
true
46+
};
47+
let _ = || true;
48+
}
49+
50+
fn main() {
51+
let _ = test_end_of_fn();
52+
let _ = test_if_block();
53+
let _ = test_match(true);
54+
test_closure();
55+
}

tests/ui/forced_return.stderr

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: missing return statement
2+
--> $DIR/forced_return.rs:21:5
3+
|
4+
21 | true
5+
| ^^^^ help: add `return` as shown: `return true`
6+
|
7+
= note: `-D clippy::forced-return` implied by `-D warnings`
8+
9+
error: missing return statement
10+
--> $DIR/forced_return.rs:27:9
11+
|
12+
27 | true
13+
| ^^^^ help: add `return` as shown: `return true`
14+
15+
error: missing return statement
16+
--> $DIR/forced_return.rs:29:9
17+
|
18+
29 | false
19+
| ^^^^^ help: add `return` as shown: `return false`
20+
21+
error: missing return statement
22+
--> $DIR/forced_return.rs:36:17
23+
|
24+
36 | true => false,
25+
| ^^^^^ help: add `return` as shown: `return false`
26+
27+
error: missing return statement
28+
--> $DIR/forced_return.rs:38:13
29+
|
30+
38 | true
31+
| ^^^^ help: add `return` as shown: `return true`
32+
33+
error: missing return statement
34+
--> $DIR/forced_return.rs:45:9
35+
|
36+
45 | true
37+
| ^^^^ help: add `return` as shown: `return true`
38+
39+
error: missing return statement
40+
--> $DIR/forced_return.rs:47:16
41+
|
42+
47 | let _ = || true;
43+
| ^^^^ help: add `return` as shown: `return true`
44+
45+
error: aborting due to 7 previous errors
46+

0 commit comments

Comments
 (0)