1
+ use std:: fmt:: Display ;
2
+
1
3
use clippy_utils:: consts:: { constant, Constant } ;
2
4
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
5
+ use clippy_utils:: source:: snippet_opt;
3
6
use clippy_utils:: { match_def_path, paths} ;
4
7
use if_chain:: if_chain;
5
8
use rustc_ast:: ast:: { LitKind , StrStyle } ;
@@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex {
77
80
}
78
81
}
79
82
80
- #[ must_use]
81
- fn str_span ( base : Span , c : regex_syntax:: ast:: Span , offset : u8 ) -> Span {
82
- let offset = u32:: from ( offset) ;
83
- let end = base. lo ( ) + BytePos ( u32:: try_from ( c. end . offset ) . expect ( "offset too large" ) + offset) ;
84
- let start = base. lo ( ) + BytePos ( u32:: try_from ( c. start . offset ) . expect ( "offset too large" ) + offset) ;
85
- assert ! ( start <= end) ;
86
- Span :: new ( start, end, base. ctxt ( ) , base. parent ( ) )
83
+ fn lint_syntax_error ( cx : & LateContext < ' _ > , error : & regex_syntax:: Error , unescaped : & str , base : Span , offset : u8 ) {
84
+ let parts: Option < ( _ , _ , & dyn Display ) > = match & error {
85
+ regex_syntax:: Error :: Parse ( e) => Some ( ( e. span ( ) , e. auxiliary_span ( ) , e. kind ( ) ) ) ,
86
+ regex_syntax:: Error :: Translate ( e) => Some ( ( e. span ( ) , None , e. kind ( ) ) ) ,
87
+ _ => None ,
88
+ } ;
89
+
90
+ let convert_span = |regex_span : & regex_syntax:: ast:: Span | {
91
+ let offset = u32:: from ( offset) ;
92
+ let start = base. lo ( ) + BytePos ( u32:: try_from ( regex_span. start . offset ) . expect ( "offset too large" ) + offset) ;
93
+ let end = base. lo ( ) + BytePos ( u32:: try_from ( regex_span. end . offset ) . expect ( "offset too large" ) + offset) ;
94
+
95
+ Span :: new ( start, end, base. ctxt ( ) , base. parent ( ) )
96
+ } ;
97
+
98
+ if let Some ( ( primary, auxiliary, kind) ) = parts
99
+ && let Some ( literal_snippet) = snippet_opt ( cx, base)
100
+ && let Some ( inner) = literal_snippet. get ( offset as usize ..)
101
+ // Only convert to native rustc spans if the parsed regex matches the
102
+ // source snippet exactly, to ensure the span offsets are correct
103
+ && inner. get ( ..unescaped. len ( ) ) == Some ( unescaped)
104
+ {
105
+ let spans = if let Some ( auxiliary) = auxiliary {
106
+ vec ! [ convert_span( primary) , convert_span( auxiliary) ]
107
+ } else {
108
+ vec ! [ convert_span( primary) ]
109
+ } ;
110
+
111
+ span_lint ( cx, INVALID_REGEX , spans, & format ! ( "regex syntax error: {kind}" ) ) ;
112
+ } else {
113
+ span_lint_and_help (
114
+ cx,
115
+ INVALID_REGEX ,
116
+ base,
117
+ & error. to_string ( ) ,
118
+ None ,
119
+ "consider using a raw string literal: `r\" ..\" `" ,
120
+ ) ;
121
+ }
87
122
}
88
123
89
124
fn const_str < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> Option < String > {
@@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
155
190
span_lint_and_help ( cx, TRIVIAL_REGEX , expr. span , "trivial regex" , None , repl) ;
156
191
}
157
192
} ,
158
- Err ( regex_syntax:: Error :: Parse ( e) ) => {
159
- span_lint (
160
- cx,
161
- INVALID_REGEX ,
162
- str_span ( expr. span , * e. span ( ) , offset) ,
163
- & format ! ( "regex syntax error: {}" , e. kind( ) ) ,
164
- ) ;
165
- } ,
166
- Err ( regex_syntax:: Error :: Translate ( e) ) => {
167
- span_lint (
168
- cx,
169
- INVALID_REGEX ,
170
- str_span ( expr. span , * e. span ( ) , offset) ,
171
- & format ! ( "regex syntax error: {}" , e. kind( ) ) ,
172
- ) ;
173
- } ,
174
- Err ( e) => {
175
- span_lint ( cx, INVALID_REGEX , expr. span , & format ! ( "regex syntax error: {e}" ) ) ;
176
- } ,
193
+ Err ( e) => lint_syntax_error ( cx, & e, r, expr. span , offset) ,
177
194
}
178
195
}
179
196
} else if let Some ( r) = const_str ( cx, expr) {
@@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
183
200
span_lint_and_help ( cx, TRIVIAL_REGEX , expr. span , "trivial regex" , None , repl) ;
184
201
}
185
202
} ,
186
- Err ( regex_syntax:: Error :: Parse ( e) ) => {
187
- span_lint (
188
- cx,
189
- INVALID_REGEX ,
190
- expr. span ,
191
- & format ! ( "regex syntax error on position {}: {}" , e. span( ) . start. offset, e. kind( ) ) ,
192
- ) ;
193
- } ,
194
- Err ( regex_syntax:: Error :: Translate ( e) ) => {
195
- span_lint (
196
- cx,
197
- INVALID_REGEX ,
198
- expr. span ,
199
- & format ! ( "regex syntax error on position {}: {}" , e. span( ) . start. offset, e. kind( ) ) ,
200
- ) ;
201
- } ,
202
- Err ( e) => {
203
- span_lint ( cx, INVALID_REGEX , expr. span , & format ! ( "regex syntax error: {e}" ) ) ;
204
- } ,
203
+ Err ( e) => span_lint ( cx, INVALID_REGEX , expr. span , & e. to_string ( ) ) ,
205
204
}
206
205
}
207
206
}
0 commit comments