1 use clippy_utils
::consts
::{constant, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
3 use clippy_utils
::source
::snippet_with_context
;
4 use clippy_utils
::{is_diag_item_method, match_def_path, paths}
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{Expr, ExprKind, HirId, LangItem, Node, QPath}
;
8 use rustc_lint
::LateContext
;
9 use rustc_middle
::ty
::{self, adjustment::Adjust}
;
10 use rustc_span
::{symbol::sym, Span, SyntaxContext}
;
12 use super::MANUAL_SPLIT_ONCE
;
14 pub(super) fn check_manual_split_once(
21 if !cx
.typeck_results().expr_ty_adjusted(self_arg
).peel_refs().is_str() {
25 let ctxt
= expr
.span
.ctxt();
26 let (method_name
, msg
, reverse
) = if method_name
== "splitn" {
27 ("split_once", "manual implementation of `split_once`", false)
29 ("rsplit_once", "manual implementation of `rsplit_once`", true)
31 let usage
= match parse_iter_usage(cx
, ctxt
, cx
.tcx
.hir().parent_iter(expr
.hir_id
), reverse
) {
36 let mut app
= Applicability
::MachineApplicable
;
37 let self_snip
= snippet_with_context(cx
, self_arg
.span
, ctxt
, "..", &mut app
).0;
38 let pat_snip
= snippet_with_context(cx
, pat_arg
.span
, ctxt
, "..", &mut app
).0;
40 let sugg
= match usage
.kind
{
41 IterUsageKind
::NextTuple
=> {
42 format
!("{}.{}({})", self_snip
, method_name
, pat_snip
)
44 IterUsageKind
::RNextTuple
=> format
!("{}.{}({}).map(|(x, y)| (y, x))", self_snip
, method_name
, pat_snip
),
45 IterUsageKind
::Next
| IterUsageKind
::Second
=> {
47 let adjust
= cx
.typeck_results().expr_adjustments(self_arg
);
50 } else if cx
.typeck_results().expr_ty(self_arg
).is_box()
53 .any(|a
| matches
!(a
.kind
, Adjust
::Deref(Some(_
))) || a
.target
.is_box())
55 format
!("&{}", "*".repeat(adjust
.len().saturating_sub(1)))
57 "*".repeat(adjust
.len().saturating_sub(2))
60 if matches
!(usage
.kind
, IterUsageKind
::Next
) {
61 match usage
.unwrap_kind
{
62 Some(UnwrapKind
::Unwrap
) => {
64 format
!("{}.{}({}).unwrap().0", self_snip
, method_name
, pat_snip
)
67 "{}.{}({}).map_or({}{}, |x| x.0)",
68 self_snip
, method_name
, pat_snip
, self_deref
, &self_snip
72 Some(UnwrapKind
::QuestionMark
) => {
74 "{}.{}({}).map_or({}{}, |x| x.0)",
75 self_snip
, method_name
, pat_snip
, self_deref
, &self_snip
80 "Some({}.{}({}).map_or({}{}, |x| x.0))",
81 &self_snip
, method_name
, pat_snip
, self_deref
, &self_snip
86 match usage
.unwrap_kind
{
87 Some(UnwrapKind
::Unwrap
) => {
89 // In this case, no better suggestion is offered.
92 format
!("{}.{}({}).unwrap().1", self_snip
, method_name
, pat_snip
)
94 Some(UnwrapKind
::QuestionMark
) => {
95 format
!("{}.{}({})?.1", self_snip
, method_name
, pat_snip
)
98 format
!("{}.{}({}).map(|x| x.1)", self_snip
, method_name
, pat_snip
)
105 span_lint_and_sugg(cx
, MANUAL_SPLIT_ONCE
, usage
.span
, msg
, "try this", sugg
, app
);
122 unwrap_kind
: Option
<UnwrapKind
>,
126 #[allow(clippy::too_many_lines)]
127 fn parse_iter_usage
<'tcx
>(
128 cx
: &LateContext
<'tcx
>,
130 mut iter
: impl Iterator
<Item
= (HirId
, Node
<'tcx
>)>,
132 ) -> Option
<IterUsage
> {
133 let (kind
, span
) = match iter
.next() {
134 Some((_
, Node
::Expr(e
))) if e
.span
.ctxt() == ctxt
=> {
135 let (name
, args
) = if let ExprKind
::MethodCall(name
, [_
, args @
..], _
) = e
.kind
{
140 let did
= cx
.typeck_results().type_dependent_def_id(e
.hir_id
)?
;
141 let iter_id
= cx
.tcx
.get_diagnostic_item(sym
::Iterator
)?
;
143 match (name
.ident
.as_str(), args
) {
144 ("next", []) if cx
.tcx
.trait_of_item(did
) == Some(iter_id
) => {
146 (IterUsageKind
::Second
, e
.span
)
148 (IterUsageKind
::Next
, e
.span
)
151 ("next_tuple", []) => {
153 if match_def_path(cx
, did
, &paths
::ITERTOOLS_NEXT_TUPLE
);
154 if let ty
::Adt(adt_def
, subs
) = cx
.typeck_results().expr_ty(e
).kind();
155 if cx
.tcx
.is_diagnostic_item(sym
::Option
, adt_def
.did());
156 if let ty
::Tuple(subs
) = subs
.type_at(0).kind();
160 kind
: if reverse { IterUsageKind::RNextTuple }
else { IterUsageKind::NextTuple }
,
169 ("nth" | "skip", [idx_expr
]) if cx
.tcx
.trait_of_item(did
) == Some(iter_id
) => {
170 if let Some((Constant
::Int(idx
), _
)) = constant(cx
, cx
.typeck_results(), idx_expr
) {
171 let span
= if name
.ident
.as_str() == "nth" {
175 if let Some((_
, Node
::Expr(next_expr
))) = iter
.next();
176 if let ExprKind
::MethodCall(next_name
, [_
], _
) = next_expr
.kind
;
177 if next_name
.ident
.name
== sym
::next
;
178 if next_expr
.span
.ctxt() == ctxt
;
179 if let Some(next_id
) = cx
.typeck_results().type_dependent_def_id(next_expr
.hir_id
);
180 if cx
.tcx
.trait_of_item(next_id
) == Some(iter_id
);
188 match if reverse { idx ^ 1 }
else { idx }
{
189 0 => (IterUsageKind
::Next
, span
),
190 1 => (IterUsageKind
::Second
, span
),
203 let (unwrap_kind
, span
) = if let Some((_
, Node
::Expr(e
))) = iter
.next() {
207 kind
: ExprKind
::Path(QPath
::LangItem(LangItem
::TryTraitBranch
, ..)),
212 let parent_span
= e
.span
.parent_callsite().unwrap();
213 if parent_span
.ctxt() == ctxt
{
214 (Some(UnwrapKind
::QuestionMark
), parent_span
)
219 _
if e
.span
.ctxt() != ctxt
=> (None
, span
),
220 ExprKind
::MethodCall(name
, [_
], _
)
221 if name
.ident
.name
== sym
::unwrap
224 .type_dependent_def_id(e
.hir_id
)
225 .map_or(false, |id
| is_diag_item_method(cx
, id
, sym
::Option
)) =>
227 (Some(UnwrapKind
::Unwrap
), e
.span
)
242 use super::NEEDLESS_SPLITN
;
244 pub(super) fn check_needless_splitn(
245 cx
: &LateContext
<'_
>,
252 if !cx
.typeck_results().expr_ty_adjusted(self_arg
).peel_refs().is_str() {
255 let ctxt
= expr
.span
.ctxt();
256 let mut app
= Applicability
::MachineApplicable
;
257 let (reverse
, message
) = if method_name
== "splitn" {
258 (false, "unnecessary use of `splitn`")
260 (true, "unnecessary use of `rsplitn`")
264 if check_iter(cx
, ctxt
, cx
.tcx
.hir().parent_iter(expr
.hir_id
), count
);
274 snippet_with_context(cx
, self_arg
.span
, ctxt
, "..", &mut app
).0,
275 if reverse {"rsplit"}
else {"split"}
,
276 snippet_with_context(cx
, pat_arg
.span
, ctxt
, "..", &mut app
).0
285 cx
: &LateContext
<'tcx
>,
287 mut iter
: impl Iterator
<Item
= (HirId
, Node
<'tcx
>)>,
291 Some((_
, Node
::Expr(e
))) if e
.span
.ctxt() == ctxt
=> {
292 let (name
, args
) = if let ExprKind
::MethodCall(name
, [_
, args @
..], _
) = e
.kind
{
298 if let Some(did
) = cx
.typeck_results().type_dependent_def_id(e
.hir_id
);
299 if let Some(iter_id
) = cx
.tcx
.get_diagnostic_item(sym
::Iterator
);
301 match (name
.ident
.as_str(), args
) {
302 ("next", []) if cx
.tcx
.trait_of_item(did
) == Some(iter_id
) => {
305 ("next_tuple", []) if count
> 2 => {
308 ("nth", [idx_expr
]) if cx
.tcx
.trait_of_item(did
) == Some(iter_id
) => {
309 if let Some((Constant
::Int(idx
), _
)) = constant(cx
, cx
.typeck_results(), idx_expr
) {