Skip to content

Commit c576bed

Browse files
committed
Auto merge of #5883 - flip1995:rollup-x9mftxe, r=flip1995
Rollup of 5 pull requests Successful merges: - #5825 (Add the new lint `same_item_push`) - #5869 (New lint against `Self` as an arbitrary self type) - #5870 (enable #[allow(clippy::unsafe_derive_deserialize)]) - #5871 (Lint .min(x).max(y) with x < y) - #5874 (Make the docs clearer for new contributors) Failed merges: r? @ghost changelog: rollup
2 parents 7228368 + 7f6897c commit c576bed

28 files changed

+795
-89
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,7 @@ Released 2018-09-13
16161616
[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
16171617
[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
16181618
[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
1619+
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
16191620
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
16201621
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
16211622
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
@@ -1687,6 +1688,7 @@ Released 2018-09-13
16871688
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
16881689
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
16891690
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
1691+
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
16901692
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
16911693
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
16921694
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse

CONTRIBUTING.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct].
2828

2929
## Getting started
3030

31-
High level approach:
31+
**Note: If this is your first time contributing to Clippy, you should
32+
first read the [Basics docs](doc/basics.md).**
33+
34+
### High level approach
3235

3336
1. Find something to fix/improve
3437
2. Change code (likely some file in `clippy_lints/src/`)
35-
3. Follow the instructions in the [Basics docs](doc/basics.md) such as running the `setup-toolchain.sh` script
38+
3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
3639
4. Run `cargo test` in the root directory and wiggle code until it passes
3740
5. Open a PR (also can be done after 2. if you run into problems)
3841

clippy_lints/src/derive.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::utils::paths;
22
use crate::utils::{
3-
get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note,
4-
span_lint_and_then,
3+
get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
4+
span_lint_and_note, span_lint_and_then,
55
};
66
use if_chain::if_chain;
77
use rustc_hir::def_id::DefId;
@@ -354,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>(
354354
if_chain! {
355355
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
356356
if let ty::Adt(def, _) = ty.kind;
357-
if def.did.is_local();
357+
if let Some(local_def_id) = def.did.as_local();
358+
let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
359+
if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
358360
if cx.tcx.inherent_impls(def.did)
359361
.iter()
360362
.map(|imp_did| item_from_def_id(cx, *imp_did))

clippy_lints/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ mod mut_mut;
250250
mod mut_reference;
251251
mod mutable_debug_assertion;
252252
mod mutex_atomic;
253+
mod needless_arbitrary_self_type;
253254
mod needless_bool;
254255
mod needless_borrow;
255256
mod needless_borrowed_ref;
@@ -608,6 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
608609
&loops::NEEDLESS_COLLECT,
609610
&loops::NEEDLESS_RANGE_LOOP,
610611
&loops::NEVER_LOOP,
612+
&loops::SAME_ITEM_PUSH,
611613
&loops::WHILE_IMMUTABLE_CONDITION,
612614
&loops::WHILE_LET_LOOP,
613615
&loops::WHILE_LET_ON_ITERATOR,
@@ -717,6 +719,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
717719
&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
718720
&mutex_atomic::MUTEX_ATOMIC,
719721
&mutex_atomic::MUTEX_INTEGER,
722+
&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
720723
&needless_bool::BOOL_COMPARISON,
721724
&needless_bool::NEEDLESS_BOOL,
722725
&needless_borrow::NEEDLESS_BORROW,
@@ -1027,6 +1030,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10271030
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
10281031
store.register_early_pass(|| box precedence::Precedence);
10291032
store.register_early_pass(|| box needless_continue::NeedlessContinue);
1033+
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
10301034
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
10311035
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
10321036
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
@@ -1295,6 +1299,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12951299
LintId::of(&loops::NEEDLESS_COLLECT),
12961300
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
12971301
LintId::of(&loops::NEVER_LOOP),
1302+
LintId::of(&loops::SAME_ITEM_PUSH),
12981303
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
12991304
LintId::of(&loops::WHILE_LET_LOOP),
13001305
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@@ -1371,6 +1376,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13711376
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
13721377
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
13731378
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
1379+
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
13741380
LintId::of(&needless_bool::BOOL_COMPARISON),
13751381
LintId::of(&needless_bool::NEEDLESS_BOOL),
13761382
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
@@ -1497,6 +1503,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14971503
LintId::of(&loops::EMPTY_LOOP),
14981504
LintId::of(&loops::FOR_KV_MAP),
14991505
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
1506+
LintId::of(&loops::SAME_ITEM_PUSH),
15001507
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
15011508
LintId::of(&main_recursion::MAIN_RECURSION),
15021509
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
@@ -1602,6 +1609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16021609
LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
16031610
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
16041611
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
1612+
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
16051613
LintId::of(&needless_bool::BOOL_COMPARISON),
16061614
LintId::of(&needless_bool::NEEDLESS_BOOL),
16071615
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),

clippy_lints/src/loops.rs

+149-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use crate::utils::usage::{is_unused, mutated_variables};
55
use crate::utils::{
66
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
77
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
8-
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint,
9-
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
8+
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
9+
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
10+
SpanlessEq,
1011
};
1112
use if_chain::if_chain;
1213
use rustc_ast::ast;
@@ -419,6 +420,39 @@ declare_clippy_lint! {
419420
"variables used within while expression are not mutated in the body"
420421
}
421422

423+
declare_clippy_lint! {
424+
/// **What it does:** Checks whether a for loop is being used to push a constant
425+
/// value into a Vec.
426+
///
427+
/// **Why is this bad?** This kind of operation can be expressed more succinctly with
428+
/// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
429+
/// have better performance.
430+
/// **Known problems:** None
431+
///
432+
/// **Example:**
433+
/// ```rust
434+
/// let item1 = 2;
435+
/// let item2 = 3;
436+
/// let mut vec: Vec<u8> = Vec::new();
437+
/// for _ in 0..20 {
438+
/// vec.push(item1);
439+
/// }
440+
/// for _ in 0..30 {
441+
/// vec.push(item2);
442+
/// }
443+
/// ```
444+
/// could be written as
445+
/// ```rust
446+
/// let item1 = 2;
447+
/// let item2 = 3;
448+
/// let mut vec: Vec<u8> = vec![item1; 20];
449+
/// vec.resize(20 + 30, item2);
450+
/// ```
451+
pub SAME_ITEM_PUSH,
452+
style,
453+
"the same item is pushed inside of a for loop"
454+
}
455+
422456
declare_lint_pass!(Loops => [
423457
MANUAL_MEMCPY,
424458
NEEDLESS_RANGE_LOOP,
@@ -435,6 +469,7 @@ declare_lint_pass!(Loops => [
435469
NEVER_LOOP,
436470
MUT_RANGE_BOUND,
437471
WHILE_IMMUTABLE_CONDITION,
472+
SAME_ITEM_PUSH,
438473
]);
439474

440475
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -740,6 +775,7 @@ fn check_for_loop<'tcx>(
740775
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
741776
check_for_mut_range_bound(cx, arg, body);
742777
detect_manual_memcpy(cx, pat, arg, body, expr);
778+
detect_same_item_push(cx, pat, arg, body, expr);
743779
}
744780

745781
fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
@@ -1016,6 +1052,117 @@ fn detect_manual_memcpy<'tcx>(
10161052
}
10171053
}
10181054

1055+
// Scans the body of the for loop and determines whether lint should be given
1056+
struct SameItemPushVisitor<'a, 'tcx> {
1057+
should_lint: bool,
1058+
// this field holds the last vec push operation visited, which should be the only push seen
1059+
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
1060+
cx: &'a LateContext<'tcx>,
1061+
}
1062+
1063+
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
1064+
type Map = Map<'tcx>;
1065+
1066+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
1067+
match &expr.kind {
1068+
// Non-determinism may occur ... don't give a lint
1069+
ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false,
1070+
ExprKind::Block(block, _) => self.visit_block(block),
1071+
_ => {},
1072+
}
1073+
}
1074+
1075+
fn visit_block(&mut self, b: &'tcx Block<'_>) {
1076+
for stmt in b.stmts.iter() {
1077+
self.visit_stmt(stmt);
1078+
}
1079+
}
1080+
1081+
fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
1082+
let vec_push_option = get_vec_push(self.cx, s);
1083+
if vec_push_option.is_none() {
1084+
// Current statement is not a push so visit inside
1085+
match &s.kind {
1086+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr),
1087+
_ => {},
1088+
}
1089+
} else {
1090+
// Current statement is a push ...check whether another
1091+
// push had been previously done
1092+
if self.vec_push.is_none() {
1093+
self.vec_push = vec_push_option;
1094+
} else {
1095+
// There are multiple pushes ... don't lint
1096+
self.should_lint = false;
1097+
}
1098+
}
1099+
}
1100+
1101+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1102+
NestedVisitorMap::None
1103+
}
1104+
}
1105+
1106+
// Given some statement, determine if that statement is a push on a Vec. If it is, return
1107+
// the Vec being pushed into and the item being pushed
1108+
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
1109+
if_chain! {
1110+
// Extract method being called
1111+
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
1112+
if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
1113+
// Figure out the parameters for the method call
1114+
if let Some(self_expr) = args.get(0);
1115+
if let Some(pushed_item) = args.get(1);
1116+
// Check that the method being called is push() on a Vec
1117+
if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
1118+
if path.ident.name.as_str() == "push";
1119+
then {
1120+
return Some((self_expr, pushed_item))
1121+
}
1122+
}
1123+
None
1124+
}
1125+
1126+
/// Detects for loop pushing the same item into a Vec
1127+
fn detect_same_item_push<'tcx>(
1128+
cx: &LateContext<'tcx>,
1129+
pat: &'tcx Pat<'_>,
1130+
_: &'tcx Expr<'_>,
1131+
body: &'tcx Expr<'_>,
1132+
_: &'tcx Expr<'_>,
1133+
) {
1134+
// Determine whether it is safe to lint the body
1135+
let mut same_item_push_visitor = SameItemPushVisitor {
1136+
should_lint: true,
1137+
vec_push: None,
1138+
cx,
1139+
};
1140+
walk_expr(&mut same_item_push_visitor, body);
1141+
if same_item_push_visitor.should_lint {
1142+
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
1143+
// Make sure that the push does not involve possibly mutating values
1144+
if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
1145+
if let PatKind::Wild = pat.kind {
1146+
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
1147+
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
1148+
1149+
span_lint_and_help(
1150+
cx,
1151+
SAME_ITEM_PUSH,
1152+
vec.span,
1153+
"it looks like the same item is being pushed into this Vec",
1154+
None,
1155+
&format!(
1156+
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
1157+
item_str, vec_str, item_str
1158+
),
1159+
)
1160+
}
1161+
}
1162+
}
1163+
}
1164+
}
1165+
10191166
/// Checks for looping over a range and then indexing a sequence with it.
10201167
/// The iteratee must be a range literal.
10211168
#[allow(clippy::too_many_lines)]

clippy_lints/src/minmax.rs

+39-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::consts::{constant_simple, Constant};
2-
use crate::utils::{match_def_path, paths, span_lint};
2+
use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
3+
use if_chain::if_chain;
34
use rustc_hir::{Expr, ExprKind};
45
use rustc_lint::{LateContext, LateLintPass};
56
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -18,6 +19,10 @@ declare_clippy_lint! {
1819
/// ```ignore
1920
/// min(0, max(100, x))
2021
/// ```
22+
/// or
23+
/// ```ignore
24+
/// x.max(100).min(0)
25+
/// ```
2126
/// It will always be equal to `0`. Probably the author meant to clamp the value
2227
/// between 0 and 100, but has erroneously swapped `min` and `max`.
2328
pub MIN_MAX,
@@ -60,25 +65,43 @@ enum MinMax {
6065
}
6166

6267
fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
63-
if let ExprKind::Call(ref path, ref args) = expr.kind {
64-
if let ExprKind::Path(ref qpath) = path.kind {
65-
cx.typeck_results()
66-
.qpath_res(qpath, path.hir_id)
67-
.opt_def_id()
68-
.and_then(|def_id| {
69-
if match_def_path(cx, def_id, &paths::CMP_MIN) {
70-
fetch_const(cx, args, MinMax::Min)
71-
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
68+
match expr.kind {
69+
ExprKind::Call(ref path, ref args) => {
70+
if let ExprKind::Path(ref qpath) = path.kind {
71+
cx.typeck_results()
72+
.qpath_res(qpath, path.hir_id)
73+
.opt_def_id()
74+
.and_then(|def_id| {
75+
if match_def_path(cx, def_id, &paths::CMP_MIN) {
76+
fetch_const(cx, args, MinMax::Min)
77+
} else if match_def_path(cx, def_id, &paths::CMP_MAX) {
78+
fetch_const(cx, args, MinMax::Max)
79+
} else {
80+
None
81+
}
82+
})
83+
} else {
84+
None
85+
}
86+
},
87+
ExprKind::MethodCall(ref path, _, ref args, _) => {
88+
if_chain! {
89+
if let [obj, _] = args;
90+
if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
91+
then {
92+
if path.ident.as_str() == sym!(max).as_str() {
7293
fetch_const(cx, args, MinMax::Max)
94+
} else if path.ident.as_str() == sym!(min).as_str() {
95+
fetch_const(cx, args, MinMax::Min)
7396
} else {
7497
None
7598
}
76-
})
77-
} else {
78-
None
79-
}
80-
} else {
81-
None
99+
} else {
100+
None
101+
}
102+
}
103+
},
104+
_ => None,
82105
}
83106
}
84107

0 commit comments

Comments
 (0)