1+ use std:: fmt:: Display ;
2+
13use clippy_utils:: consts:: { constant, Constant } ;
24use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help} ;
5+ use clippy_utils:: source:: snippet_opt;
36use clippy_utils:: { match_def_path, paths} ;
47use if_chain:: if_chain;
58use rustc_ast:: ast:: { LitKind , StrStyle } ;
@@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex {
7780 }
7881}
7982
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+ }
87122}
88123
89124fn 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) {
155190 span_lint_and_help ( cx, TRIVIAL_REGEX , expr. span , "trivial regex" , None , repl) ;
156191 }
157192 } ,
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) ,
177194 }
178195 }
179196 } 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) {
183200 span_lint_and_help ( cx, TRIVIAL_REGEX , expr. span , "trivial regex" , None , repl) ;
184201 }
185202 } ,
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 ( ) ) ,
205204 }
206205 }
207206}
0 commit comments