@@ -11,7 +11,13 @@ use rustc_span::{symbol::sym, Span, SyntaxContext};
1111
1212use super :: MANUAL_SPLIT_ONCE ;
1313
14- pub ( super ) fn check ( cx : & LateContext < ' _ > , method_name : & str , expr : & Expr < ' _ > , self_arg : & Expr < ' _ > , pat_arg : & Expr < ' _ > ) {
14+ pub ( super ) fn check_manual_split_once (
15+ cx : & LateContext < ' _ > ,
16+ method_name : & str ,
17+ expr : & Expr < ' _ > ,
18+ self_arg : & Expr < ' _ > ,
19+ pat_arg : & Expr < ' _ > ,
20+ ) {
1521 if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
1622 return ;
1723 }
@@ -36,7 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
3642 format ! ( "{}.{}({})" , self_snip, method_name, pat_snip)
3743 } ,
3844 IterUsageKind :: RNextTuple => format ! ( "{}.{}({}).map(|(x, y)| (y, x))" , self_snip, method_name, pat_snip) ,
39- IterUsageKind :: Next => {
45+ IterUsageKind :: Next | IterUsageKind :: Second => {
4046 let self_deref = {
4147 let adjust = cx. typeck_results ( ) . expr_adjustments ( self_arg) ;
4248 if adjust. is_empty ( ) {
@@ -51,26 +57,49 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
5157 "*" . repeat ( adjust. len ( ) - 2 )
5258 }
5359 } ;
54- if usage. unwrap_kind . is_some ( ) {
55- format ! (
56- "{}.{}({}).map_or({}{}, |x| x.0)" ,
57- & self_snip, method_name, pat_snip, self_deref, & self_snip
58- )
60+ if matches ! ( usage. kind, IterUsageKind :: Next ) {
61+ match usage. unwrap_kind {
62+ Some ( UnwrapKind :: Unwrap ) => {
63+ if reverse {
64+ format ! ( "{}.{}({}).unwrap().0" , self_snip, method_name, pat_snip)
65+ } else {
66+ format ! (
67+ "{}.{}({}).map_or({}{}, |x| x.0)" ,
68+ self_snip, method_name, pat_snip, self_deref, & self_snip
69+ )
70+ }
71+ } ,
72+ Some ( UnwrapKind :: QuestionMark ) => {
73+ format ! (
74+ "{}.{}({}).map_or({}{}, |x| x.0)" ,
75+ self_snip, method_name, pat_snip, self_deref, & self_snip
76+ )
77+ } ,
78+ None => {
79+ format ! (
80+ "Some({}.{}({}).map_or({}{}, |x| x.0))" ,
81+ & self_snip, method_name, pat_snip, self_deref, & self_snip
82+ )
83+ } ,
84+ }
5985 } else {
60- format ! (
61- "Some({}.{}({}).map_or({}{}, |x| x.0))" ,
62- & self_snip, method_name, pat_snip, self_deref, & self_snip
63- )
86+ match usage. unwrap_kind {
87+ Some ( UnwrapKind :: Unwrap ) => {
88+ if reverse {
89+ // In this case, no better suggestion is offered.
90+ return ;
91+ }
92+ format ! ( "{}.{}({}).unwrap().1" , self_snip, method_name, pat_snip)
93+ } ,
94+ Some ( UnwrapKind :: QuestionMark ) => {
95+ format ! ( "{}.{}({})?.1" , self_snip, method_name, pat_snip)
96+ } ,
97+ None => {
98+ format ! ( "{}.{}({}).map(|x| x.1)" , self_snip, method_name, pat_snip)
99+ } ,
100+ }
64101 }
65102 } ,
66- IterUsageKind :: Second => {
67- let access_str = match usage. unwrap_kind {
68- Some ( UnwrapKind :: Unwrap ) => ".unwrap().1" ,
69- Some ( UnwrapKind :: QuestionMark ) => "?.1" ,
70- None => ".map(|x| x.1)" ,
71- } ;
72- format ! ( "{}.{}({}){}" , self_snip, method_name, pat_snip, access_str)
73- } ,
74103 } ;
75104
76105 span_lint_and_sugg ( cx, MANUAL_SPLIT_ONCE , usage. span , msg, "try this" , sugg, app) ;
@@ -209,3 +238,86 @@ fn parse_iter_usage(
209238 span,
210239 } )
211240}
241+
242+ use super :: NEEDLESS_SPLITN ;
243+
244+ pub ( super ) fn check_needless_splitn (
245+ cx : & LateContext < ' _ > ,
246+ method_name : & str ,
247+ expr : & Expr < ' _ > ,
248+ self_arg : & Expr < ' _ > ,
249+ pat_arg : & Expr < ' _ > ,
250+ count : u128 ,
251+ ) {
252+ if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
253+ return ;
254+ }
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`" )
259+ } else {
260+ ( true , "unnecessary use of `rsplitn`" )
261+ } ;
262+ if_chain ! {
263+ if count >= 2 ;
264+ if check_iter( cx, ctxt, cx. tcx. hir( ) . parent_iter( expr. hir_id) , count) ;
265+ then {
266+ span_lint_and_sugg(
267+ cx,
268+ NEEDLESS_SPLITN ,
269+ expr. span,
270+ message,
271+ "try this" ,
272+ format!(
273+ "{}.{}({})" ,
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
277+ ) ,
278+ app,
279+ ) ;
280+ }
281+ }
282+ }
283+
284+ fn check_iter (
285+ cx : & LateContext < ' tcx > ,
286+ ctxt : SyntaxContext ,
287+ mut iter : impl Iterator < Item = ( HirId , Node < ' tcx > ) > ,
288+ count : u128 ,
289+ ) -> bool {
290+ match iter. next ( ) {
291+ Some ( ( _, Node :: Expr ( e) ) ) if e. span . ctxt ( ) == ctxt => {
292+ let ( name, args) = if let ExprKind :: MethodCall ( name, _, [ _, args @ ..] , _) = e. kind {
293+ ( name, args)
294+ } else {
295+ return false ;
296+ } ;
297+ if_chain ! {
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 ) ;
300+ then {
301+ match ( & * name. ident. as_str( ) , args) {
302+ ( "next" , [ ] ) if cx. tcx. trait_of_item( did) == Some ( iter_id) => {
303+ return true ;
304+ } ,
305+ ( "next_tuple" , [ ] ) if count > 2 => {
306+ return true ;
307+ } ,
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) {
310+ if count > idx + 1 {
311+ return true ;
312+ }
313+ }
314+ } ,
315+ _ => return false ,
316+ }
317+ }
318+ }
319+ } ,
320+ _ => return false ,
321+ } ;
322+ false
323+ }
0 commit comments