@@ -86,6 +86,7 @@ mod skip_while_next;
8686mod stable_sort_primitive;
8787mod str_splitn;
8888mod string_extend_chars;
89+ mod string_lit_chars_any;
8990mod suspicious_command_arg_space;
9091mod suspicious_map;
9192mod suspicious_splitn;
@@ -115,7 +116,7 @@ use clippy_utils::consts::{constant, Constant};
115116use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
116117use clippy_utils:: msrvs:: { self , Msrv } ;
117118use 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} ;
119120use if_chain:: if_chain;
120121use rustc_hir as hir;
121122use rustc_hir:: { Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
@@ -3379,6 +3380,34 @@ declare_clippy_lint! {
33793380 "calling `Stdin::read_line`, then trying to parse it without first trimming"
33803381}
33813382
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+
33823411declare_clippy_lint ! {
33833412 /// ### What it does
33843413 /// Checks for usage of `.map(|_| format!(..)).collect::<String>()`.
@@ -3549,6 +3578,7 @@ impl_lint_pass!(Methods => [
35493578 DRAIN_COLLECT ,
35503579 MANUAL_TRY_FOLD ,
35513580 FORMAT_COLLECT ,
3581+ STRING_LIT_CHARS_ANY ,
35523582] ) ;
35533583
35543584/// Extracts a method call name, args, and `Span` of the method name.
@@ -3923,6 +3953,13 @@ impl Methods {
39233953 }
39243954 }
39253955 } ,
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+ }
39263963 ( "nth" , [ n_arg] ) => match method_call ( recv) {
39273964 Some ( ( "bytes" , recv2, [ ] , _, _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
39283965 Some ( ( "cloned" , recv2, [ ] , _, _) ) => iter_overeager_cloned:: check ( cx, expr, recv, recv2, false , false ) ,
0 commit comments