]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/main_recursion.rs
Rustup to rustc 1.40.0-nightly (7a76fe76f 2019-11-07)
[rust.git] / clippy_lints / src / main_recursion.rs
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::ast::AttrKind;
5 use syntax::symbol::sym;
6
7 use crate::utils::{is_entrypoint_fn, snippet, span_help_and_lint};
8 use if_chain::if_chain;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks for recursion using the entrypoint.
12     ///
13     /// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),
14     /// recursing into main() seems like an unintuitive antipattern we should be able to detect.
15     ///
16     /// **Known problems:** None.
17     ///
18     /// **Example:**
19     /// ```no_run
20     /// fn main() {
21     ///     main();
22     /// }
23     /// ```
24     pub MAIN_RECURSION,
25     style,
26     "recursion using the entrypoint"
27 }
28
29 #[derive(Default)]
30 pub struct MainRecursion {
31     has_no_std_attr: bool,
32 }
33
34 impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
35
36 impl LateLintPass<'_, '_> for MainRecursion {
37     fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate) {
38         self.has_no_std_attr = krate.attrs.iter().any(|attr| {
39             if let AttrKind::Normal(ref attr) = attr.kind {
40                 attr.path == sym::no_std
41             } else {
42                 false
43             }
44         });
45     }
46
47     fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
48         if self.has_no_std_attr {
49             return;
50         }
51
52         if_chain! {
53             if let ExprKind::Call(func, _) = &expr.kind;
54             if let ExprKind::Path(path) = &func.kind;
55             if let QPath::Resolved(_, path) = &path;
56             if let Some(def_id) = path.res.opt_def_id();
57             if is_entrypoint_fn(cx, def_id);
58             then {
59                 span_help_and_lint(
60                     cx,
61                     MAIN_RECURSION,
62                     func.span,
63                     &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
64                     "consider using another function for this recursion"
65                 )
66             }
67         }
68     }
69 }