@@ -86,6 +86,7 @@ mod skip_while_next;
86
86
mod stable_sort_primitive;
87
87
mod str_splitn;
88
88
mod string_extend_chars;
89
+ mod string_lit_chars_any;
89
90
mod suspicious_command_arg_space;
90
91
mod suspicious_map;
91
92
mod suspicious_splitn;
@@ -115,7 +116,7 @@ use clippy_utils::consts::{constant, Constant};
115
116
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
116
117
use clippy_utils:: msrvs:: { self , Msrv } ;
117
118
use clippy_utils:: ty:: { contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item} ;
118
- use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, return_ty} ;
119
+ use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks , return_ty} ;
119
120
use if_chain:: if_chain;
120
121
use rustc_hir as hir;
121
122
use rustc_hir:: { Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
@@ -3379,6 +3380,34 @@ declare_clippy_lint! {
3379
3380
"calling `Stdin::read_line`, then trying to parse it without first trimming"
3380
3381
}
3381
3382
3383
+ declare_clippy_lint ! {
3384
+ /// ### What it does
3385
+ /// Checks for `<string_lit>.chars().any(|i| i == c)`.
3386
+ ///
3387
+ /// ### Why is this bad?
3388
+ /// It's significantly slower than using a pattern instead, like
3389
+ /// `matches!(c, '\\' | '.' | '+')`.
3390
+ ///
3391
+ /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
3392
+ /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
3393
+ /// for situations where that additional performance is absolutely necessary.
3394
+ ///
3395
+ /// ### Example
3396
+ /// ```rust
3397
+ /// # let c = 'c';
3398
+ /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
3399
+ /// ```
3400
+ /// Use instead:
3401
+ /// ```rust
3402
+ /// # let c = 'c';
3403
+ /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
3404
+ /// ```
3405
+ #[ clippy:: version = "1.72.0" ]
3406
+ pub STRING_LIT_CHARS_ANY ,
3407
+ restriction,
3408
+ "checks for `<string_lit>.chars().any(|i| i == c)`"
3409
+ }
3410
+
3382
3411
declare_clippy_lint ! {
3383
3412
/// ### What it does
3384
3413
/// Checks for usage of `.map(|_| format!(..)).collect::<String>()`.
@@ -3549,6 +3578,7 @@ impl_lint_pass!(Methods => [
3549
3578
DRAIN_COLLECT ,
3550
3579
MANUAL_TRY_FOLD ,
3551
3580
FORMAT_COLLECT ,
3581
+ STRING_LIT_CHARS_ANY ,
3552
3582
] ) ;
3553
3583
3554
3584
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3923,6 +3953,13 @@ impl Methods {
3923
3953
}
3924
3954
}
3925
3955
} ,
3956
+ ( "any" , [ arg] ) if let ExprKind :: Closure ( arg) = arg. kind
3957
+ && let body = cx. tcx . hir ( ) . body ( arg. body )
3958
+ && let [ param] = body. params
3959
+ && let Some ( ( "chars" , recv, _, _, _) ) = method_call ( recv) =>
3960
+ {
3961
+ string_lit_chars_any:: check ( cx, expr, recv, param, peel_blocks ( body. value ) , & self . msrv ) ;
3962
+ }
3926
3963
( "nth" , [ n_arg] ) => match method_call ( recv) {
3927
3964
Some ( ( "bytes" , recv2, [ ] , _, _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
3928
3965
Some ( ( "cloned" , recv2, [ ] , _, _) ) => iter_overeager_cloned:: check ( cx, expr, recv, recv2, false , false ) ,
0 commit comments