11use clippy_utils:: consts:: { constant, Constant } ;
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use clippy_utils:: source:: snippet_with_context;
4- use clippy_utils:: { is_diag_item_method, match_def_path, paths} ;
4+ use clippy_utils:: { is_diag_item_method, match_def_path, meets_msrv , msrvs , paths} ;
55use if_chain:: if_chain;
66use rustc_errors:: Applicability ;
77use rustc_hir:: { Expr , ExprKind , HirId , LangItem , Node , QPath } ;
88use rustc_lint:: LateContext ;
9- use rustc_middle:: ty:: { self , adjustment:: Adjust } ;
9+ use rustc_middle:: ty;
10+ use rustc_semver:: RustcVersion ;
1011use rustc_span:: { symbol:: sym, Span , SyntaxContext } ;
1112
12- use super :: MANUAL_SPLIT_ONCE ;
13+ use super :: { MANUAL_SPLIT_ONCE , NEEDLESS_SPLITN } ;
1314
14- pub ( super ) fn check_manual_split_once (
15+ pub ( super ) fn check (
1516 cx : & LateContext < ' _ > ,
1617 method_name : & str ,
1718 expr : & Expr < ' _ > ,
1819 self_arg : & Expr < ' _ > ,
1920 pat_arg : & Expr < ' _ > ,
21+ count : u128 ,
22+ msrv : Option < & RustcVersion > ,
2023) {
21- if !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
24+ if count < 2 || !cx. typeck_results ( ) . expr_ty_adjusted ( self_arg) . peel_refs ( ) . is_str ( ) {
2225 return ;
2326 }
2427
2528 let ctxt = expr. span . ctxt ( ) ;
26- let ( method_name, msg, reverse) = if method_name == "splitn" {
27- ( "split_once" , "manual implementation of `split_once`" , false )
28- } else {
29- ( "rsplit_once" , "manual implementation of `rsplit_once`" , true )
29+ let Some ( usage) = parse_iter_usage ( cx, ctxt, cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) ) else { return } ;
30+
31+ let needless = match usage. kind {
32+ IterUsageKind :: Nth ( n) => count > n + 1 ,
33+ IterUsageKind :: NextTuple => count > 2 ,
3034 } ;
31- let usage = match parse_iter_usage ( cx, ctxt, cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) , reverse) {
32- Some ( x) => x,
33- None => return ,
35+
36+ if needless {
37+ let mut app = Applicability :: MachineApplicable ;
38+ let ( r, message) = if method_name == "splitn" {
39+ ( "" , "unnecessary use of `splitn`" )
40+ } else {
41+ ( "r" , "unnecessary use of `rsplitn`" )
42+ } ;
43+
44+ span_lint_and_sugg (
45+ cx,
46+ NEEDLESS_SPLITN ,
47+ expr. span ,
48+ message,
49+ "try this" ,
50+ format ! (
51+ "{}.{r}split({})" ,
52+ snippet_with_context( cx, self_arg. span, ctxt, ".." , & mut app) . 0 ,
53+ snippet_with_context( cx, pat_arg. span, ctxt, ".." , & mut app) . 0 ,
54+ ) ,
55+ app,
56+ ) ;
57+ } else if count == 2 && meets_msrv ( msrv, & msrvs:: STR_SPLIT_ONCE ) {
58+ check_manual_split_once ( cx, method_name, expr, self_arg, pat_arg, & usage) ;
59+ }
60+ }
61+
62+ fn check_manual_split_once (
63+ cx : & LateContext < ' _ > ,
64+ method_name : & str ,
65+ expr : & Expr < ' _ > ,
66+ self_arg : & Expr < ' _ > ,
67+ pat_arg : & Expr < ' _ > ,
68+ usage : & IterUsage ,
69+ ) {
70+ let ctxt = expr. span . ctxt ( ) ;
71+ let ( msg, reverse) = if method_name == "splitn" {
72+ ( "manual implementation of `split_once`" , false )
73+ } else {
74+ ( "manual implementation of `rsplit_once`" , true )
3475 } ;
3576
3677 let mut app = Applicability :: MachineApplicable ;
@@ -39,77 +80,36 @@ pub(super) fn check_manual_split_once(
3980
4081 let sugg = match usage. kind {
4182 IterUsageKind :: NextTuple => {
42- format ! ( "{}.{}({})" , self_snip, method_name, pat_snip)
43- } ,
44- IterUsageKind :: RNextTuple => format ! ( "{}.{}({}).map(|(x, y)| (y, x))" , self_snip, method_name, pat_snip) ,
45- IterUsageKind :: Next | IterUsageKind :: Second => {
46- let self_deref = {
47- let adjust = cx. typeck_results ( ) . expr_adjustments ( self_arg) ;
48- if adjust. len ( ) < 2 {
49- String :: new ( )
50- } else if cx. typeck_results ( ) . expr_ty ( self_arg) . is_box ( )
51- || adjust
52- . iter ( )
53- . any ( |a| matches ! ( a. kind, Adjust :: Deref ( Some ( _) ) ) || a. target . is_box ( ) )
54- {
55- format ! ( "&{}" , "*" . repeat( adjust. len( ) . saturating_sub( 1 ) ) )
56- } else {
57- "*" . repeat ( adjust. len ( ) . saturating_sub ( 2 ) )
58- }
59- } ;
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- }
83+ if reverse {
84+ format ! ( "{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))" )
8585 } else {
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- }
86+ format ! ( "{self_snip}.split_once({pat_snip})" )
10187 }
10288 } ,
89+ IterUsageKind :: Nth ( 1 ) => {
90+ let ( r, field) = if reverse { ( "r" , 0 ) } else { ( "" , 1 ) } ;
91+
92+ match usage. unwrap_kind {
93+ Some ( UnwrapKind :: Unwrap ) => {
94+ format ! ( "{self_snip}.{r}split_once({pat_snip}).unwrap().{field}" )
95+ } ,
96+ Some ( UnwrapKind :: QuestionMark ) => {
97+ format ! ( "{self_snip}.{r}split_once({pat_snip})?.{field}" )
98+ } ,
99+ None => {
100+ format ! ( "{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})" )
101+ } ,
102+ }
103+ } ,
104+ IterUsageKind :: Nth ( _) => return ,
103105 } ;
104106
105107 span_lint_and_sugg ( cx, MANUAL_SPLIT_ONCE , usage. span , msg, "try this" , sugg, app) ;
106108}
107109
108110enum IterUsageKind {
109- Next ,
110- Second ,
111+ Nth ( u128 ) ,
111112 NextTuple ,
112- RNextTuple ,
113113}
114114
115115enum UnwrapKind {
@@ -128,7 +128,6 @@ fn parse_iter_usage<'tcx>(
128128 cx : & LateContext < ' tcx > ,
129129 ctxt : SyntaxContext ,
130130 mut iter : impl Iterator < Item = ( HirId , Node < ' tcx > ) > ,
131- reverse : bool ,
132131) -> Option < IterUsage > {
133132 let ( kind, span) = match iter. next ( ) {
134133 Some ( ( _, Node :: Expr ( e) ) ) if e. span . ctxt ( ) == ctxt => {
@@ -141,13 +140,7 @@ fn parse_iter_usage<'tcx>(
141140 let iter_id = cx. tcx . get_diagnostic_item ( sym:: Iterator ) ?;
142141
143142 match ( name. ident . as_str ( ) , args) {
144- ( "next" , [ ] ) if cx. tcx . trait_of_item ( did) == Some ( iter_id) => {
145- if reverse {
146- ( IterUsageKind :: Second , e. span )
147- } else {
148- ( IterUsageKind :: Next , e. span )
149- }
150- } ,
143+ ( "next" , [ ] ) if cx. tcx . trait_of_item ( did) == Some ( iter_id) => ( IterUsageKind :: Nth ( 0 ) , e. span ) ,
151144 ( "next_tuple" , [ ] ) => {
152145 return if_chain ! {
153146 if match_def_path( cx, did, & paths:: ITERTOOLS_NEXT_TUPLE ) ;
@@ -157,7 +150,7 @@ fn parse_iter_usage<'tcx>(
157150 if subs. len( ) == 2 ;
158151 then {
159152 Some ( IterUsage {
160- kind: if reverse { IterUsageKind :: RNextTuple } else { IterUsageKind :: NextTuple } ,
153+ kind: IterUsageKind :: NextTuple ,
161154 span: e. span,
162155 unwrap_kind: None
163156 } )
@@ -185,11 +178,7 @@ fn parse_iter_usage<'tcx>(
185178 }
186179 }
187180 } ;
188- match if reverse { idx ^ 1 } else { idx } {
189- 0 => ( IterUsageKind :: Next , span) ,
190- 1 => ( IterUsageKind :: Second , span) ,
191- _ => return None ,
192- }
181+ ( IterUsageKind :: Nth ( idx) , span)
193182 } else {
194183 return None ;
195184 }
@@ -238,86 +227,3 @@ fn parse_iter_usage<'tcx>(
238227 span,
239228 } )
240229}
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 < ' tcx > (
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