1
1
use clippy_utils:: diagnostics:: span_lint;
2
- use clippy_utils:: is_entrypoint_fn;
3
2
use rustc_hir:: { Expr , ExprKind , Item , ItemKind , OwnerNode } ;
4
3
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
5
4
use rustc_session:: declare_lint_pass;
6
5
use rustc_span:: sym;
7
6
8
7
declare_clippy_lint ! {
9
8
/// ### What it does
10
- /// Detects calls to the `exit()` function which terminates the program.
9
+ /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()`
10
+ /// immediately terminate the program.
11
11
///
12
12
/// ### Why restrict this?
13
13
/// `exit()` immediately terminates the program with no information other than an exit code.
14
14
/// This provides no means to troubleshoot a problem, and may be an unexpected side effect.
15
15
///
16
16
/// Codebases may use this lint to require that all exits are performed either by panicking
17
17
/// (which produces a message, a code location, and optionally a backtrace)
18
- /// or by returning from `main()` (which is a single place to look).
18
+ /// or by calling `exit()` from `main()` (which is a single place to look).
19
19
///
20
- /// ### Example
20
+ /// ### Good example
21
21
/// ```no_run
22
- /// std::process::exit(0)
22
+ /// fn main() {
23
+ /// std::process::exit(0);
24
+ /// }
25
+ /// ```
26
+ ///
27
+ /// ### Bad example
28
+ /// ```no_run
29
+ /// fn main() {
30
+ /// other_function();
31
+ /// }
32
+ ///
33
+ /// fn other_function() {
34
+ /// std::process::exit(0);
35
+ /// }
23
36
/// ```
24
37
///
25
38
/// Use instead:
@@ -36,7 +49,7 @@ declare_clippy_lint! {
36
49
#[ clippy:: version = "1.41.0" ]
37
50
pub EXIT ,
38
51
restriction,
39
- "detects `std::process::exit` calls"
52
+ "detects `std::process::exit` calls outside of `main` "
40
53
}
41
54
42
55
declare_lint_pass ! ( Exit => [ EXIT ] ) ;
@@ -52,10 +65,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
52
65
&& let Some ( def_id) = cx. qpath_res ( path, path_expr. hir_id ) . opt_def_id ( )
53
66
&& cx. tcx . is_diagnostic_item ( sym:: process_exit, def_id)
54
67
&& let parent = cx. tcx . hir_get_parent_item ( e. hir_id )
55
- && let OwnerNode :: Item ( Item { kind : ItemKind :: Fn { .. } , ..} ) = cx. tcx . hir_owner_node ( parent)
56
- // If the next item up is a function we check if it is an entry point
68
+ && let OwnerNode :: Item ( Item { kind : ItemKind :: Fn { ident , .. } , ..} ) = cx. tcx . hir_owner_node ( parent)
69
+ // If the next item up is a function we check if it isn't named "main"
57
70
// and only then emit a linter warning
58
- && !is_entrypoint_fn ( cx, parent. to_def_id ( ) )
71
+
72
+ // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before,
73
+ // in compilation contexts like --all-targets (which include --tests), you get false positives
74
+ // because in a test context, main is not the entrypoint function
75
+ && ident. name . as_str ( ) != "main"
59
76
{
60
77
span_lint ( cx, EXIT , e. span , "usage of `process::exit`" ) ;
61
78
}
0 commit comments