@@ -3,12 +3,12 @@ use std::fmt::Display;
3
3
use clippy_utils:: consts:: { constant, Constant } ;
4
4
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
5
5
use clippy_utils:: source:: snippet_opt;
6
- use clippy_utils:: { match_def_path, paths} ;
7
- use if_chain:: if_chain;
6
+ use clippy_utils:: { def_path_def_ids, path_def_id, paths} ;
8
7
use rustc_ast:: ast:: { LitKind , StrStyle } ;
8
+ use rustc_hir:: def_id:: DefIdMap ;
9
9
use rustc_hir:: { BorrowKind , Expr , ExprKind } ;
10
10
use rustc_lint:: { LateContext , LateLintPass } ;
11
- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
11
+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
12
12
use rustc_span:: source_map:: { BytePos , Span } ;
13
13
14
14
declare_clippy_lint ! {
@@ -55,26 +55,52 @@ declare_clippy_lint! {
55
55
"trivial regular expressions"
56
56
}
57
57
58
- declare_lint_pass ! ( Regex => [ INVALID_REGEX , TRIVIAL_REGEX ] ) ;
58
+ #[ derive( Copy , Clone ) ]
59
+ enum RegexKind {
60
+ Unicode ,
61
+ UnicodeSet ,
62
+ Bytes ,
63
+ BytesSet ,
64
+ }
65
+
66
+ #[ derive( Default ) ]
67
+ pub struct Regex {
68
+ definitions : DefIdMap < RegexKind > ,
69
+ }
70
+
71
+ impl_lint_pass ! ( Regex => [ INVALID_REGEX , TRIVIAL_REGEX ] ) ;
59
72
60
73
impl < ' tcx > LateLintPass < ' tcx > for Regex {
74
+ fn check_crate ( & mut self , cx : & LateContext < ' tcx > ) {
75
+ // We don't use `match_def_path` here because that relies on matching the exact path, which changed
76
+ // between regex 1.8 and 1.9
77
+ //
78
+ // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform
79
+ // the operation once and store the results
80
+ let mut resolve = |path, kind| {
81
+ for id in def_path_def_ids ( cx, path) {
82
+ self . definitions . insert ( id, kind) ;
83
+ }
84
+ } ;
85
+
86
+ resolve ( & paths:: REGEX_NEW , RegexKind :: Unicode ) ;
87
+ resolve ( & paths:: REGEX_BUILDER_NEW , RegexKind :: Unicode ) ;
88
+ resolve ( & paths:: REGEX_SET_NEW , RegexKind :: UnicodeSet ) ;
89
+ resolve ( & paths:: REGEX_BYTES_NEW , RegexKind :: Bytes ) ;
90
+ resolve ( & paths:: REGEX_BYTES_BUILDER_NEW , RegexKind :: Bytes ) ;
91
+ resolve ( & paths:: REGEX_BYTES_SET_NEW , RegexKind :: BytesSet ) ;
92
+ }
93
+
61
94
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
62
- if_chain ! {
63
- if let ExprKind :: Call ( fun, [ arg] ) = expr. kind;
64
- if let ExprKind :: Path ( ref qpath) = fun. kind;
65
- if let Some ( def_id) = cx. qpath_res( qpath, fun. hir_id) . opt_def_id( ) ;
66
- then {
67
- if match_def_path( cx, def_id, & paths:: REGEX_NEW ) ||
68
- match_def_path( cx, def_id, & paths:: REGEX_BUILDER_NEW ) {
69
- check_regex( cx, arg, true ) ;
70
- } else if match_def_path( cx, def_id, & paths:: REGEX_BYTES_NEW ) ||
71
- match_def_path( cx, def_id, & paths:: REGEX_BYTES_BUILDER_NEW ) {
72
- check_regex( cx, arg, false ) ;
73
- } else if match_def_path( cx, def_id, & paths:: REGEX_SET_NEW ) {
74
- check_set( cx, arg, true ) ;
75
- } else if match_def_path( cx, def_id, & paths:: REGEX_BYTES_SET_NEW ) {
76
- check_set( cx, arg, false ) ;
77
- }
95
+ if let ExprKind :: Call ( fun, [ arg] ) = expr. kind
96
+ && let Some ( def_id) = path_def_id ( cx, fun)
97
+ && let Some ( regex_kind) = self . definitions . get ( & def_id)
98
+ {
99
+ match regex_kind {
100
+ RegexKind :: Unicode => check_regex ( cx, arg, true ) ,
101
+ RegexKind :: UnicodeSet => check_set ( cx, arg, true ) ,
102
+ RegexKind :: Bytes => check_regex ( cx, arg, false ) ,
103
+ RegexKind :: BytesSet => check_set ( cx, arg, false ) ,
78
104
}
79
105
}
80
106
}
0 commit comments