|
| 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 | +} |
0 commit comments