Skip to content

Commit d97fbdb

Browse files
committed
Auto merge of #4635 - Lythenas:suggestions-for-assert-false, r=flip1995
Add assert message to suggestion in lint assertions_on_constants <!-- Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only updates to the latest nightly, you can leave the `changelog` entry as `none`. Otherwise, please write a short comment explaining your change. If your PR fixes an issue, you can add "fixes #issue_number" into this PR description. This way the issue will be automatically closed when your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. - [x] Followed [lint naming conventions][lint_naming] - [x] Added passing UI tests (including committed `.stderr` file) - [ ] `cargo test` passes locally - [x] Executed `./util/dev update_lints` - [ ] Added lint documentation - [ ] Run `./util/dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints Note that you can skip the above if you are just opening a WIP PR in order to get feedback. Delete this line and everything above before opening your PR --> - [x] suggest replacing `assert!(false, "msg")` with `panic!("msg")` - [x] extend to allow ~~variables~~ any expression for `"msg"` - ~~suggest replacing `assert!(false, "msg {}", "arg")` with `panic!("msg {}", "arg")`~~ changelog: add assert message to suggestion in lint assertions_on_constants Work towards fixing: #3575
2 parents 1dc7f5c + 6ee8d75 commit d97fbdb

File tree

3 files changed

+152
-34
lines changed

3 files changed

+152
-34
lines changed
+126-28
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use rustc::hir::{Expr, ExprKind};
1+
use crate::consts::{constant, Constant};
2+
use crate::utils::paths;
3+
use crate::utils::{is_direct_expn_of, is_expn_of, match_def_path, snippet_opt, span_help_and_lint};
4+
use if_chain::if_chain;
5+
use rustc::hir::*;
26
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
37
use rustc::{declare_lint_pass, declare_tool_lint};
4-
5-
use crate::consts::{constant, Constant};
6-
use crate::utils::{is_direct_expn_of, is_expn_of, span_help_and_lint};
8+
use syntax::ast::LitKind;
79

810
declare_clippy_lint! {
911
/// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
@@ -31,39 +33,135 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
3133

3234
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
3335
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
34-
let lint_assert_cb = |is_debug_assert: bool| {
35-
if let ExprKind::Unary(_, ref lit) = e.kind {
36-
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit) {
37-
if is_true {
38-
span_help_and_lint(
39-
cx,
40-
ASSERTIONS_ON_CONSTANTS,
41-
e.span,
42-
"`assert!(true)` will be optimized out by the compiler",
43-
"remove it",
44-
);
45-
} else if !is_debug_assert {
46-
span_help_and_lint(
47-
cx,
48-
ASSERTIONS_ON_CONSTANTS,
49-
e.span,
50-
"`assert!(false)` should probably be replaced",
51-
"use `panic!()` or `unreachable!()`",
52-
);
53-
}
54-
}
55-
}
36+
let lint_true = || {
37+
span_help_and_lint(
38+
cx,
39+
ASSERTIONS_ON_CONSTANTS,
40+
e.span,
41+
"`assert!(true)` will be optimized out by the compiler",
42+
"remove it",
43+
);
5644
};
45+
let lint_false_without_message = || {
46+
span_help_and_lint(
47+
cx,
48+
ASSERTIONS_ON_CONSTANTS,
49+
e.span,
50+
"`assert!(false)` should probably be replaced",
51+
"use `panic!()` or `unreachable!()`",
52+
);
53+
};
54+
let lint_false_with_message = |panic_message: String| {
55+
span_help_and_lint(
56+
cx,
57+
ASSERTIONS_ON_CONSTANTS,
58+
e.span,
59+
&format!("`assert!(false, {})` should probably be replaced", panic_message),
60+
&format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
61+
)
62+
};
63+
5764
if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
5865
if debug_assert_span.from_expansion() {
5966
return;
6067
}
61-
lint_assert_cb(true);
68+
if_chain! {
69+
if let ExprKind::Unary(_, ref lit) = e.kind;
70+
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit);
71+
if is_true;
72+
then {
73+
lint_true();
74+
}
75+
};
6276
} else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
6377
if assert_span.from_expansion() {
6478
return;
6579
}
66-
lint_assert_cb(false);
80+
if let Some(assert_match) = match_assert_with_message(&cx, e) {
81+
match assert_match {
82+
// matched assert but not message
83+
AssertKind::WithoutMessage(false) => lint_false_without_message(),
84+
AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(),
85+
AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
86+
};
87+
}
6788
}
6889
}
6990
}
91+
92+
/// Result of calling `match_assert_with_message`.
93+
enum AssertKind {
94+
WithMessage(String, bool),
95+
WithoutMessage(bool),
96+
}
97+
98+
/// Check if the expression matches
99+
///
100+
/// ```rust,ignore
101+
/// match { let _t = !c; _t } {
102+
/// true => {
103+
/// {
104+
/// ::std::rt::begin_panic(message, _)
105+
/// }
106+
/// }
107+
/// _ => { }
108+
/// };
109+
/// ```
110+
///
111+
/// where `message` is any expression and `c` is a constant bool.
112+
fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> Option<AssertKind> {
113+
if_chain! {
114+
if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
115+
// matches { let _t = expr; _t }
116+
if let ExprKind::DropTemps(ref expr) = expr.kind;
117+
if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
118+
// bind the first argument of the `assert!` macro
119+
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr);
120+
// arm 1 pattern
121+
if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
122+
if let ExprKind::Lit(ref lit) = lit_expr.kind;
123+
if let LitKind::Bool(true) = lit.node;
124+
// arm 1 block
125+
if let ExprKind::Block(ref block, _) = arms[0].body.kind;
126+
if block.stmts.len() == 0;
127+
if let Some(block_expr) = &block.expr;
128+
if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
129+
if let Some(begin_panic_call) = &inner_block.expr;
130+
// function call
131+
if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
132+
if args.len() == 2;
133+
// bind the second argument of the `assert!` macro if it exists
134+
if let panic_message = snippet_opt(cx, args[0].span);
135+
// second argument of begin_panic is irrelevant
136+
// as is the second match arm
137+
then {
138+
// an empty message occurs when it was generated by the macro
139+
// (and not passed by the user)
140+
return panic_message
141+
.filter(|msg| !msg.is_empty())
142+
.map(|msg| AssertKind::WithMessage(msg, is_true))
143+
.or(Some(AssertKind::WithoutMessage(is_true)));
144+
}
145+
}
146+
None
147+
}
148+
149+
/// Matches a function call with the given path and returns the arguments.
150+
///
151+
/// Usage:
152+
///
153+
/// ```rust,ignore
154+
/// if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
155+
/// ```
156+
fn match_function_call<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, path: &[&str]) -> Option<&'a [Expr]> {
157+
if_chain! {
158+
if let ExprKind::Call(ref fun, ref args) = expr.kind;
159+
if let ExprKind::Path(ref qpath) = fun.kind;
160+
if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
161+
if match_def_path(cx, fun_def_id, path);
162+
then {
163+
return Some(&args)
164+
}
165+
};
166+
None
167+
}

tests/ui/assertions_on_constants.rs

+4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ fn main() {
1111
assert!(true, "true message");
1212
assert!(false, "false message");
1313

14+
let msg = "panic message";
15+
assert!(false, msg.to_uppercase());
16+
1417
const B: bool = true;
1518
assert!(B);
1619

1720
const C: bool = false;
1821
assert!(C);
22+
assert!(C, "C message");
1923

2024
debug_assert!(true);
2125
// Don't lint this, since there is no better way for expressing "Only panic in debug mode".

tests/ui/assertions_on_constants.stderr

+22-6
Original file line numberDiff line numberDiff line change
@@ -23,38 +23,54 @@ LL | assert!(true, "true message");
2323
|
2424
= help: remove it
2525

26-
error: `assert!(false)` should probably be replaced
26+
error: `assert!(false, "false message")` should probably be replaced
2727
--> $DIR/assertions_on_constants.rs:12:5
2828
|
2929
LL | assert!(false, "false message");
3030
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3131
|
32-
= help: use `panic!()` or `unreachable!()`
32+
= help: use `panic!("false message")` or `unreachable!("false message")`
3333

34-
error: `assert!(true)` will be optimized out by the compiler
34+
error: `assert!(false, msg.to_uppercase())` should probably be replaced
3535
--> $DIR/assertions_on_constants.rs:15:5
3636
|
37+
LL | assert!(false, msg.to_uppercase());
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
|
40+
= help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())`
41+
42+
error: `assert!(true)` will be optimized out by the compiler
43+
--> $DIR/assertions_on_constants.rs:18:5
44+
|
3745
LL | assert!(B);
3846
| ^^^^^^^^^^^
3947
|
4048
= help: remove it
4149

4250
error: `assert!(false)` should probably be replaced
43-
--> $DIR/assertions_on_constants.rs:18:5
51+
--> $DIR/assertions_on_constants.rs:21:5
4452
|
4553
LL | assert!(C);
4654
| ^^^^^^^^^^^
4755
|
4856
= help: use `panic!()` or `unreachable!()`
4957

58+
error: `assert!(false, "C message")` should probably be replaced
59+
--> $DIR/assertions_on_constants.rs:22:5
60+
|
61+
LL | assert!(C, "C message");
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^
63+
|
64+
= help: use `panic!("C message")` or `unreachable!("C message")`
65+
5066
error: `assert!(true)` will be optimized out by the compiler
51-
--> $DIR/assertions_on_constants.rs:20:5
67+
--> $DIR/assertions_on_constants.rs:24:5
5268
|
5369
LL | debug_assert!(true);
5470
| ^^^^^^^^^^^^^^^^^^^^
5571
|
5672
= help: remove it
5773
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
5874

59-
error: aborting due to 7 previous errors
75+
error: aborting due to 9 previous errors
6076

0 commit comments

Comments
 (0)