diff --git a/CHANGELOG.md b/CHANGELOG.md index 45321751ba7..d0164d3be37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2185,6 +2185,7 @@ Released 2018-09-13 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7f5c4f56f9e..f6014c246d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -310,6 +310,7 @@ mod regex; mod repeat_once; mod returns; mod self_assignment; +mod semicolon_if_nothing_returned; mod serde_api; mod shadow; mod single_component_path_imports; @@ -876,6 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, &self_assignment::SELF_ASSIGNMENT, + &semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1237,6 +1239,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1364,6 +1367,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&redundant_else::REDUNDANT_ELSE), LintId::of(&ref_option_ref::REF_OPTION_REF), + LintId::of(&semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs new file mode 100644 index 00000000000..99e5841d5a9 --- /dev/null +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -0,0 +1,58 @@ +use crate::utils::{match_def_path, paths, span_lint_and_then, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub SEMICOLON_IF_NOTHING_RETURNED, + pedantic, + "default lint description" +} + +declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); + +impl LateLintPass<'_> for SemicolonIfNothingReturned { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if_chain! { + if let Some(expr) = block.expr; + let t_expr = cx.typeck_results().expr_ty(expr); + if t_expr.is_unit(); + then { + let sugg = sugg::Sugg::hir(cx, &expr, ".."); + let suggestion = format!("{0};", sugg); + span_lint_and_then( + cx, + SEMICOLON_IF_NOTHING_RETURNED, + expr.span, + "add `;` to terminate block", + | diag | { + diag.span_suggestion( + expr.span, + "add `;`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + ) + } + } + } +} diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs new file mode 100644 index 00000000000..6790e91d813 --- /dev/null +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -0,0 +1,18 @@ +#![warn(clippy::semicolon_if_nothing_returned)] + +fn get_unit() {} + +// the functions below trigger the lint +fn main() { + println!("Hello") +} + +fn hello() { + get_unit() +} + +// this is fine +fn print_sum(a: i32, b: i32) { + println!("{}", a + b); + assert_eq!(true, false); +} diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr new file mode 100644 index 00000000000..ecb284ecf04 --- /dev/null +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -0,0 +1,17 @@ +error: add `;` to terminate block + --> $DIR/semicolon_if_nothing_returned.rs:7:5 + | +LL | println!("Hello") + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: add `;` to terminate block + --> $DIR/semicolon_if_nothing_returned.rs:11:5 + | +LL | get_unit() + | ^^^^^^^^^^ help: add `;`: `get_unit();` + +error: aborting due to 2 previous errors +