]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint}; |
2 | use if_chain::if_chain; | |
3 | use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; | |
4 | use rustc_lint::{LateContext, LateLintPass}; | |
5 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
6 | ||
7 | declare_clippy_lint! { | |
8 | /// **What it does:** `exit()` terminates the program and doesn't provide a | |
9 | /// stack trace. | |
10 | /// | |
11 | /// **Why is this bad?** Ideally a program is terminated by finishing | |
12 | /// the main function. | |
13 | /// | |
14 | /// **Known problems:** None. | |
15 | /// | |
16 | /// **Example:** | |
17 | /// ```ignore | |
18 | /// std::process::exit(0) | |
19 | /// ``` | |
20 | pub EXIT, | |
21 | restriction, | |
22 | "`std::process::exit` is called, terminating the program" | |
23 | } | |
24 | ||
25 | declare_lint_pass!(Exit => [EXIT]); | |
26 | ||
27 | impl<'tcx> LateLintPass<'tcx> for Exit { | |
28 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
29 | if_chain! { | |
30 | if let ExprKind::Call(ref path_expr, ref _args) = e.kind; | |
31 | if let ExprKind::Path(ref path) = path_expr.kind; | |
32 | if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); | |
33 | if match_def_path(cx, def_id, &paths::EXIT); | |
34 | then { | |
35 | let parent = cx.tcx.hir().get_parent_item(e.hir_id); | |
36 | if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { | |
37 | // If the next item up is a function we check if it is an entry point | |
38 | // and only then emit a linter warning | |
39 | let def_id = cx.tcx.hir().local_def_id(parent); | |
40 | if !is_entrypoint_fn(cx, def_id.to_def_id()) { | |
41 | span_lint(cx, EXIT, e.span, "usage of `process::exit`"); | |
42 | } | |
43 | } | |
44 | } | |
45 | } | |
46 | } | |
47 | } |