diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 76cb22864779..c34e0d33e713 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -50,32 +51,32 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { // Should this have a config value? && !is_else_clause(cx.tcx, expr) { - let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); - let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); - // we show to the user the suggestion without the comments, but when applying the fix, include the - // comments in the block span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", |diag| { - // comments can be noisy, do not show them to the user + let mut applicability = Applicability::MachineApplicable; + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability, - ); + comments += "\n"; } - diag.span_suggestion(expr.span, "try instead", sugg, applicability); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); + let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; + + let indent = indent_of(cx, expr.span); + let full_sugg = reindent_multiline( + format!("{comments}assert!({cond_sugg}, {format_args_snip}){semicolon}").as_str(), + true, + indent, + ); + diag.span_suggestion_verbose( + expr.span, + "replace `if`-then-`panic!` with `assert!`", + full_sugg, + applicability, + ); }, ); } diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed new file mode 100644 index 000000000000..d1f798bd5c54 --- /dev/null +++ b/tests/ui/manual_assert.edition2018.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 2e9c9045caae..c81a85527527 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed new file mode 100644 index 000000000000..d1f798bd5c54 --- /dev/null +++ b/tests/ui/manual_assert.edition2021.fixed @@ -0,0 +1,98 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 + +#![warn(clippy::manual_assert)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)] + +macro_rules! one { + () => { + 1 + }; +} + +fn main() { + let a = vec![1, 2, 3]; + let c = Some(2); + if !a.is_empty() + && a.len() == 3 + && c.is_some() + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + && !a.is_empty() + && a.len() == 3 + { + panic!("qaqaq{:?}", a); + } + //~^ manual_assert + assert!(a.is_empty(), "qaqaq{:?}", a); + //~^ manual_assert + assert!(a.is_empty(), "qwqwq"); + if a.len() == 3 { + println!("qwq"); + println!("qwq"); + println!("qwq"); + } + if let Some(b) = c { + panic!("orz {}", b); + } + if a.len() == 3 { + panic!("qaqaq"); + } else { + println!("qwq"); + } + let b = vec![1, 2, 3]; + //~^ manual_assert + assert!(!b.is_empty(), "panic1"); + //~^ manual_assert + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + //~^ manual_assert + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + //~^ manual_assert + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + //~^ manual_assert + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + //~^ manual_assert + assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } +} + +fn issue7730(a: u8) { + // Suggestion should preserve comment + //~^ manual_assert + // comment + /* this is a + multiline + comment */ + /// Doc comment + // comment after `panic!` + assert!(a <= 2, "panic with comment"); +} + +fn issue12505() { + struct Foo(T); + + impl Foo { + const BAR: () = //~^ manual_assert + assert!(N != 0, ); + } +} + +fn issue15227(left: u64, right: u64) -> u64 { + macro_rules! is_x86_feature_detected { + ($feature:literal) => { + $feature.len() > 0 && $feature.starts_with("ss") + }; + } + + //~^ manual_assert + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); + unsafe { todo!() } +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 2e9c9045caae..c81a85527527 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,5 +1,5 @@ error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:32:5 + --> tests/ui/manual_assert.rs:30:5 | LL | / if !a.is_empty() { LL | | @@ -9,17 +9,14 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_assert)]` -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qaqaq{:?}", a); -LL - } +LL ~ LL + assert!(a.is_empty(), "qaqaq{:?}", a); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:36:5 + --> tests/ui/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | @@ -27,17 +24,14 @@ LL | | panic!("qwqwq"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !a.is_empty() { -LL - -LL - panic!("qwqwq"); -LL - } +LL ~ LL + assert!(a.is_empty(), "qwqwq"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:54:5 + --> tests/ui/manual_assert.rs:52:5 | LL | / if b.is_empty() { LL | | @@ -45,17 +39,14 @@ LL | | panic!("panic1"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() { -LL - -LL - panic!("panic1"); -LL - } +LL ~ LL + assert!(!b.is_empty(), "panic1"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:58:5 + --> tests/ui/manual_assert.rs:56:5 | LL | / if b.is_empty() && a.is_empty() { LL | | @@ -63,17 +54,14 @@ LL | | panic!("panic2"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() && a.is_empty() { -LL - -LL - panic!("panic2"); -LL - } +LL ~ LL + assert!(!(b.is_empty() && a.is_empty()), "panic2"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:62:5 + --> tests/ui/manual_assert.rs:60:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | @@ -81,17 +69,14 @@ LL | | panic!("panic3"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() && !b.is_empty() { -LL - -LL - panic!("panic3"); -LL - } +LL ~ LL + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:66:5 + --> tests/ui/manual_assert.rs:64:5 | LL | / if b.is_empty() || a.is_empty() { LL | | @@ -99,17 +84,14 @@ LL | | panic!("panic4"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if b.is_empty() || a.is_empty() { -LL - -LL - panic!("panic4"); -LL - } +LL ~ LL + assert!(!(b.is_empty() || a.is_empty()), "panic4"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:70:5 + --> tests/ui/manual_assert.rs:68:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | @@ -117,17 +99,14 @@ LL | | panic!("panic5"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() || !b.is_empty() { -LL - -LL - panic!("panic5"); -LL - } +LL ~ LL + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:74:5 + --> tests/ui/manual_assert.rs:72:5 | LL | / if a.is_empty() { LL | | @@ -135,17 +114,14 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if a.is_empty() { -LL - -LL - panic!("with expansion {}", one!()) -LL - } +LL ~ LL + assert!(!a.is_empty(), "with expansion {}", one!()); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:87:5 + --> tests/ui/manual_assert.rs:85:5 | LL | / if a > 2 { LL | | @@ -156,22 +132,20 @@ LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | -help: try instead - | -LL - if a > 2 { -LL - -LL - // comment -LL - /* this is a -LL - multiline -LL - comment */ -LL - /// Doc comment -LL - panic!("panic with comment") // comment after `panic!` -LL - } +help: replace `if`-then-`panic!` with `assert!` + | +LL ~ +LL + // comment +LL + /* this is a +LL + multiline +LL + comment */ +LL + /// Doc comment +LL + // comment after `panic!` LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:102:25 + --> tests/ui/manual_assert.rs:100:25 | LL | const BAR: () = if N == 0 { | _________________________^ @@ -180,17 +154,14 @@ LL | | panic!() LL | | }; | |_________^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - const BAR: () = if N == 0 { -LL - -LL - panic!() -LL - }; -LL + const BAR: () = assert!(N != 0, ); +LL ~ const BAR: () = +LL ~ assert!(N != 0, ); | error: only a `panic!` in `if`-then statement - --> tests/ui/manual_assert.rs:116:5 + --> tests/ui/manual_assert.rs:114:5 | LL | / if !is_x86_feature_detected!("ssse3") { LL | | @@ -198,12 +169,9 @@ LL | | panic!("SSSE3 is not supported"); LL | | } | |_____^ | -help: try instead +help: replace `if`-then-`panic!` with `assert!` | -LL - if !is_x86_feature_detected!("ssse3") { -LL - -LL - panic!("SSSE3 is not supported"); -LL - } +LL ~ LL + assert!(is_x86_feature_detected!("ssse3"), "SSSE3 is not supported"); | diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index ab02bd5f5e53..761179554223 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -2,8 +2,6 @@ //@[edition2018] edition:2018 //@[edition2021] edition:2021 -//@no-rustfix: need to change the suggestion to a multipart suggestion - #![warn(clippy::manual_assert)] #![allow(dead_code, unused_doc_comments)] #![allow(clippy::nonminimal_bool, clippy::uninlined_format_args, clippy::useless_vec)]