@@ -84,6 +84,7 @@ mod skip_while_next;
8484mod stable_sort_primitive;
8585mod str_splitn;
8686mod string_extend_chars;
87+ mod string_lit_chars_any;
8788mod suspicious_command_arg_space;
8889mod suspicious_map;
8990mod suspicious_splitn;
@@ -112,7 +113,7 @@ use clippy_utils::consts::{constant, Constant};
112113use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
113114use clippy_utils:: msrvs:: { self , Msrv } ;
114115use clippy_utils:: ty:: { contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item} ;
115- use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, return_ty} ;
116+ use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks , return_ty} ;
116117use if_chain:: if_chain;
117118use rustc_hir as hir;
118119use rustc_hir:: { Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
@@ -3316,6 +3317,34 @@ declare_clippy_lint! {
33163317 "checks for usage of `Iterator::fold` with a type that implements `Try`"
33173318}
33183319
3320+ declare_clippy_lint ! {
3321+ /// ### What it does
3322+ /// Checks for `<string_lit>.chars().any(|i| i == c)`.
3323+ ///
3324+ /// ### Why is this bad?
3325+ /// It's significantly slower than using a pattern instead, like
3326+ /// `matches!(c, '\\' | '.' | '+')`.
3327+ ///
3328+ /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
3329+ /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
3330+ /// for situations where that additional performance is absolutely necessary.
3331+ ///
3332+ /// ### Example
3333+ /// ```rust
3334+ /// # let c = 'c';
3335+ /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
3336+ /// ```
3337+ /// Use instead:
3338+ /// ```rust
3339+ /// # let c = 'c';
3340+ /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
3341+ /// ```
3342+ #[ clippy:: version = "1.72.0" ]
3343+ pub STRING_LIT_CHARS_ANY ,
3344+ restriction,
3345+ "checks for `<string_lit>.chars().any(|i| i == c)`"
3346+ }
3347+
33193348pub struct Methods {
33203349 avoid_breaking_exported_api : bool ,
33213350 msrv : Msrv ,
@@ -3448,6 +3477,7 @@ impl_lint_pass!(Methods => [
34483477 UNNECESSARY_LITERAL_UNWRAP ,
34493478 DRAIN_COLLECT ,
34503479 MANUAL_TRY_FOLD ,
3480+ STRING_LIT_CHARS_ANY ,
34513481] ) ;
34523482
34533483/// Extracts a method call name, args, and `Span` of the method name.
@@ -3821,6 +3851,13 @@ impl Methods {
38213851 }
38223852 }
38233853 } ,
3854+ ( "any" , [ arg] ) if let ExprKind :: Closure ( arg) = arg. kind
3855+ && let body = cx. tcx . hir ( ) . body ( arg. body )
3856+ && let [ param] = body. params
3857+ && let Some ( ( "chars" , recv, _, _, _) ) = method_call ( recv) =>
3858+ {
3859+ string_lit_chars_any:: check ( cx, expr, recv, param, peel_blocks ( body. value ) , & self . msrv ) ;
3860+ }
38243861 ( "nth" , [ n_arg] ) => match method_call ( recv) {
38253862 Some ( ( "bytes" , recv2, [ ] , _, _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
38263863 Some ( ( "cloned" , recv2, [ ] , _, _) ) => iter_overeager_cloned:: check ( cx, expr, recv, recv2, false , false ) ,
0 commit comments