Skip to content

Commit 4f9d633

Browse files
Merge branch 'master' into issue-4078
2 parents 17be043 + db233b0 commit 4f9d633

22 files changed

+550
-45
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,7 @@ Released 2018-09-13
10631063
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
10641064
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
10651065
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
1066+
[`manual_mul_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_mul_add
10661067
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
10671068
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
10681069
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
@@ -1191,6 +1192,7 @@ Released 2018-09-13
11911192
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
11921193
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
11931194
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
1195+
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
11941196
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
11951197
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
11961198
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ authors = [
1111
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
1212
repository = "https://github.com/rust-lang/rust-clippy"
1313
readme = "README.md"
14-
license = "MIT/Apache-2.0"
14+
license = "MIT OR Apache-2.0"
1515
keywords = ["clippy", "lint", "plugin"]
1616
categories = ["development-tools", "development-tools::cargo-plugins"]
1717
build = "build.rs"

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
[![Build Status](https://travis-ci.com/rust-lang/rust-clippy.svg?branch=master)](https://travis-ci.com/rust-lang/rust-clippy)
44
[![Windows Build status](https://ci.appveyor.com/api/projects/status/id677xpw1dguo7iw?svg=true)](https://ci.appveyor.com/project/rust-lang-libs/rust-clippy)
5-
[![License: MIT/Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
5+
[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 320 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 322 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_dummy/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ build = 'build.rs'
99

1010
repository = "https://github.com/rust-lang/rust-clippy"
1111

12-
license = "MIT/Apache-2.0"
12+
license = "MIT OR Apache-2.0"
1313
keywords = ["clippy", "lint", "plugin"]
1414
categories = ["development-tools", "development-tools::cargo-plugins"]
1515

clippy_lints/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ authors = [
1212
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
1313
repository = "https://github.com/rust-lang/rust-clippy"
1414
readme = "README.md"
15-
license = "MPL-2.0"
15+
license = "MIT OR Apache-2.0"
1616
keywords = ["clippy", "lint", "plugin"]
1717
edition = "2018"
1818

+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+
}

clippy_lints/src/cargo_common_metadata.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ declare_clippy_lint! {
2727
/// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
2828
/// repository = "https://github.com/rust-lang/rust-clippy"
2929
/// readme = "README.md"
30-
/// license = "MIT/Apache-2.0"
30+
/// license = "MIT OR Apache-2.0"
3131
/// keywords = ["clippy", "lint", "plugin"]
3232
/// categories = ["development-tools", "development-tools::cargo-plugins"]
3333
/// ```

clippy_lints/src/formatting.rs

+64-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::utils::{differing_macro_contexts, snippet_opt, span_note_and_lint};
1+
use crate::utils::{differing_macro_contexts, snippet_opt, span_help_and_lint, span_note_and_lint};
22
use if_chain::if_chain;
33
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
44
use rustc::{declare_lint_pass, declare_tool_lint};
@@ -22,6 +22,28 @@ declare_clippy_lint! {
2222
"suspicious formatting of `*=`, `-=` or `!=`"
2323
}
2424

25+
declare_clippy_lint! {
26+
/// **What it does:** Checks the formatting of a unary operator on the right hand side
27+
/// of a binary operator. It lints if there is no space between the binary and unary operators,
28+
/// but there is a space between the unary and its operand.
29+
///
30+
/// **Why is this bad?** This is either a typo in the binary operator or confusing.
31+
///
32+
/// **Known problems:** None.
33+
///
34+
/// **Example:**
35+
/// ```rust,ignore
36+
/// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
37+
/// }
38+
///
39+
/// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
40+
/// }
41+
/// ```
42+
pub SUSPICIOUS_UNARY_OP_FORMATTING,
43+
style,
44+
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
45+
}
46+
2547
declare_clippy_lint! {
2648
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
2749
/// is followed immediately by a newline or the `else` seems to be missing.
@@ -80,6 +102,7 @@ declare_clippy_lint! {
80102

81103
declare_lint_pass!(Formatting => [
82104
SUSPICIOUS_ASSIGNMENT_FORMATTING,
105+
SUSPICIOUS_UNARY_OP_FORMATTING,
83106
SUSPICIOUS_ELSE_FORMATTING,
84107
POSSIBLE_MISSING_COMMA
85108
]);
@@ -99,6 +122,7 @@ impl EarlyLintPass for Formatting {
99122

100123
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
101124
check_assign(cx, expr);
125+
check_unop(cx, expr);
102126
check_else(cx, expr);
103127
check_array(cx, expr);
104128
}
@@ -133,6 +157,45 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
133157
}
134158
}
135159

160+
/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
161+
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
162+
if_chain! {
163+
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
164+
if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
165+
// span between BinOp LHS and RHS
166+
let binop_span = lhs.span.between(rhs.span);
167+
// if RHS is a UnOp
168+
if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
169+
// from UnOp operator to UnOp operand
170+
let unop_operand_span = rhs.span.until(un_rhs.span);
171+
if let Some(binop_snippet) = snippet_opt(cx, binop_span);
172+
if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
173+
let binop_str = BinOpKind::to_string(&binop.node);
174+
// no space after BinOp operator and space after UnOp operator
175+
if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
176+
then {
177+
let unop_str = UnOp::to_string(op);
178+
let eqop_span = lhs.span.between(un_rhs.span);
179+
span_help_and_lint(
180+
cx,
181+
SUSPICIOUS_UNARY_OP_FORMATTING,
182+
eqop_span,
183+
&format!(
184+
"by not having a space between `{binop}` and `{unop}` it looks like \
185+
`{binop}{unop}` is a single operator",
186+
binop = binop_str,
187+
unop = unop_str
188+
),
189+
&format!(
190+
"put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
191+
binop = binop_str,
192+
unop = unop_str
193+
),
194+
);
195+
}
196+
}
197+
}
198+
136199
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
137200
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
138201
if_chain! {

clippy_lints/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub mod misc_early;
224224
pub mod missing_const_for_fn;
225225
pub mod missing_doc;
226226
pub mod missing_inline;
227+
pub mod mul_add;
227228
pub mod multiple_crate_versions;
228229
pub mod mut_mut;
229230
pub mod mut_reference;
@@ -604,6 +605,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
604605
reg.register_late_lint_pass(box inherent_to_string::InherentToString);
605606
reg.register_late_lint_pass(box trait_bounds::TraitBounds);
606607
reg.register_late_lint_pass(box comparison_chain::ComparisonChain);
608+
reg.register_late_lint_pass(box mul_add::MulAddCheck);
607609

608610
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
609611
arithmetic::FLOAT_ARITHMETIC,
@@ -741,6 +743,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
741743
formatting::POSSIBLE_MISSING_COMMA,
742744
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
743745
formatting::SUSPICIOUS_ELSE_FORMATTING,
746+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
744747
functions::NOT_UNSAFE_PTR_ARG_DEREF,
745748
functions::TOO_MANY_ARGUMENTS,
746749
get_last_with_len::GET_LAST_WITH_LEN,
@@ -837,6 +840,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
837840
misc_early::UNNEEDED_FIELD_PATTERN,
838841
misc_early::UNNEEDED_WILDCARD_PATTERN,
839842
misc_early::ZERO_PREFIXED_LITERAL,
843+
mul_add::MANUAL_MUL_ADD,
840844
mut_reference::UNNECESSARY_MUT_PASSED,
841845
mutex_atomic::MUTEX_ATOMIC,
842846
needless_bool::BOOL_COMPARISON,
@@ -951,6 +955,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
951955
excessive_precision::EXCESSIVE_PRECISION,
952956
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
953957
formatting::SUSPICIOUS_ELSE_FORMATTING,
958+
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
954959
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
955960
inherent_to_string::INHERENT_TO_STRING,
956961
len_zero::LEN_WITHOUT_IS_EMPTY,
@@ -1175,6 +1180,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con
11751180
methods::OR_FUN_CALL,
11761181
methods::SINGLE_CHAR_PATTERN,
11771182
misc::CMP_OWNED,
1183+
mul_add::MANUAL_MUL_ADD,
11781184
mutex_atomic::MUTEX_ATOMIC,
11791185
redundant_clone::REDUNDANT_CLONE,
11801186
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,

0 commit comments

Comments
 (0)