@@ -4,8 +4,9 @@ use oxc_ast::{
44} ;
55use oxc_diagnostics:: OxcDiagnostic ;
66use oxc_macros:: declare_oxc_lint;
7- use oxc_regular_expression:: ast:: {
8- Alternative , Character , CharacterClassContents , CharacterKind , Disjunction , Pattern , Term ,
7+ use oxc_regular_expression:: {
8+ ast:: { Character , CharacterKind } ,
9+ visit:: Visit ,
910} ;
1011use oxc_span:: Span ;
1112
@@ -97,80 +98,32 @@ impl Rule for NoHexEscape {
9798 return ;
9899 } ;
99100
100- visit_terms ( pattern, & mut |term| match term {
101- Term :: Character ( ch) => {
102- check_character ( ch, ctx) ;
103- }
104- Term :: CharacterClass ( class) => {
105- for term in & class. body {
106- match term {
107- CharacterClassContents :: Character ( ch) => {
108- check_character ( ch, ctx) ;
109- }
110- CharacterClassContents :: CharacterClassRange ( range) => {
111- check_character ( & range. min , ctx) ;
112- check_character ( & range. max , ctx) ;
113- }
114- _ => ( ) ,
115- }
116- }
117- }
118- _ => ( ) ,
119- } ) ;
101+ let mut finder = HexEscapeFinder { hex_escapes : vec ! [ ] } ;
102+ finder. visit_pattern ( pattern) ;
103+
104+ for span in finder. hex_escapes {
105+ let unicode_escape =
106+ format ! ( r"\u00{}" , & span. source_text( ctx. source_text( ) ) [ 2 ..] ) ;
107+
108+ ctx. diagnostic_with_fix ( no_hex_escape_diagnostic ( span) , |fixer| {
109+ fixer. replace ( span, unicode_escape)
110+ } ) ;
111+ }
120112 }
121113 _ => { }
122114 }
123115 }
124116}
125117
126- fn check_character ( ch : & Character , ctx : & LintContext ) {
127- if ch. kind == CharacterKind :: HexadecimalEscape {
128- let unicode_escape = format ! ( r"\u00{}" , & ch. span. source_text( ctx. source_text( ) ) [ 2 ..] ) ;
129- ctx. diagnostic_with_fix ( no_hex_escape_diagnostic ( ch. span ) , |fixer| {
130- fixer. replace ( ch. span , unicode_escape)
131- } ) ;
132- }
133- }
134-
135- // TODO: Replace with proper regex AST visitor when available
136- /// Calls the given closure on every [`Term`] in the [`Pattern`].
137- fn visit_terms < ' a , F : FnMut ( & ' a Term < ' a > ) > ( pattern : & ' a Pattern , f : & mut F ) {
138- visit_terms_disjunction ( & pattern. body , f) ;
118+ struct HexEscapeFinder {
119+ hex_escapes : Vec < Span > ,
139120}
140121
141- /// Calls the given closure on every [`Term`] in the [`Disjunction`].
142- fn visit_terms_disjunction < ' a , F : FnMut ( & ' a Term < ' a > ) > ( disjunction : & ' a Disjunction , f : & mut F ) {
143- for alternative in & disjunction. body {
144- visit_terms_alternative ( alternative, f) ;
145- }
146- }
147-
148- /// Calls the given closure on every [`Term`] in the [`Alternative`].
149- fn visit_terms_alternative < ' a , F : FnMut ( & ' a Term < ' a > ) > ( alternative : & ' a Alternative , f : & mut F ) {
150- for term in & alternative. body {
151- visit_term ( term, f) ;
152- }
153- }
154-
155- fn visit_term < ' a , F : FnMut ( & ' a Term < ' a > ) > ( term : & ' a Term < ' a > , f : & mut F ) {
156- match term {
157- Term :: LookAroundAssertion ( lookaround) => {
158- f ( term) ;
159- visit_terms_disjunction ( & lookaround. body , f) ;
160- }
161- Term :: Quantifier ( quant) => {
162- f ( term) ;
163- visit_term ( & quant. body , f) ;
164- }
165- Term :: CapturingGroup ( group) => {
166- f ( term) ;
167- visit_terms_disjunction ( & group. body , f) ;
168- }
169- Term :: IgnoreGroup ( group) => {
170- f ( term) ;
171- visit_terms_disjunction ( & group. body , f) ;
122+ impl < ' a > Visit < ' a > for HexEscapeFinder {
123+ fn visit_character ( & mut self , ch : & Character ) {
124+ if ch. kind == CharacterKind :: HexadecimalEscape {
125+ self . hex_escapes . push ( ch. span ) ;
172126 }
173- _ => f ( term) ,
174127 }
175128}
176129
0 commit comments