diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2258ae765928..c7eba5b49189 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -164,7 +164,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::excessive_nesting::EXCESSIVE_NESTING_INFO, crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, - crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, @@ -382,6 +381,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, + crate::methods::EXIT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, crate::methods::EXPECT_USED_INFO, crate::methods::EXTEND_WITH_DRAIN_INFO, diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs deleted file mode 100644 index 245d3bd8ec25..000000000000 --- a/clippy_lints/src/exit.rs +++ /dev/null @@ -1,76 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::sym; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; -use rustc_lint::{LateContext, LateLintPass, declare_lint_pass}; - -declare_clippy_lint! { - /// ### What it does - /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` - /// immediately terminate the program. - /// - /// ### Why restrict this? - /// `exit()` immediately terminates the program with no information other than an exit code. - /// This provides no means to troubleshoot a problem, and may be an unexpected side effect. - /// - /// Codebases may use this lint to require that all exits are performed either by panicking - /// (which produces a message, a code location, and optionally a backtrace) - /// or by calling `exit()` from `main()` (which is a single place to look). - /// - /// ### Good example - /// ```no_run - /// fn main() { - /// std::process::exit(0); - /// } - /// ``` - /// - /// ### Bad example - /// ```no_run - /// fn main() { - /// other_function(); - /// } - /// - /// fn other_function() { - /// std::process::exit(0); - /// } - /// ``` - /// - /// Use instead: - /// - /// ```ignore - /// // To provide a stacktrace and additional information - /// panic!("message"); - /// - /// // or a main method with a return - /// fn main() -> Result<(), i32> { - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.41.0"] - pub EXIT, - restriction, - "detects `std::process::exit` calls outside of `main`" -} - -declare_lint_pass!(Exit => [EXIT]); - -impl<'tcx> LateLintPass<'tcx> for Exit { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, [_]) = e.kind - && let ExprKind::Path(ref path) = path_expr.kind - && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() - && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) - && let parent = cx.tcx.hir_get_parent_item(e.hir_id) - && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) - // If the next item up is a function we check if it isn't named "main" - // and only then emit a linter warning - - // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, - // in compilation contexts like --all-targets (which include --tests), you get false positives - // because in a test context, main is not the entrypoint function - && ident.name != sym::main - && !e.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e1a1055b609d..e03eeb711126 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -132,7 +132,6 @@ mod eta_reduction; mod excessive_bools; mod excessive_nesting; mod exhaustive_items; -mod exit; mod explicit_write; mod extra_unused_type_parameters; mod fallible_impl_from; @@ -688,7 +687,6 @@ rustc_lint::late_lint_methods!( Default: default::Default = ::default(), UnusedSelf: unused_self::UnusedSelf = unused_self::UnusedSelf::new(conf), DebugAssertWithMutCall: mutable_debug_assertion::DebugAssertWithMutCall = mutable_debug_assertion::DebugAssertWithMutCall, - Exit: exit::Exit = exit::Exit, ToDigitIsSome: to_digit_is_some::ToDigitIsSome = to_digit_is_some::ToDigitIsSome::new(conf), LargeStackArrays: large_stack_arrays::LargeStackArrays = large_stack_arrays::LargeStackArrays::new(conf), LargeConstArrays: large_const_arrays::LargeConstArrays = large_const_arrays::LargeConstArrays::new(conf), diff --git a/clippy_lints/src/methods/exit.rs b/clippy_lints/src/methods/exit.rs new file mode 100644 index 000000000000..2c2f58abac2f --- /dev/null +++ b/clippy_lints/src/methods/exit.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::res::{MaybeDef as _, MaybeQPath as _}; +use clippy_utils::sym; +use rustc_hir::{Expr, Item, ItemKind, OwnerNode}; +use rustc_lint::LateContext; + +use super::EXIT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, func: &'tcx Expr<'_>) { + if func.res(cx).is_diag_item(cx, sym::process_exit) + && let parent = cx.tcx.hir_get_parent_item(expr.hir_id) + && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) + // If the next item up is a function we check if it isn't named "main" + // and only then emit a linter warning + + // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, + // in compilation contexts like --all-targets (which include --tests), you get false positives + // because in a test context, main is not the entrypoint function + && ident.name != sym::main + && !expr.span.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint(cx, EXIT, expr.span, "usage of `process::exit`"); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3153c7a73049..cd7ac7d60b94 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -19,6 +19,7 @@ mod collapsible_str_replace; mod double_ended_iterator_last; mod drain_collect; mod err_expect; +mod exit; mod expect_fun_call; mod extend_with_drain; mod filetype_is_file; @@ -625,6 +626,54 @@ declare_clippy_lint! { r#"using `.err().expect("")` when `.expect_err("")` can be used"# } +declare_clippy_lint! { + /// ### What it does + /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` + /// immediately terminate the program. + /// + /// ### Why restrict this? + /// `exit()` immediately terminates the program with no information other than an exit code. + /// This provides no means to troubleshoot a problem, and may be an unexpected side effect. + /// + /// Codebases may use this lint to require that all exits are performed either by panicking + /// (which produces a message, a code location, and optionally a backtrace) + /// or by calling `exit()` from `main()` (which is a single place to look). + /// + /// ### Good example + /// ```no_run + /// fn main() { + /// std::process::exit(0); + /// } + /// ``` + /// + /// ### Bad example + /// ```no_run + /// fn main() { + /// other_function(); + /// } + /// + /// fn other_function() { + /// std::process::exit(0); + /// } + /// ``` + /// + /// Use instead: + /// + /// ```ignore + /// // To provide a stacktrace and additional information + /// panic!("message"); + /// + /// // or a main method with a return + /// fn main() -> Result<(), i32> { + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.41.0"] + pub EXIT, + restriction, + "detects `std::process::exit` calls outside of `main`" +} + declare_clippy_lint! { /// ### What it does /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, @@ -4970,6 +5019,7 @@ impl_lint_pass!(Methods => [ DOUBLE_ENDED_ITERATOR_LAST, DRAIN_COLLECT, ERR_EXPECT, + EXIT, EXPECT_FUN_CALL, EXPECT_USED, EXTEND_WITH_DRAIN, @@ -5180,6 +5230,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Call(func, _) = expr.kind { + // The functions from this block perform their own macro context checks + exit::check(cx, expr, func); + } + if expr.span.from_expansion() { return; }