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 } ;
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 ] ) ;
@@ -48,10 +61,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
48
61
&& let Some ( def_id) = cx. qpath_res ( path, path_expr. hir_id ) . opt_def_id ( )
49
62
&& cx. tcx . is_diagnostic_item ( sym:: process_exit, def_id)
50
63
&& let parent = cx. tcx . hir_get_parent_item ( e. hir_id )
51
- && let OwnerNode :: Item ( Item { kind : ItemKind :: Fn { .. } , ..} ) = cx. tcx . hir_owner_node ( parent)
52
- // If the next item up is a function we check if it is an entry point
64
+ && let OwnerNode :: Item ( Item { kind : ItemKind :: Fn { ident , .. } , ..} ) = cx. tcx . hir_owner_node ( parent)
65
+ // If the next item up is a function we check if it isn't named "main"
53
66
// and only then emit a linter warning
54
- && !is_entrypoint_fn ( cx, parent. to_def_id ( ) )
67
+
68
+ // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before,
69
+ // in compilation contexts like --all-targets (which include --tests), you get false positives
70
+ // because in a test context, main is not the entrypoint function
71
+ && ident. name != sym:: main
55
72
{
56
73
span_lint ( cx, EXIT , e. span , "usage of `process::exit`" ) ;
57
74
}
0 commit comments