11use  clippy_utils:: diagnostics:: span_lint; 
2- use  clippy_utils:: is_entrypoint_fn; 
32use  rustc_hir:: { Expr ,  ExprKind ,  Item ,  ItemKind ,  OwnerNode } ; 
43use  rustc_lint:: { LateContext ,  LateLintPass ,  LintContext } ; 
54use  rustc_session:: declare_lint_pass; 
65use  rustc_span:: sym; 
76
87declare_clippy_lint !  { 
98    /// ### 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. 
1111/// 
1212/// ### Why restrict this? 
1313/// `exit()` immediately terminates the program with no information other than an exit code. 
1414/// This provides no means to troubleshoot a problem, and may be an unexpected side effect. 
1515/// 
1616/// Codebases may use this lint to require that all exits are performed either by panicking 
1717/// (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). 
1919/// 
20- /// ### Example  
20+ /// ### Good example  
2121/// ```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+ /// } 
2336/// ``` 
2437/// 
2538/// Use instead: 
@@ -36,7 +49,7 @@ declare_clippy_lint! {
3649[ clippy:: version = "1.41.0" ] 
3750    pub  EXIT , 
3851    restriction, 
39-     "detects `std::process::exit` calls" 
52+     "detects `std::process::exit` calls outside of `main` " 
4053} 
4154
4255declare_lint_pass ! ( Exit  => [ EXIT ] ) ; 
@@ -52,10 +65,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
5265            && let  Some ( def_id)  = cx. qpath_res ( path,  path_expr. hir_id ) . opt_def_id ( ) 
5366            && cx. tcx . is_diagnostic_item ( sym:: process_exit,  def_id) 
5467            && 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"  
5770            // 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" 
5976        { 
6077            span_lint ( cx,  EXIT ,  e. span ,  "usage of `process::exit`" ) ; 
6178        } 
0 commit comments