|
1 | | - |
2 | | -use syntax::ast::{Crate, Expr, ExprKind}; |
3 | | -use syntax::symbol::sym; |
4 | | -use rustc::lint::{LintArray, LintPass, EarlyLintPass, EarlyContext}; |
| 1 | +use rustc::hir::{Crate, Expr, ExprKind, QPath}; |
| 2 | +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; |
5 | 3 | use rustc::{declare_tool_lint, impl_lint_pass}; |
| 4 | +use syntax::symbol::sym; |
6 | 5 |
|
| 6 | +use crate::utils::{is_entrypoint_fn, snippet, span_help_and_lint}; |
7 | 7 | use if_chain::if_chain; |
8 | | -use crate::utils::span_help_and_lint; |
9 | 8 |
|
10 | 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 | + /// ``` |
11 | 23 | pub MAIN_RECURSION, |
12 | | - pedantic, |
13 | | - "function named `foo`, which is not a descriptive name" |
| 24 | + style, |
| 25 | + "recursion using the entrypoint" |
14 | 26 | } |
15 | 27 |
|
| 28 | +#[derive(Default)] |
16 | 29 | pub struct MainRecursion { |
17 | | - has_no_std_attr: bool |
| 30 | + has_no_std_attr: bool, |
18 | 31 | } |
19 | 32 |
|
20 | 33 | impl_lint_pass!(MainRecursion => [MAIN_RECURSION]); |
21 | 34 |
|
22 | | -impl MainRecursion { |
23 | | - pub fn new() -> MainRecursion { |
24 | | - MainRecursion { |
25 | | - has_no_std_attr: false |
26 | | - } |
27 | | - } |
28 | | -} |
29 | | - |
30 | | -impl EarlyLintPass for MainRecursion { |
31 | | - fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) { |
| 35 | +impl LateLintPass<'_, '_> for MainRecursion { |
| 36 | + fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate) { |
32 | 37 | self.has_no_std_attr = krate.attrs.iter().any(|attr| attr.path == sym::no_std); |
33 | 38 | } |
34 | 39 |
|
35 | | - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { |
| 40 | + fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) { |
36 | 41 | if self.has_no_std_attr { |
37 | 42 | return; |
38 | 43 | } |
39 | 44 |
|
40 | 45 | if_chain! { |
41 | 46 | if let ExprKind::Call(func, _) = &expr.node; |
42 | | - if let ExprKind::Path(_, path) = &func.node; |
43 | | - if *path == sym::main; |
| 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); |
44 | 51 | then { |
45 | 52 | span_help_and_lint( |
46 | 53 | cx, |
47 | 54 | MAIN_RECURSION, |
48 | | - expr.span, |
49 | | - "You are recursing into main()", |
50 | | - "Consider using another function for this recursion" |
| 55 | + func.span, |
| 56 | + &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), |
| 57 | + "consider using another function for this recursion" |
51 | 58 | ) |
52 | 59 | } |
53 | 60 | } |
|
0 commit comments