diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b32914854fe..84a3bd491cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -996,6 +996,7 @@ Released 2018-09-13 [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop diff --git a/README.md b/README.md index 87ef441eadd5..922dbcd11380 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 332 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs new file mode 100644 index 000000000000..7220833b9f23 --- /dev/null +++ b/clippy_lints/src/exit.rs @@ -0,0 +1,47 @@ +use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint}; +use if_chain::if_chain; +use rustc::hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use rustc::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** `exit()` terminates the program and doesn't provide a + /// stack trace. + /// + /// **Why is this bad?** Ideally a program is terminated by finishing + /// the main function. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// std::process::exit(0) + /// ``` + pub EXIT, + restriction, + "`std::process::exit` is called, terminating the program" +} + +declare_lint_pass!(Exit => [EXIT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) { + if_chain! { + if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Path(ref path) = path_expr.kind; + if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::EXIT); + then { + let mut parent = cx.tcx.hir().get_parent_item(e.hir_id); + if let Some(Node::Item(Item{ident, kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { + // If the next item up is a function we check if it is an entry point + // and only then emit a linter warning + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id) { + 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 817575092cc5..dcec8778b769 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -188,6 +188,7 @@ pub mod escape; pub mod eta_reduction; pub mod eval_order_dependence; pub mod excessive_precision; +pub mod exit; pub mod explicit_write; pub mod fallible_impl_from; pub mod format; @@ -497,6 +498,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &eval_order_dependence::DIVERGING_SUB_EXPRESSION, &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_precision::EXCESSIVE_PRECISION, + &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, &format::USELESS_FORMAT, @@ -938,12 +940,14 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); + store.register_late_pass(|| box exit::Exit); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(&exit::EXIT), LintId::of(&implicit_return::IMPLICIT_RETURN), LintId::of(&indexing_slicing::INDEXING_SLICING), LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 9787517e5055..042ca4ee7fdc 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -27,6 +27,7 @@ pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; +pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a053cf8bef0c..55f86ec401ad 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 332] = [ +pub const ALL_LINTS: [Lint; 333] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -490,6 +490,13 @@ pub const ALL_LINTS: [Lint; 332] = [ deprecation: None, module: "excessive_precision", }, + Lint { + name: "exit", + group: "restriction", + desc: "`std::process::exit` is called, terminating the program", + deprecation: None, + module: "exit", + }, Lint { name: "expect_fun_call", group: "perf", diff --git a/tests/ui/exit1.rs b/tests/ui/exit1.rs new file mode 100644 index 000000000000..4eac6eb74672 --- /dev/null +++ b/tests/ui/exit1.rs @@ -0,0 +1,15 @@ +#[warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/tests/ui/exit1.stderr b/tests/ui/exit1.stderr new file mode 100644 index 000000000000..a8d3956aa27a --- /dev/null +++ b/tests/ui/exit1.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit1.rs:5:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/exit2.rs b/tests/ui/exit2.rs new file mode 100644 index 000000000000..4b693ed7083f --- /dev/null +++ b/tests/ui/exit2.rs @@ -0,0 +1,13 @@ +#[warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/tests/ui/exit2.stderr b/tests/ui/exit2.stderr new file mode 100644 index 000000000000..7263e156a9d2 --- /dev/null +++ b/tests/ui/exit2.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit2.rs:4:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/exit3.rs b/tests/ui/exit3.rs new file mode 100644 index 000000000000..9dc0e1015a4f --- /dev/null +++ b/tests/ui/exit3.rs @@ -0,0 +1,8 @@ +#[warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +}