Skip to content

Commit 1fc1aee

Browse files
committed
Auto merge of rust-lang#7565 - Jarcho:manual_split_once, r=llogiq
New lint `manual_split_once` This is a WIP because it still needs to recognize more patterns. Currently handles: ```rust s.splitn(2, ' ').next(); s.splitn(2, ' ').nth(0) s.splitn(2, ' ').nth(1); s.splitn(2, ' ').skip(0).next(); s.splitn(2, ' ').skip(1).next(); s.splitn(2, ' ').next_tuple(); // from itertools // as well as `unwrap()` and `?` forms ``` Still to do: ```rust let mut iter = s.splitn(2, ' '); (iter.next().unwrap(), iter.next()?) let mut iter = s.splitn(2, ' '); let key = iter.next().unwrap(); let value = iter.next()?; ``` Suggestions on other patterns to check for would be useful. I've done a search on github for uses of `splitn`. Still have yet to actually look through the results. There's also the question of whether the lint shouold trigger on all uses of `splitn` with two values, or only on recognized usages. The former could have false positives where it couldn't be replaced, but I'm not sure how common that would be. changelog: Add lint `manual_split_once`
2 parents cde7e6b + aab3267 commit 1fc1aee

23 files changed

+538
-120
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2754,6 +2754,7 @@ Released 2018-09-13
27542754
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
27552755
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
27562756
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
2757+
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
27572758
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
27582759
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
27592760
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap

clippy_lints/src/bytecount.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::ty::match_type;
4-
use clippy_utils::visitors::LocalUsedVisitor;
4+
use clippy_utils::visitors::is_local_used;
55
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
@@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
6565
return;
6666
};
6767
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
68-
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
68+
if !is_local_used(cx, needle, arg_id);
6969
then {
7070
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
7171
filter_recv.kind {

clippy_lints/src/collapsible_match.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::visitors::LocalUsedVisitor;
2+
use clippy_utils::visitors::is_local_used;
33
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
44
use if_chain::if_chain;
55
use rustc_hir::LangItem::OptionNone;
@@ -83,13 +83,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
8383
// the "wild-like" branches must be equal
8484
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
8585
// the binding must not be used in the if guard
86-
let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
8786
if match arm.guard {
8887
None => true,
89-
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
88+
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !is_local_used(cx, expr, binding_id),
9089
};
9190
// ...or anywhere in the inner match
92-
if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm));
91+
if !arms_inner.iter().any(|arm| is_local_used(cx, arm, binding_id));
9392
then {
9493
span_lint_and_then(
9594
cx,

clippy_lints/src/let_if_seq.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
3-
use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
3+
use clippy_utils::{path_to_local_id, visitors::is_local_used};
44
use if_chain::if_chain;
55
use rustc_errors::Applicability;
66
use rustc_hir as hir;
@@ -65,11 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
6565
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
6666
if let hir::StmtKind::Expr(if_) = expr.kind;
6767
if let hir::ExprKind::If(cond, then, ref else_) = if_.kind;
68-
let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
69-
if !used_visitor.check_expr(cond);
68+
if !is_local_used(cx, cond, canonical_id);
7069
if let hir::ExprKind::Block(then, _) = then.kind;
7170
if let Some(value) = check_assign(cx, canonical_id, &*then);
72-
if !used_visitor.check_expr(value);
71+
if !is_local_used(cx, value, canonical_id);
7372
then {
7473
let span = stmt.span.to(if_.span);
7574

@@ -148,15 +147,13 @@ fn check_assign<'tcx>(
148147
if let hir::ExprKind::Assign(var, value, _) = expr.kind;
149148
if path_to_local_id(var, decl);
150149
then {
151-
let mut v = LocalUsedVisitor::new(cx, decl);
152-
153-
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
154-
return None;
150+
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
151+
None
152+
} else {
153+
Some(value)
155154
}
156-
157-
return Some(value);
155+
} else {
156+
None
158157
}
159158
}
160-
161-
None
162159
}

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
773773
methods::MANUAL_FILTER_MAP,
774774
methods::MANUAL_FIND_MAP,
775775
methods::MANUAL_SATURATING_ARITHMETIC,
776+
methods::MANUAL_SPLIT_ONCE,
776777
methods::MANUAL_STR_REPEAT,
777778
methods::MAP_COLLECT_RESULT_UNIT,
778779
methods::MAP_FLATTEN,
@@ -1319,6 +1320,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13191320
LintId::of(methods::MANUAL_FILTER_MAP),
13201321
LintId::of(methods::MANUAL_FIND_MAP),
13211322
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
1323+
LintId::of(methods::MANUAL_SPLIT_ONCE),
13221324
LintId::of(methods::MANUAL_STR_REPEAT),
13231325
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
13241326
LintId::of(methods::MAP_IDENTITY),
@@ -1617,6 +1619,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16171619
LintId::of(methods::ITER_COUNT),
16181620
LintId::of(methods::MANUAL_FILTER_MAP),
16191621
LintId::of(methods::MANUAL_FIND_MAP),
1622+
LintId::of(methods::MANUAL_SPLIT_ONCE),
16201623
LintId::of(methods::MAP_IDENTITY),
16211624
LintId::of(methods::OPTION_AS_REF_DEREF),
16221625
LintId::of(methods::OPTION_FILTER_MAP),

clippy_lints/src/loops/for_kv_map.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
33
use clippy_utils::source::snippet;
44
use clippy_utils::sugg;
55
use clippy_utils::ty::is_type_diagnostic_item;
6-
use clippy_utils::visitors::LocalUsedVisitor;
6+
use clippy_utils::visitors::is_local_used;
77
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
88
use rustc_lint::LateContext;
99
use rustc_middle::ty;
@@ -66,9 +66,7 @@ pub(super) fn check<'tcx>(
6666
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
6767
match *pat {
6868
PatKind::Wild => true,
69-
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
70-
!LocalUsedVisitor::new(cx, id).check_expr(body)
71-
},
69+
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
7270
_ => false,
7371
}
7472
}

clippy_lints/src/loops/manual_flatten.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::utils::make_iterator_snippet;
22
use super::MANUAL_FLATTEN;
33
use clippy_utils::diagnostics::span_lint_and_then;
4-
use clippy_utils::visitors::LocalUsedVisitor;
4+
use clippy_utils::visitors::is_local_used;
55
use clippy_utils::{is_lang_ctor, path_to_local_id};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
@@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(
4949
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
5050
if some_ctor || ok_ctor;
5151
// Ensure epxr in `if let` is not used afterwards
52-
if !LocalUsedVisitor::new(cx, pat_hir_id).check_arm(true_arm);
52+
if !is_local_used(cx, true_arm, pat_hir_id);
5353
then {
5454
let if_let_type = if some_ctor { "Some" } else { "Ok" };
5555
// Prepare the error message

clippy_lints/src/loops/needless_range_loop.rs

+8-17
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ use super::NEEDLESS_RANGE_LOOP;
22
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
33
use clippy_utils::source::snippet;
44
use clippy_utils::ty::has_iter_method;
5-
use clippy_utils::visitors::LocalUsedVisitor;
6-
use clippy_utils::{
7-
contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
8-
};
5+
use clippy_utils::visitors::is_local_used;
6+
use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
97
use if_chain::if_chain;
108
use rustc_ast::ast;
119
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -256,43 +254,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
256254
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
257255
if let QPath::Resolved(None, seqvar) = *seqpath;
258256
if seqvar.segments.len() == 1;
259-
let index_used_directly = path_to_local_id(idx, self.var);
260-
let indexed_indirectly = {
261-
let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
262-
walk_expr(&mut used_visitor, idx);
263-
used_visitor.used
264-
};
265-
if indexed_indirectly || index_used_directly;
257+
if is_local_used(self.cx, idx, self.var);
266258
then {
267259
if self.prefer_mutable {
268260
self.indexed_mut.insert(seqvar.segments[0].ident.name);
269261
}
262+
let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
270263
let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
271264
match res {
272265
Res::Local(hir_id) => {
273266
let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
274267
let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
275268
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
276-
if indexed_indirectly {
277-
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
278-
}
279269
if index_used_directly {
280270
self.indexed_directly.insert(
281271
seqvar.segments[0].ident.name,
282272
(Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
283273
);
274+
} else {
275+
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
284276
}
285277
return false; // no need to walk further *on the variable*
286278
}
287279
Res::Def(DefKind::Static | DefKind::Const, ..) => {
288-
if indexed_indirectly {
289-
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
290-
}
291280
if index_used_directly {
292281
self.indexed_directly.insert(
293282
seqvar.segments[0].ident.name,
294283
(None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
295284
);
285+
} else {
286+
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
296287
}
297288
return false; // no need to walk further *on the variable*
298289
}

clippy_lints/src/matches.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{
55
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
66
use clippy_utils::sugg::Sugg;
77
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
8-
use clippy_utils::visitors::LocalUsedVisitor;
8+
use clippy_utils::visitors::is_local_used;
99
use clippy_utils::{
1010
get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs,
1111
path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
@@ -953,9 +953,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
953953
// Looking for unused bindings (i.e.: `_e`)
954954
for pat in inner.iter() {
955955
if let PatKind::Binding(_, id, ident, None) = pat.kind {
956-
if ident.as_str().starts_with('_')
957-
&& !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
958-
{
956+
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
959957
ident_bind_name = (&ident.name.as_str()).to_string();
960958
matching_wild = true;
961959
}

0 commit comments

Comments
 (0)