@@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method
5
5
use clippy_utils:: { peel_blocks, SpanlessEq } ;
6
6
use if_chain:: if_chain;
7
7
use rustc_errors:: Applicability ;
8
+ use rustc_hir:: def_id:: DefId ;
8
9
use rustc_hir:: { BinOpKind , BorrowKind , Expr , ExprKind , LangItem , QPath } ;
9
10
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
11
use rustc_middle:: lint:: in_external_macro;
@@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
451
452
}
452
453
}
453
454
}
455
+
456
+ declare_clippy_lint ! {
457
+ /// ### What it does
458
+ /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
459
+ ///
460
+ /// ### Why is this bad?
461
+ /// `split_whitespace` already ignores leading and trailing whitespace.
462
+ ///
463
+ /// ### Example
464
+ /// ```rust
465
+ /// " A B C ".trim().split_whitespace();
466
+ /// ```
467
+ /// Use instead:
468
+ /// ```rust
469
+ /// " A B C ".split_whitespace();
470
+ /// ```
471
+ #[ clippy:: version = "1.62.0" ]
472
+ pub TRIM_SPLIT_WHITESPACE ,
473
+ style,
474
+ "using `str::trim()` or alike before `str::split_whitespace`"
475
+ }
476
+ declare_lint_pass ! ( TrimSplitWhitespace => [ TRIM_SPLIT_WHITESPACE ] ) ;
477
+
478
+ impl < ' tcx > LateLintPass < ' tcx > for TrimSplitWhitespace {
479
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & Expr < ' _ > ) {
480
+ let tyckres = cx. typeck_results ( ) ;
481
+ if_chain ! {
482
+ if let ExprKind :: MethodCall ( path, [ split_recv] , split_ws_span) = expr. kind;
483
+ if path. ident. name == sym!( split_whitespace) ;
484
+ if let Some ( split_ws_def_id) = tyckres. type_dependent_def_id( expr. hir_id) ;
485
+ if cx. tcx. is_diagnostic_item( sym:: str_split_whitespace, split_ws_def_id) ;
486
+ if let ExprKind :: MethodCall ( path, [ _trim_recv] , trim_span) = split_recv. kind;
487
+ if let trim_fn_name @ ( "trim" | "trim_start" | "trim_end" ) = path. ident. name. as_str( ) ;
488
+ if let Some ( trim_def_id) = tyckres. type_dependent_def_id( split_recv. hir_id) ;
489
+ if is_one_of_trim_diagnostic_items( cx, trim_def_id) ;
490
+ then {
491
+ span_lint_and_sugg(
492
+ cx,
493
+ TRIM_SPLIT_WHITESPACE ,
494
+ trim_span. with_hi( split_ws_span. lo( ) ) ,
495
+ & format!( "found call to `str::{}` before `str::split_whitespace`" , trim_fn_name) ,
496
+ & format!( "remove `{}()`" , trim_fn_name) ,
497
+ String :: new( ) ,
498
+ Applicability :: MachineApplicable ,
499
+ ) ;
500
+ }
501
+ }
502
+ }
503
+ }
504
+
505
+ fn is_one_of_trim_diagnostic_items ( cx : & LateContext < ' _ > , trim_def_id : DefId ) -> bool {
506
+ cx. tcx . is_diagnostic_item ( sym:: str_trim, trim_def_id)
507
+ || cx. tcx . is_diagnostic_item ( sym:: str_trim_start, trim_def_id)
508
+ || cx. tcx . is_diagnostic_item ( sym:: str_trim_end, trim_def_id)
509
+ }
0 commit comments