Skip to content

Commit a813c05

Browse files
committed
Auto merge of #4203 - Urriel:feat/333_calling_main, r=flip1995
Add recursion check on main function Changes: - Add MainRecursion lint to Clippy - Check for no-std setup fixes #333 changelog: Add `main_recursion` lint
2 parents ca6a9be + dabf599 commit a813c05

10 files changed

+149
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ Released 2018-09-13
10001000
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
10011001
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
10021002
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
1003+
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
10031004
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
10041005
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
10051006
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

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

9-
[There are 309 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 310 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

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

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ pub mod let_if_seq;
208208
pub mod lifetimes;
209209
pub mod literal_representation;
210210
pub mod loops;
211+
pub mod main_recursion;
211212
pub mod map_clone;
212213
pub mod map_unit_fn;
213214
pub mod matches;
@@ -473,6 +474,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
473474
reg.register_late_lint_pass(box types::LetUnitValue);
474475
reg.register_late_lint_pass(box types::UnitCmp);
475476
reg.register_late_lint_pass(box loops::Loops);
477+
reg.register_late_lint_pass(box main_recursion::MainRecursion::default());
476478
reg.register_late_lint_pass(box lifetimes::Lifetimes);
477479
reg.register_late_lint_pass(box entry::HashMapPass);
478480
reg.register_late_lint_pass(box ranges::Ranges);
@@ -760,6 +762,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
760762
loops::WHILE_IMMUTABLE_CONDITION,
761763
loops::WHILE_LET_LOOP,
762764
loops::WHILE_LET_ON_ITERATOR,
765+
main_recursion::MAIN_RECURSION,
763766
map_clone::MAP_CLONE,
764767
map_unit_fn::OPTION_MAP_UNIT_FN,
765768
map_unit_fn::RESULT_MAP_UNIT_FN,
@@ -933,6 +936,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
933936
loops::FOR_KV_MAP,
934937
loops::NEEDLESS_RANGE_LOOP,
935938
loops::WHILE_LET_ON_ITERATOR,
939+
main_recursion::MAIN_RECURSION,
936940
map_clone::MAP_CLONE,
937941
matches::MATCH_BOOL,
938942
matches::MATCH_OVERLAPPING_ARM,

clippy_lints/src/main_recursion.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use rustc::hir::{Crate, Expr, ExprKind, QPath};
2+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
3+
use rustc::{declare_tool_lint, impl_lint_pass};
4+
use syntax::symbol::sym;
5+
6+
use crate::utils::{is_entrypoint_fn, snippet, span_help_and_lint};
7+
use if_chain::if_chain;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for recursion using the entrypoint.
11+
///
12+
/// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),
13+
/// recursing into main() seems like an unintuitive antipattern we should be able to detect.
14+
///
15+
/// **Known problems:** None.
16+
///
17+
/// **Example:**
18+
/// ```no_run
19+
/// fn main() {
20+
/// main();
21+
/// }
22+
/// ```
23+
pub MAIN_RECURSION,
24+
style,
25+
"recursion using the entrypoint"
26+
}
27+
28+
#[derive(Default)]
29+
pub struct MainRecursion {
30+
has_no_std_attr: bool,
31+
}
32+
33+
impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
34+
35+
impl LateLintPass<'_, '_> for MainRecursion {
36+
fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate) {
37+
self.has_no_std_attr = krate.attrs.iter().any(|attr| attr.path == sym::no_std);
38+
}
39+
40+
fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
41+
if self.has_no_std_attr {
42+
return;
43+
}
44+
45+
if_chain! {
46+
if let ExprKind::Call(func, _) = &expr.node;
47+
if let ExprKind::Path(path) = &func.node;
48+
if let QPath::Resolved(_, path) = &path;
49+
if let Some(def_id) = path.res.opt_def_id();
50+
if is_entrypoint_fn(cx, def_id);
51+
then {
52+
span_help_and_lint(
53+
cx,
54+
MAIN_RECURSION,
55+
func.span,
56+
&format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
57+
"consider using another function for this recursion"
58+
)
59+
}
60+
}
61+
}
62+
}

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 309] = [
9+
pub const ALL_LINTS: [Lint; 310] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -917,6 +917,13 @@ pub const ALL_LINTS: [Lint; 309] = [
917917
deprecation: None,
918918
module: "booleans",
919919
},
920+
Lint {
921+
name: "main_recursion",
922+
group: "style",
923+
desc: "recursion using the entrypoint",
924+
deprecation: None,
925+
module: "main_recursion",
926+
},
920927
Lint {
921928
name: "manual_memcpy",
922929
group: "perf",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// ignore-macos
2+
// ignore-windows
3+
4+
#![feature(main)]
5+
6+
#[warn(clippy::main_recursion)]
7+
#[allow(unconditional_recursion)]
8+
#[main]
9+
fn a() {
10+
println!("Hello, World!");
11+
a();
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: recursing into entrypoint `a`
2+
--> $DIR/entrypoint_recursion.rs:11:5
3+
|
4+
LL | a();
5+
| ^
6+
|
7+
= note: `-D clippy::main-recursion` implied by `-D warnings`
8+
= help: consider using another function for this recursion
9+
10+
error: aborting due to previous error
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// ignore-macos
2+
// ignore-windows
3+
4+
#![feature(lang_items, link_args, start, libc)]
5+
#![link_args = "-nostartfiles"]
6+
#![no_std]
7+
8+
use core::panic::PanicInfo;
9+
use core::sync::atomic::{AtomicUsize, Ordering};
10+
11+
static N: AtomicUsize = AtomicUsize::new(0);
12+
13+
#[warn(clippy::main_recursion)]
14+
#[start]
15+
fn main(argc: isize, argv: *const *const u8) -> isize {
16+
let x = N.load(Ordering::Relaxed);
17+
N.store(x + 1, Ordering::Relaxed);
18+
19+
if x < 3 {
20+
main(argc, argv);
21+
}
22+
23+
0
24+
}
25+
26+
#[allow(clippy::empty_loop)]
27+
#[panic_handler]
28+
fn panic(_info: &PanicInfo) -> ! {
29+
loop {}
30+
}
31+
32+
#[lang = "eh_personality"]
33+
extern "C" fn eh_personality() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[warn(clippy::main_recursion)]
2+
#[allow(unconditional_recursion)]
3+
fn main() {
4+
println!("Hello, World!");
5+
main();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: recursing into entrypoint `main`
2+
--> $DIR/std_main_recursion.rs:5:5
3+
|
4+
LL | main();
5+
| ^^^^
6+
|
7+
= note: `-D clippy::main-recursion` implied by `-D warnings`
8+
= help: consider using another function for this recursion
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)