Auto merge of #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`
This commit is contained in:
commit
1fc1aee7be
@ -2754,6 +2754,7 @@ Released 2018-09-13
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
|
||||
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
|
||||
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
|
||||
filter_recv.kind {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
@ -83,13 +83,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
||||
// the "wild-like" branches must be equal
|
||||
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
|
||||
// the binding must not be used in the if guard
|
||||
let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
|
||||
if match arm.guard {
|
||||
None => true,
|
||||
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
|
||||
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !is_local_used(cx, expr, binding_id),
|
||||
};
|
||||
// ...or anywhere in the inner match
|
||||
if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm));
|
||||
if !arms_inner.iter().any(|arm| is_local_used(cx, arm, binding_id));
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
|
||||
use clippy_utils::{path_to_local_id, visitors::is_local_used};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
@ -65,11 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
|
||||
if let hir::StmtKind::Expr(if_) = expr.kind;
|
||||
if let hir::ExprKind::If(cond, then, ref else_) = if_.kind;
|
||||
let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
|
||||
if !used_visitor.check_expr(cond);
|
||||
if !is_local_used(cx, cond, canonical_id);
|
||||
if let hir::ExprKind::Block(then, _) = then.kind;
|
||||
if let Some(value) = check_assign(cx, canonical_id, &*then);
|
||||
if !used_visitor.check_expr(value);
|
||||
if !is_local_used(cx, value, canonical_id);
|
||||
then {
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
@ -148,15 +147,13 @@ fn check_assign<'tcx>(
|
||||
if let hir::ExprKind::Assign(var, value, _) = expr.kind;
|
||||
if path_to_local_id(var, decl);
|
||||
then {
|
||||
let mut v = LocalUsedVisitor::new(cx, decl);
|
||||
|
||||
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
|
||||
return None;
|
||||
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
|
||||
return Some(value);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -773,6 +773,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
methods::MANUAL_FILTER_MAP,
|
||||
methods::MANUAL_FIND_MAP,
|
||||
methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
methods::MANUAL_SPLIT_ONCE,
|
||||
methods::MANUAL_STR_REPEAT,
|
||||
methods::MAP_COLLECT_RESULT_UNIT,
|
||||
methods::MAP_FLATTEN,
|
||||
@ -1319,6 +1320,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(methods::MANUAL_FIND_MAP),
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MANUAL_SPLIT_ONCE),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
@ -1617,6 +1619,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(methods::ITER_COUNT),
|
||||
LintId::of(methods::MANUAL_FILTER_MAP),
|
||||
LintId::of(methods::MANUAL_FIND_MAP),
|
||||
LintId::of(methods::MANUAL_SPLIT_ONCE),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
@ -66,9 +66,7 @@ pub(super) fn check<'tcx>(
|
||||
fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
|
||||
match *pat {
|
||||
PatKind::Wild => true,
|
||||
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
|
||||
!LocalUsedVisitor::new(cx, id).check_expr(body)
|
||||
},
|
||||
PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::utils::make_iterator_snippet;
|
||||
use super::MANUAL_FLATTEN;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(
|
||||
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
|
||||
if some_ctor || ok_ctor;
|
||||
// Ensure epxr in `if let` is not used afterwards
|
||||
if !LocalUsedVisitor::new(cx, pat_hir_id).check_arm(true_arm);
|
||||
if !is_local_used(cx, true_arm, pat_hir_id);
|
||||
then {
|
||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||
// Prepare the error message
|
||||
|
@ -2,10 +2,8 @@ use super::NEEDLESS_RANGE_LOOP;
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::has_iter_method;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{
|
||||
contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
|
||||
};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -256,43 +254,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
|
||||
if let QPath::Resolved(None, seqvar) = *seqpath;
|
||||
if seqvar.segments.len() == 1;
|
||||
let index_used_directly = path_to_local_id(idx, self.var);
|
||||
let indexed_indirectly = {
|
||||
let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
|
||||
walk_expr(&mut used_visitor, idx);
|
||||
used_visitor.used
|
||||
};
|
||||
if indexed_indirectly || index_used_directly;
|
||||
if is_local_used(self.cx, idx, self.var);
|
||||
then {
|
||||
if self.prefer_mutable {
|
||||
self.indexed_mut.insert(seqvar.segments[0].ident.name);
|
||||
}
|
||||
let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
|
||||
let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
|
||||
match res {
|
||||
Res::Local(hir_id) => {
|
||||
let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||
let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
|
||||
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
|
||||
if indexed_indirectly {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
|
||||
}
|
||||
if index_used_directly {
|
||||
self.indexed_directly.insert(
|
||||
seqvar.segments[0].ident.name,
|
||||
(Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
||||
);
|
||||
} else {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
}
|
||||
Res::Def(DefKind::Static | DefKind::Const, ..) => {
|
||||
if indexed_indirectly {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
|
||||
}
|
||||
if index_used_directly {
|
||||
self.indexed_directly.insert(
|
||||
seqvar.segments[0].ident.name,
|
||||
(None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
||||
);
|
||||
} else {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{
|
||||
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{
|
||||
get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs,
|
||||
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
|
||||
// Looking for unused bindings (i.e.: `_e`)
|
||||
for pat in inner.iter() {
|
||||
if let PatKind::Binding(_, id, ident, None) = pat.kind {
|
||||
if ident.as_str().starts_with('_')
|
||||
&& !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
|
||||
{
|
||||
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
|
||||
ident_bind_name = (&ident.name.as_str()).to_string();
|
||||
matching_wild = true;
|
||||
}
|
||||
|
213
clippy_lints/src/methods/manual_split_once.rs
Normal file
213
clippy_lints/src/methods/manual_split_once.rs
Normal file
@ -0,0 +1,213 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, adjustment::Adjust};
|
||||
use rustc_span::{symbol::sym, Span, SyntaxContext};
|
||||
|
||||
use super::MANUAL_SPLIT_ONCE;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
|
||||
if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
};
|
||||
let (method_name, msg) = if method_name == "splitn" {
|
||||
("split_once", "manual implementation of `split_once`")
|
||||
} else {
|
||||
("rsplit_once", "manual implementation of `rsplit_once`")
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
|
||||
let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
|
||||
|
||||
match usage.kind {
|
||||
IterUsageKind::NextTuple => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_SPLIT_ONCE,
|
||||
usage.span,
|
||||
msg,
|
||||
"try this",
|
||||
format!("{}.{}({})", self_snip, method_name, pat_snip),
|
||||
app,
|
||||
);
|
||||
},
|
||||
IterUsageKind::Next => {
|
||||
let self_deref = {
|
||||
let adjust = cx.typeck_results().expr_adjustments(self_arg);
|
||||
if adjust.is_empty() {
|
||||
String::new()
|
||||
} else if cx.typeck_results().expr_ty(self_arg).is_box()
|
||||
|| adjust
|
||||
.iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
|
||||
{
|
||||
format!("&{}", "*".repeat(adjust.len() - 1))
|
||||
} else {
|
||||
"*".repeat(adjust.len() - 2)
|
||||
}
|
||||
};
|
||||
let sugg = if usage.unwrap_kind.is_some() {
|
||||
format!(
|
||||
"{}.{}({}).map_or({}{}, |x| x.0)",
|
||||
&self_snip, method_name, pat_snip, self_deref, &self_snip
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Some({}.{}({}).map_or({}{}, |x| x.0))",
|
||||
&self_snip, method_name, pat_snip, self_deref, &self_snip
|
||||
)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
|
||||
},
|
||||
IterUsageKind::Second => {
|
||||
let access_str = match usage.unwrap_kind {
|
||||
Some(UnwrapKind::Unwrap) => ".unwrap().1",
|
||||
Some(UnwrapKind::QuestionMark) => "?.1",
|
||||
None => ".map(|x| x.1)",
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_SPLIT_ONCE,
|
||||
usage.span,
|
||||
msg,
|
||||
"try this",
|
||||
format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
|
||||
app,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
enum IterUsageKind {
|
||||
Next,
|
||||
Second,
|
||||
NextTuple,
|
||||
}
|
||||
|
||||
enum UnwrapKind {
|
||||
Unwrap,
|
||||
QuestionMark,
|
||||
}
|
||||
|
||||
struct IterUsage {
|
||||
kind: IterUsageKind,
|
||||
unwrap_kind: Option<UnwrapKind>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
fn parse_iter_usage(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
|
||||
) -> Option<IterUsage> {
|
||||
let (kind, span) = match iter.next() {
|
||||
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
|
||||
let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
|
||||
(name, args)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
|
||||
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
|
||||
|
||||
match (&*name.ident.as_str(), args) {
|
||||
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
|
||||
("next_tuple", []) => {
|
||||
if_chain! {
|
||||
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
|
||||
if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
|
||||
if let ty::Tuple(subs) = subs.type_at(0).kind();
|
||||
if subs.len() == 2;
|
||||
then {
|
||||
return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
},
|
||||
("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
|
||||
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
|
||||
let span = if name.ident.as_str() == "nth" {
|
||||
e.span
|
||||
} else {
|
||||
if_chain! {
|
||||
if let Some((_, Node::Expr(next_expr))) = iter.next();
|
||||
if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
|
||||
if next_name.ident.name == sym::next;
|
||||
if next_expr.span.ctxt() == ctxt;
|
||||
if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
|
||||
if cx.tcx.trait_of_item(next_id) == Some(iter_id);
|
||||
then {
|
||||
next_expr.span
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
match idx {
|
||||
0 => (IterUsageKind::Next, span),
|
||||
1 => (IterUsageKind::Second, span),
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
|
||||
match e.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => {
|
||||
let parent_span = e.span.parent().unwrap();
|
||||
if parent_span.ctxt() == ctxt {
|
||||
(Some(UnwrapKind::QuestionMark), parent_span)
|
||||
} else {
|
||||
(None, span)
|
||||
}
|
||||
},
|
||||
_ if e.span.ctxt() != ctxt => (None, span),
|
||||
ExprKind::MethodCall(name, _, [_], _)
|
||||
if name.ident.name == sym::unwrap
|
||||
&& cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(e.hir_id)
|
||||
.map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) =>
|
||||
{
|
||||
(Some(UnwrapKind::Unwrap), e.span)
|
||||
},
|
||||
_ => (None, span),
|
||||
}
|
||||
} else {
|
||||
(None, span)
|
||||
};
|
||||
|
||||
Some(IterUsage {
|
||||
kind,
|
||||
unwrap_kind,
|
||||
span,
|
||||
})
|
||||
}
|
@ -33,6 +33,7 @@ mod iter_nth_zero;
|
||||
mod iter_skip_next;
|
||||
mod iterator_step_by_zero;
|
||||
mod manual_saturating_arithmetic;
|
||||
mod manual_split_once;
|
||||
mod manual_str_repeat;
|
||||
mod map_collect_result_unit;
|
||||
mod map_flatten;
|
||||
@ -64,6 +65,7 @@ mod wrong_self_convention;
|
||||
mod zst_offset;
|
||||
|
||||
use bind_instead_of_map::BindInsteadOfMap;
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
|
||||
@ -1771,6 +1773,29 @@ declare_clippy_lint! {
|
||||
"manual implementation of `str::repeat`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `str::splitn(2, _)`
|
||||
///
|
||||
/// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let (key, value) = _.splitn(2, '=').next_tuple()?;
|
||||
/// let value = _.splitn(2, '=').nth(1)?;
|
||||
///
|
||||
/// // Good
|
||||
/// let (key, value) = _.split_once('=')?;
|
||||
/// let value = _.split_once('=')?.1;
|
||||
/// ```
|
||||
pub MANUAL_SPLIT_ONCE,
|
||||
complexity,
|
||||
"replace `.splitn(2, pat)` with `.split_once(pat)`"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
@ -1848,7 +1873,8 @@ impl_lint_pass!(Methods => [
|
||||
IMPLICIT_CLONE,
|
||||
SUSPICIOUS_SPLITN,
|
||||
MANUAL_STR_REPEAT,
|
||||
EXTEND_WITH_DRAIN
|
||||
EXTEND_WITH_DRAIN,
|
||||
MANUAL_SPLIT_ONCE
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
@ -2176,8 +2202,18 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
|
||||
}
|
||||
},
|
||||
("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count_arg);
|
||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
|
||||
manual_split_once::check(cx, name, expr, recv, pat_arg);
|
||||
}
|
||||
}
|
||||
},
|
||||
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
}
|
||||
},
|
||||
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_note;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
@ -8,15 +7,8 @@ use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::SUSPICIOUS_SPLITN;
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: &str,
|
||||
expr: &Expr<'_>,
|
||||
self_arg: &Expr<'_>,
|
||||
count_arg: &Expr<'_>,
|
||||
) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) {
|
||||
if_chain! {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg);
|
||||
if count <= 1;
|
||||
if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(call_id);
|
||||
@ -24,9 +16,9 @@ pub(super) fn check(
|
||||
if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id);
|
||||
then {
|
||||
// Ignore empty slice and string literals when used with a literal count.
|
||||
if (matches!(self_arg.kind, ExprKind::Array([]))
|
||||
if matches!(self_arg.kind, ExprKind::Array([]))
|
||||
|| matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty())
|
||||
) && matches!(count_arg.kind, ExprKind::Lit(_))
|
||||
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -50,8 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
||||
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
|
||||
let body = cx.tcx.hir().body(*body_id);
|
||||
if let [self_param, ..] = body.params;
|
||||
let self_hir_id = self_param.pat.hir_id;
|
||||
if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body);
|
||||
if !is_local_used(cx, body, self_param.pat.hir_id);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
@ -132,7 +132,7 @@ define_Conf! {
|
||||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
(avoid_breaking_exported_api: bool = true),
|
||||
/// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE.
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE.
|
||||
///
|
||||
/// The minimum rust version that the project supports
|
||||
(msrv: Option<String> = None),
|
||||
|
@ -21,7 +21,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
|
||||
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
|
||||
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
|
||||
// extract just major + minor version and ignore patch versions
|
||||
format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
|
||||
format!("rust-{}", n.rsplit_once('.').unwrap().1)
|
||||
}),
|
||||
lint
|
||||
));
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(iter_zip)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![recursion_limit = "512"]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
|
||||
|
@ -13,6 +13,7 @@ macro_rules! msrv_aliases {
|
||||
// names may refer to stabilized feature flags or library items
|
||||
msrv_aliases! {
|
||||
1,53,0 { OR_PATTERNS }
|
||||
1,52,0 { STR_SPLIT_ONCE }
|
||||
1,50,0 { BOOL_THEN }
|
||||
1,46,0 { CONST_IF_MATCH }
|
||||
1,45,0 { STR_STRIP_PREFIX }
|
||||
|
@ -68,6 +68,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
|
||||
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
|
||||
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
|
@ -4,6 +4,7 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito
|
||||
use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// returns `true` if expr contains match expr desugared from try
|
||||
fn contains_try(expr: &hir::Expr<'_>) -> bool {
|
||||
@ -133,62 +134,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalUsedVisitor<'hir> {
|
||||
hir: Map<'hir>,
|
||||
pub local_hir_id: HirId,
|
||||
pub used: bool,
|
||||
}
|
||||
|
||||
impl<'hir> LocalUsedVisitor<'hir> {
|
||||
pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
|
||||
Self {
|
||||
hir: cx.tcx.hir(),
|
||||
local_hir_id,
|
||||
used: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
|
||||
visit(self, t);
|
||||
std::mem::replace(&mut self.used, false)
|
||||
}
|
||||
|
||||
pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
|
||||
self.check(arm, Self::visit_arm)
|
||||
}
|
||||
|
||||
pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
|
||||
self.check(body, Self::visit_body)
|
||||
}
|
||||
|
||||
pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
|
||||
self.check(expr, Self::visit_expr)
|
||||
}
|
||||
|
||||
pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
|
||||
self.check(stmt, Self::visit_stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
|
||||
type Map = Map<'v>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'v Expr<'v>) {
|
||||
if self.used {
|
||||
return;
|
||||
}
|
||||
if path_to_local_id(expr, self.local_hir_id) {
|
||||
self.used = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::OnlyBodies(self.hir)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type which can be visited.
|
||||
pub trait Visitable<'tcx> {
|
||||
/// Calls the corresponding `visit_*` function on the visitor.
|
||||
@ -203,7 +148,22 @@ macro_rules! visitable_ref {
|
||||
}
|
||||
};
|
||||
}
|
||||
visitable_ref!(Arm, visit_arm);
|
||||
visitable_ref!(Block, visit_block);
|
||||
visitable_ref!(Body, visit_body);
|
||||
visitable_ref!(Expr, visit_expr);
|
||||
visitable_ref!(Stmt, visit_stmt);
|
||||
|
||||
// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
|
||||
// where
|
||||
// I::Item: Visitable<'tcx>,
|
||||
// {
|
||||
// fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
|
||||
// for x in self {
|
||||
// x.visit(visitor);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Calls the given function for each break expression.
|
||||
pub fn visit_break_exprs<'tcx>(
|
||||
@ -260,3 +220,48 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
||||
v.visit_expr(&cx.tcx.hir().body(body).value);
|
||||
v.found
|
||||
}
|
||||
|
||||
/// Calls the given function for each usage of the given local.
|
||||
pub fn for_each_local_usage<'tcx, B>(
|
||||
cx: &LateContext<'tcx>,
|
||||
visitable: impl Visitable<'tcx>,
|
||||
id: HirId,
|
||||
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
|
||||
) -> ControlFlow<B> {
|
||||
struct V<'tcx, B, F> {
|
||||
map: Map<'tcx>,
|
||||
id: HirId,
|
||||
f: F,
|
||||
res: ControlFlow<B>,
|
||||
}
|
||||
impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
|
||||
type Map = Map<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::OnlyBodies(self.map)
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
if self.res.is_continue() {
|
||||
if path_to_local_id(e, self.id) {
|
||||
self.res = (self.f)(e);
|
||||
} else {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = V {
|
||||
map: cx.tcx.hir(),
|
||||
id,
|
||||
f,
|
||||
res: ControlFlow::CONTINUE,
|
||||
};
|
||||
visitable.visit(&mut v);
|
||||
v.res
|
||||
}
|
||||
|
||||
/// Checks if the given local is used.
|
||||
pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
||||
for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ fn third_party_crates() -> String {
|
||||
"clippy_lints",
|
||||
"clippy_utils",
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"quote",
|
||||
"regex",
|
||||
"serde",
|
||||
|
50
tests/ui/manual_split_once.fixed
Normal file
50
tests/ui/manual_split_once.fixed
Normal file
@ -0,0 +1,50 @@
|
||||
// run-rustfix
|
||||
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![warn(clippy::manual_split_once)]
|
||||
#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
|
||||
|
||||
extern crate itertools;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use itertools::Itertools;
|
||||
|
||||
fn main() {
|
||||
let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
|
||||
let _ = "key=value".splitn(2, '=').nth(2);
|
||||
let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
|
||||
let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
|
||||
let _ = "key=value".split_once('=').unwrap().1;
|
||||
let _ = "key=value".split_once('=').unwrap().1;
|
||||
let (_, _) = "key=value".split_once('=').unwrap();
|
||||
|
||||
let s = String::from("key=value");
|
||||
let _ = s.split_once('=').map_or(&*s, |x| x.0);
|
||||
|
||||
let s = Box::<str>::from("key=value");
|
||||
let _ = s.split_once('=').map_or(&*s, |x| x.0);
|
||||
|
||||
let s = &"key=value";
|
||||
let _ = s.split_once('=').map_or(*s, |x| x.0);
|
||||
|
||||
fn _f(s: &str) -> Option<&str> {
|
||||
let _ = s.split_once("key=value").map_or(s, |x| x.0);
|
||||
let _ = s.split_once("key=value")?.1;
|
||||
let _ = s.split_once("key=value")?.1;
|
||||
None
|
||||
}
|
||||
|
||||
// Don't lint, slices don't have `split_once`
|
||||
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
|
||||
}
|
||||
|
||||
fn _msrv_1_51() {
|
||||
#![clippy::msrv = "1.51"]
|
||||
// `str::split_once` was stabilized in 1.16. Do not lint this
|
||||
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
}
|
||||
|
||||
fn _msrv_1_52() {
|
||||
#![clippy::msrv = "1.52"]
|
||||
let _ = "key=value".split_once('=').unwrap().1;
|
||||
}
|
50
tests/ui/manual_split_once.rs
Normal file
50
tests/ui/manual_split_once.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// run-rustfix
|
||||
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![warn(clippy::manual_split_once)]
|
||||
#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
|
||||
|
||||
extern crate itertools;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use itertools::Itertools;
|
||||
|
||||
fn main() {
|
||||
let _ = "key=value".splitn(2, '=').next();
|
||||
let _ = "key=value".splitn(2, '=').nth(2);
|
||||
let _ = "key=value".splitn(2, '=').next().unwrap();
|
||||
let _ = "key=value".splitn(2, '=').nth(0).unwrap();
|
||||
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
|
||||
let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
|
||||
|
||||
let s = String::from("key=value");
|
||||
let _ = s.splitn(2, '=').next().unwrap();
|
||||
|
||||
let s = Box::<str>::from("key=value");
|
||||
let _ = s.splitn(2, '=').nth(0).unwrap();
|
||||
|
||||
let s = &"key=value";
|
||||
let _ = s.splitn(2, '=').skip(0).next().unwrap();
|
||||
|
||||
fn _f(s: &str) -> Option<&str> {
|
||||
let _ = s.splitn(2, "key=value").next()?;
|
||||
let _ = s.splitn(2, "key=value").nth(1)?;
|
||||
let _ = s.splitn(2, "key=value").skip(1).next()?;
|
||||
None
|
||||
}
|
||||
|
||||
// Don't lint, slices don't have `split_once`
|
||||
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
|
||||
}
|
||||
|
||||
fn _msrv_1_51() {
|
||||
#![clippy::msrv = "1.51"]
|
||||
// `str::split_once` was stabilized in 1.16. Do not lint this
|
||||
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
}
|
||||
|
||||
fn _msrv_1_52() {
|
||||
#![clippy::msrv = "1.52"]
|
||||
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
}
|
82
tests/ui/manual_split_once.stderr
Normal file
82
tests/ui/manual_split_once.stderr
Normal file
@ -0,0 +1,82 @@
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:13:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
|
||||
|
|
||||
= note: `-D clippy::manual-split-once` implied by `-D warnings`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:15:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').next().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:16:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:17:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:18:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:19:18
|
||||
|
|
||||
LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:22:13
|
||||
|
|
||||
LL | let _ = s.splitn(2, '=').next().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:25:13
|
||||
|
|
||||
LL | let _ = s.splitn(2, '=').nth(0).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:28:13
|
||||
|
|
||||
LL | let _ = s.splitn(2, '=').skip(0).next().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:31:17
|
||||
|
|
||||
LL | let _ = s.splitn(2, "key=value").next()?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:32:17
|
||||
|
|
||||
LL | let _ = s.splitn(2, "key=value").nth(1)?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:33:17
|
||||
|
|
||||
LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
|
||||
|
||||
error: manual implementation of `split_once`
|
||||
--> $DIR/manual_split_once.rs:49:13
|
||||
|
|
||||
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user