@@ -83,6 +83,7 @@ mod skip_while_next;
8383mod stable_sort_primitive;
8484mod str_splitn;
8585mod string_extend_chars;
86+ mod string_lit_chars_any;
8687mod suspicious_command_arg_space;
8788mod suspicious_map;
8889mod suspicious_splitn;
@@ -111,7 +112,7 @@ use clippy_utils::consts::{constant, Constant};
111112use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
112113use clippy_utils:: msrvs:: { self , Msrv } ;
113114use clippy_utils:: ty:: { contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item} ;
114- use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, return_ty} ;
115+ use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks , return_ty} ;
115116use if_chain:: if_chain;
116117use rustc_hir as hir;
117118use rustc_hir:: { Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
@@ -3286,6 +3287,34 @@ declare_clippy_lint! {
32863287 "calling `.drain(..).collect()` to move all elements into a new collection"
32873288}
32883289
3290+ declare_clippy_lint ! {
3291+ /// ### What it does
3292+ /// Checks for `<string_lit>.chars().any(|i| i == c)`.
3293+ ///
3294+ /// ### Why is this bad?
3295+ /// It's significantly slower than using a pattern instead, like
3296+ /// `matches!(c, '\\' | '.' | '+')`.
3297+ ///
3298+ /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
3299+ /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
3300+ /// for situations where that additional performance is absolutely necessary.
3301+ ///
3302+ /// ### Example
3303+ /// ```rust
3304+ /// # let c = 'c';
3305+ /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
3306+ /// ```
3307+ /// Use instead:
3308+ /// ```rust
3309+ /// # let c = 'c';
3310+ /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
3311+ /// ```
3312+ #[ clippy:: version = "1.72.0" ]
3313+ pub STRING_LIT_CHARS_ANY ,
3314+ restriction,
3315+ "checks for `<string_lit>.chars().any(|i| i == c)`"
3316+ }
3317+
32893318pub struct Methods {
32903319 avoid_breaking_exported_api : bool ,
32913320 msrv : Msrv ,
@@ -3416,7 +3445,8 @@ impl_lint_pass!(Methods => [
34163445 CLEAR_WITH_DRAIN ,
34173446 MANUAL_NEXT_BACK ,
34183447 UNNECESSARY_LITERAL_UNWRAP ,
3419- DRAIN_COLLECT
3448+ DRAIN_COLLECT ,
3449+ STRING_LIT_CHARS_ANY ,
34203450] ) ;
34213451
34223452/// Extracts a method call name, args, and `Span` of the method name.
@@ -3787,6 +3817,13 @@ impl Methods {
37873817 }
37883818 }
37893819 } ,
3820+ ( "any" , [ arg] ) if let ExprKind :: Closure ( arg) = arg. kind
3821+ && let body = cx. tcx . hir ( ) . body ( arg. body )
3822+ && let [ param] = body. params
3823+ && let Some ( ( "chars" , recv, _, _, _) ) = method_call ( recv) =>
3824+ {
3825+ string_lit_chars_any:: check ( cx, expr, recv, param, peel_blocks ( body. value ) , & self . msrv ) ;
3826+ }
37903827 ( "nth" , [ n_arg] ) => match method_call ( recv) {
37913828 Some ( ( "bytes" , recv2, [ ] , _, _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
37923829 Some ( ( "cloned" , recv2, [ ] , _, _) ) => iter_overeager_cloned:: check ( cx, expr, recv, recv2, false , false ) ,
0 commit comments