@@ -85,6 +85,7 @@ mod skip_while_next;
8585mod stable_sort_primitive;
8686mod str_splitn;
8787mod string_extend_chars;
88+ mod string_lit_chars_any;
8889mod suspicious_command_arg_space;
8990mod suspicious_map;
9091mod suspicious_splitn;
@@ -114,7 +115,7 @@ use clippy_utils::consts::{constant, Constant};
114115use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
115116use clippy_utils:: msrvs:: { self , Msrv } ;
116117use clippy_utils:: ty:: { contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item} ;
117- use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, return_ty} ;
118+ use clippy_utils:: { contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks , return_ty} ;
118119use if_chain:: if_chain;
119120use rustc_hir as hir;
120121use rustc_hir:: { Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
@@ -3378,6 +3379,34 @@ declare_clippy_lint! {
33783379 "calling `Stdin::read_line`, then trying to parse it without first trimming"
33793380}
33803381
3382+ declare_clippy_lint ! {
3383+ /// ### What it does
3384+ /// Checks for `<string_lit>.chars().any(|i| i == c)`.
3385+ ///
3386+ /// ### Why is this bad?
3387+ /// It's significantly slower than using a pattern instead, like
3388+ /// `matches!(c, '\\' | '.' | '+')`.
3389+ ///
3390+ /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
3391+ /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
3392+ /// for situations where that additional performance is absolutely necessary.
3393+ ///
3394+ /// ### Example
3395+ /// ```rust
3396+ /// # let c = 'c';
3397+ /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
3398+ /// ```
3399+ /// Use instead:
3400+ /// ```rust
3401+ /// # let c = 'c';
3402+ /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
3403+ /// ```
3404+ #[ clippy:: version = "1.72.0" ]
3405+ pub STRING_LIT_CHARS_ANY ,
3406+ restriction,
3407+ "checks for `<string_lit>.chars().any(|i| i == c)`"
3408+ }
3409+
33813410pub struct Methods {
33823411 avoid_breaking_exported_api : bool ,
33833412 msrv : Msrv ,
@@ -3512,6 +3541,7 @@ impl_lint_pass!(Methods => [
35123541 UNNECESSARY_LITERAL_UNWRAP ,
35133542 DRAIN_COLLECT ,
35143543 MANUAL_TRY_FOLD ,
3544+ STRING_LIT_CHARS_ANY ,
35153545] ) ;
35163546
35173547/// Extracts a method call name, args, and `Span` of the method name.
@@ -3885,6 +3915,13 @@ impl Methods {
38853915 }
38863916 }
38873917 } ,
3918+ ( "any" , [ arg] ) if let ExprKind :: Closure ( arg) = arg. kind
3919+ && let body = cx. tcx . hir ( ) . body ( arg. body )
3920+ && let [ param] = body. params
3921+ && let Some ( ( "chars" , recv, _, _, _) ) = method_call ( recv) =>
3922+ {
3923+ string_lit_chars_any:: check ( cx, expr, recv, param, peel_blocks ( body. value ) , & self . msrv ) ;
3924+ }
38883925 ( "nth" , [ n_arg] ) => match method_call ( recv) {
38893926 Some ( ( "bytes" , recv2, [ ] , _, _) ) => bytes_nth:: check ( cx, expr, recv2, n_arg) ,
38903927 Some ( ( "cloned" , recv2, [ ] , _, _) ) => iter_overeager_cloned:: check ( cx, expr, recv, recv2, false , false ) ,
0 commit comments