1use regex::Regex;
2
3use crate::nodes::*;
4use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
5use crate::rules::{
6 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
7};
8
9#[derive(Debug, Default)]
10pub(crate) struct RemoveCommentProcessor {}
11
12impl NodeProcessor for RemoveCommentProcessor {
13 fn process_block(&mut self, block: &mut Block) {
14 block.clear_comments();
15 }
16
17 fn process_function_call(&mut self, call: &mut FunctionCall) {
18 call.clear_comments();
19 call.mutate_arguments().clear_comments();
20 }
21
22 fn process_assign_statement(&mut self, assign: &mut AssignStatement) {
23 assign.clear_comments();
24 }
25
26 fn process_compound_assign_statement(&mut self, assign: &mut CompoundAssignStatement) {
27 assign.clear_comments();
28 }
29
30 fn process_do_statement(&mut self, statement: &mut DoStatement) {
31 statement.clear_comments();
32 }
33
34 fn process_function_statement(&mut self, function: &mut FunctionStatement) {
35 function.clear_comments();
36 }
37
38 fn process_generic_for_statement(&mut self, generic_for: &mut GenericForStatement) {
39 generic_for.clear_comments();
40 }
41
42 fn process_if_statement(&mut self, if_statement: &mut IfStatement) {
43 if_statement.clear_comments();
44 }
45
46 fn process_last_statement(&mut self, statement: &mut LastStatement) {
47 match statement {
48 LastStatement::Break(token) | LastStatement::Continue(token) => {
49 if let Some(token) = token {
50 token.clear_comments();
51 }
52 }
53 LastStatement::Return(statement) => statement.clear_comments(),
54 }
55 }
56
57 fn process_local_assign_statement(&mut self, assign: &mut LocalAssignStatement) {
58 assign.clear_comments();
59 }
60
61 fn process_local_function_statement(&mut self, function: &mut LocalFunctionStatement) {
62 function.clear_comments();
63 }
64
65 fn process_numeric_for_statement(&mut self, numeric_for: &mut NumericForStatement) {
66 numeric_for.clear_comments();
67 }
68
69 fn process_repeat_statement(&mut self, repeat: &mut RepeatStatement) {
70 repeat.clear_comments();
71 }
72
73 fn process_while_statement(&mut self, statement: &mut WhileStatement) {
74 statement.clear_comments();
75 }
76
77 fn process_type_declaration(&mut self, type_declaration: &mut TypeDeclarationStatement) {
78 type_declaration.clear_comments();
79 }
80
81 fn process_expression(&mut self, expression: &mut Expression) {
82 match expression {
83 Expression::False(token)
84 | Expression::Nil(token)
85 | Expression::True(token)
86 | Expression::VariableArguments(token) => {
87 if let Some(token) = token {
88 token.clear_comments()
89 }
90 }
91 Expression::Binary(_)
92 | Expression::Call(_)
93 | Expression::Field(_)
94 | Expression::Function(_)
95 | Expression::Identifier(_)
96 | Expression::If(_)
97 | Expression::Index(_)
98 | Expression::Number(_)
99 | Expression::Parenthese(_)
100 | Expression::String(_)
101 | Expression::InterpolatedString(_)
102 | Expression::Table(_)
103 | Expression::Unary(_)
104 | Expression::TypeCast(_) => {}
105 }
106 }
107
108 fn process_binary_expression(&mut self, binary: &mut BinaryExpression) {
109 binary.clear_comments();
110 }
111
112 fn process_field_expression(&mut self, field: &mut FieldExpression) {
113 field.clear_comments();
114 }
115
116 fn process_function_expression(&mut self, function: &mut FunctionExpression) {
117 function.clear_comments();
118 }
119
120 fn process_if_expression(&mut self, if_expression: &mut IfExpression) {
121 if_expression.clear_comments();
122 }
123
124 fn process_variable_expression(&mut self, identifier: &mut Identifier) {
125 identifier.clear_comments();
126 }
127
128 fn process_index_expression(&mut self, index: &mut IndexExpression) {
129 index.clear_comments();
130 }
131
132 fn process_number_expression(&mut self, number: &mut NumberExpression) {
133 number.clear_comments();
134 }
135
136 fn process_parenthese_expression(&mut self, expression: &mut ParentheseExpression) {
137 expression.clear_comments();
138 }
139
140 fn process_string_expression(&mut self, string: &mut StringExpression) {
141 string.clear_comments();
142 }
143
144 fn process_table_expression(&mut self, table: &mut TableExpression) {
145 table.clear_comments();
146 }
147
148 fn process_unary_expression(&mut self, unary: &mut UnaryExpression) {
149 unary.clear_comments();
150 }
151
152 fn process_interpolated_string_expression(
153 &mut self,
154 string: &mut InterpolatedStringExpression,
155 ) {
156 string.clear_comments();
157 }
158
159 fn process_type_cast_expression(&mut self, type_cast: &mut TypeCastExpression) {
160 type_cast.clear_comments();
161 }
162
163 fn process_prefix_expression(&mut self, _: &mut Prefix) {}
164
165 fn process_type(&mut self, r#type: &mut Type) {
166 match r#type {
167 Type::True(token) | Type::False(token) | Type::Nil(token) => {
168 if let Some(token) = token {
169 token.clear_comments();
170 }
171 }
172 _ => {}
173 }
174 }
175
176 fn process_type_name(&mut self, type_name: &mut TypeName) {
177 type_name.clear_comments();
178 }
179
180 fn process_type_field(&mut self, type_field: &mut TypeField) {
181 type_field.clear_comments();
182 }
183
184 fn process_string_type(&mut self, string_type: &mut StringType) {
185 string_type.clear_comments();
186 }
187
188 fn process_array_type(&mut self, array: &mut ArrayType) {
189 array.clear_comments();
190 }
191
192 fn process_table_type(&mut self, table: &mut TableType) {
193 table.clear_comments();
194 }
195
196 fn process_expression_type(&mut self, expression_type: &mut ExpressionType) {
197 expression_type.clear_comments();
198 }
199
200 fn process_parenthese_type(&mut self, parenthese_type: &mut ParentheseType) {
201 parenthese_type.clear_comments();
202 }
203
204 fn process_function_type(&mut self, function_type: &mut FunctionType) {
205 function_type.clear_comments();
206 }
207
208 fn process_optional_type(&mut self, optional: &mut OptionalType) {
209 optional.clear_comments();
210 }
211
212 fn process_intersection_type(&mut self, intersection: &mut IntersectionType) {
213 intersection.clear_comments();
214 }
215
216 fn process_union_type(&mut self, union: &mut UnionType) {
217 union.clear_comments();
218 }
219
220 fn process_type_pack(&mut self, type_pack: &mut TypePack) {
221 type_pack.clear_comments();
222 }
223
224 fn process_generic_type_pack(&mut self, generic_type_pack: &mut GenericTypePack) {
225 generic_type_pack.clear_comments();
226 }
227
228 fn process_variadic_type_pack(&mut self, variadic_type_pack: &mut VariadicTypePack) {
229 variadic_type_pack.clear_comments();
230 }
231}
232
233#[derive(Debug)]
234pub(crate) struct FilterCommentProcessor<'a> {
235 original_code: &'a str,
236 except: &'a Vec<Regex>,
237}
238
239impl<'a> FilterCommentProcessor<'a> {
240 pub(crate) fn new(original_code: &'a str, except: &'a Vec<Regex>) -> Self {
241 Self {
242 original_code,
243 except,
244 }
245 }
246
247 fn ignore_trivia(&self, trivia: &Trivia) -> bool {
248 let content = trivia.read(self.original_code);
249 self.except.iter().any(|pattern| pattern.is_match(content))
250 }
251}
252
253impl NodeProcessor for FilterCommentProcessor<'_> {
254 fn process_block(&mut self, block: &mut Block) {
255 block.filter_comments(|trivia| self.ignore_trivia(trivia));
256 }
257
258 fn process_function_call(&mut self, call: &mut FunctionCall) {
259 call.filter_comments(|trivia| self.ignore_trivia(trivia));
260 call.mutate_arguments()
261 .filter_comments(|trivia| self.ignore_trivia(trivia));
262 }
263
264 fn process_assign_statement(&mut self, assign: &mut AssignStatement) {
265 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
266 }
267
268 fn process_compound_assign_statement(&mut self, assign: &mut CompoundAssignStatement) {
269 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
270 }
271
272 fn process_do_statement(&mut self, statement: &mut DoStatement) {
273 statement.filter_comments(|trivia| self.ignore_trivia(trivia));
274 }
275
276 fn process_function_statement(&mut self, function: &mut FunctionStatement) {
277 function.filter_comments(|trivia| self.ignore_trivia(trivia));
278 }
279
280 fn process_generic_for_statement(&mut self, generic_for: &mut GenericForStatement) {
281 generic_for.filter_comments(|trivia| self.ignore_trivia(trivia));
282 }
283
284 fn process_if_statement(&mut self, if_statement: &mut IfStatement) {
285 if_statement.filter_comments(|trivia| self.ignore_trivia(trivia));
286 }
287
288 fn process_last_statement(&mut self, statement: &mut LastStatement) {
289 match statement {
290 LastStatement::Break(token) | LastStatement::Continue(token) => {
291 if let Some(token) = token {
292 token.filter_comments(|trivia| self.ignore_trivia(trivia));
293 }
294 }
295 LastStatement::Return(statement) => {
296 statement.filter_comments(|trivia| self.ignore_trivia(trivia))
297 }
298 }
299 }
300
301 fn process_local_assign_statement(&mut self, assign: &mut LocalAssignStatement) {
302 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
303 }
304
305 fn process_local_function_statement(&mut self, function: &mut LocalFunctionStatement) {
306 function.filter_comments(|trivia| self.ignore_trivia(trivia));
307 }
308
309 fn process_numeric_for_statement(&mut self, numeric_for: &mut NumericForStatement) {
310 numeric_for.filter_comments(|trivia| self.ignore_trivia(trivia));
311 }
312
313 fn process_repeat_statement(&mut self, repeat: &mut RepeatStatement) {
314 repeat.filter_comments(|trivia| self.ignore_trivia(trivia));
315 }
316
317 fn process_while_statement(&mut self, statement: &mut WhileStatement) {
318 statement.filter_comments(|trivia| self.ignore_trivia(trivia));
319 }
320
321 fn process_type_declaration(&mut self, type_declaration: &mut TypeDeclarationStatement) {
322 type_declaration.filter_comments(|trivia| self.ignore_trivia(trivia));
323 }
324
325 fn process_expression(&mut self, expression: &mut Expression) {
326 match expression {
327 Expression::False(token)
328 | Expression::Nil(token)
329 | Expression::True(token)
330 | Expression::VariableArguments(token) => {
331 if let Some(token) = token {
332 token.filter_comments(|trivia| self.ignore_trivia(trivia))
333 }
334 }
335 Expression::Binary(_)
336 | Expression::Call(_)
337 | Expression::Field(_)
338 | Expression::Function(_)
339 | Expression::Identifier(_)
340 | Expression::If(_)
341 | Expression::Index(_)
342 | Expression::Number(_)
343 | Expression::Parenthese(_)
344 | Expression::String(_)
345 | Expression::InterpolatedString(_)
346 | Expression::Table(_)
347 | Expression::Unary(_)
348 | Expression::TypeCast(_) => {}
349 }
350 }
351
352 fn process_binary_expression(&mut self, binary: &mut BinaryExpression) {
353 binary.filter_comments(|trivia| self.ignore_trivia(trivia));
354 }
355
356 fn process_field_expression(&mut self, field: &mut FieldExpression) {
357 field.filter_comments(|trivia| self.ignore_trivia(trivia));
358 }
359
360 fn process_function_expression(&mut self, function: &mut FunctionExpression) {
361 function.filter_comments(|trivia| self.ignore_trivia(trivia));
362 }
363
364 fn process_if_expression(&mut self, if_expression: &mut IfExpression) {
365 if_expression.filter_comments(|trivia| self.ignore_trivia(trivia));
366 }
367
368 fn process_variable_expression(&mut self, identifier: &mut Identifier) {
369 identifier.filter_comments(|trivia| self.ignore_trivia(trivia));
370 }
371
372 fn process_index_expression(&mut self, index: &mut IndexExpression) {
373 index.filter_comments(|trivia| self.ignore_trivia(trivia));
374 }
375
376 fn process_number_expression(&mut self, number: &mut NumberExpression) {
377 number.filter_comments(|trivia| self.ignore_trivia(trivia));
378 }
379
380 fn process_parenthese_expression(&mut self, expression: &mut ParentheseExpression) {
381 expression.filter_comments(|trivia| self.ignore_trivia(trivia));
382 }
383
384 fn process_string_expression(&mut self, string: &mut StringExpression) {
385 string.filter_comments(|trivia| self.ignore_trivia(trivia));
386 }
387
388 fn process_table_expression(&mut self, table: &mut TableExpression) {
389 table.filter_comments(|trivia| self.ignore_trivia(trivia));
390 }
391
392 fn process_unary_expression(&mut self, unary: &mut UnaryExpression) {
393 unary.filter_comments(|trivia| self.ignore_trivia(trivia));
394 }
395
396 fn process_interpolated_string_expression(
397 &mut self,
398 string: &mut InterpolatedStringExpression,
399 ) {
400 string.filter_comments(|trivia| self.ignore_trivia(trivia));
401 }
402
403 fn process_type_cast_expression(&mut self, type_cast: &mut TypeCastExpression) {
404 type_cast.filter_comments(|trivia| self.ignore_trivia(trivia));
405 }
406
407 fn process_prefix_expression(&mut self, _: &mut Prefix) {}
408
409 fn process_type(&mut self, r#type: &mut Type) {
410 match r#type {
411 Type::True(token) | Type::False(token) | Type::Nil(token) => {
412 if let Some(token) = token {
413 token.filter_comments(|trivia| self.ignore_trivia(trivia));
414 }
415 }
416 _ => {}
417 }
418 }
419
420 fn process_type_name(&mut self, type_name: &mut TypeName) {
421 type_name.filter_comments(|trivia| self.ignore_trivia(trivia));
422 }
423
424 fn process_type_field(&mut self, type_field: &mut TypeField) {
425 type_field.filter_comments(|trivia| self.ignore_trivia(trivia));
426 }
427
428 fn process_string_type(&mut self, string_type: &mut StringType) {
429 string_type.filter_comments(|trivia| self.ignore_trivia(trivia));
430 }
431
432 fn process_array_type(&mut self, array: &mut ArrayType) {
433 array.filter_comments(|trivia| self.ignore_trivia(trivia));
434 }
435
436 fn process_table_type(&mut self, table: &mut TableType) {
437 table.filter_comments(|trivia| self.ignore_trivia(trivia));
438 }
439
440 fn process_expression_type(&mut self, expression_type: &mut ExpressionType) {
441 expression_type.filter_comments(|trivia| self.ignore_trivia(trivia));
442 }
443
444 fn process_parenthese_type(&mut self, parenthese_type: &mut ParentheseType) {
445 parenthese_type.filter_comments(|trivia| self.ignore_trivia(trivia));
446 }
447
448 fn process_function_type(&mut self, function_type: &mut FunctionType) {
449 function_type.filter_comments(|trivia| self.ignore_trivia(trivia));
450 }
451
452 fn process_optional_type(&mut self, optional: &mut OptionalType) {
453 optional.filter_comments(|trivia| self.ignore_trivia(trivia));
454 }
455
456 fn process_intersection_type(&mut self, intersection: &mut IntersectionType) {
457 intersection.filter_comments(|trivia| self.ignore_trivia(trivia));
458 }
459
460 fn process_union_type(&mut self, union: &mut UnionType) {
461 union.filter_comments(|trivia| self.ignore_trivia(trivia));
462 }
463
464 fn process_type_pack(&mut self, type_pack: &mut TypePack) {
465 type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
466 }
467
468 fn process_generic_type_pack(&mut self, generic_type_pack: &mut GenericTypePack) {
469 generic_type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
470 }
471
472 fn process_variadic_type_pack(&mut self, variadic_type_pack: &mut VariadicTypePack) {
473 variadic_type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
474 }
475}
476
477pub const REMOVE_COMMENTS_RULE_NAME: &str = "remove_comments";
478
479#[derive(Debug, Default)]
481pub struct RemoveComments {
482 except: Vec<Regex>,
483}
484
485impl FlawlessRule for RemoveComments {
486 fn flawless_process(&self, block: &mut Block, context: &Context) {
487 if self.except.is_empty() {
488 let mut processor = RemoveCommentProcessor::default();
489 DefaultVisitor::visit_block(block, &mut processor);
490 } else {
491 let mut processor = FilterCommentProcessor::new(context.original_code(), &self.except);
492 DefaultVisitor::visit_block(block, &mut processor);
493 }
494 }
495}
496
497impl RuleConfiguration for RemoveComments {
498 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
499 for (key, value) in properties {
500 match key.as_str() {
501 "except" => {
502 self.except = value.expect_regex_list(&key)?;
503 }
504 _ => return Err(RuleConfigurationError::UnexpectedProperty(key)),
505 }
506 }
507
508 Ok(())
509 }
510
511 fn get_name(&self) -> &'static str {
512 REMOVE_COMMENTS_RULE_NAME
513 }
514
515 fn serialize_to_properties(&self) -> RuleProperties {
516 RuleProperties::new()
517 }
518}
519
520#[cfg(test)]
521mod test {
522 use super::*;
523 use crate::{
524 generator::{LuaGenerator, TokenBasedLuaGenerator},
525 rules::{ContextBuilder, Rule},
526 Parser, Resources,
527 };
528
529 use insta::assert_json_snapshot;
530
531 fn new_rule() -> RemoveComments {
532 RemoveComments::default()
533 }
534
535 #[test]
536 fn serialize_default_rule() {
537 let rule: Box<dyn Rule> = Box::new(new_rule());
538
539 assert_json_snapshot!("default_remove_comments", rule);
540 }
541
542 #[test]
543 fn configure_with_extra_field_error() {
544 let result = json5::from_str::<Box<dyn Rule>>(
545 r#"{
546 rule: 'remove_comments',
547 prop: "something",
548 }"#,
549 );
550 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
551 }
552
553 #[test]
554 fn configure_with_invalid_regex_error() {
555 let result = json5::from_str::<Box<dyn Rule>>(
556 r#"{
557 rule: 'remove_comments',
558 except: ["^[0-9"],
559 }"#,
560 );
561
562 insta::assert_snapshot!(
563 "remove_comments_configure_with_invalid_regex_error",
564 result.unwrap_err().to_string()
565 );
566 }
567
568 #[test]
569 fn remove_comments_in_code() {
570 let code = include_str!("../../tests/test_cases/spaces_and_comments.lua");
571
572 let parser = Parser::default().preserve_tokens();
573
574 let mut block = parser.parse(code).expect("unable to parse code");
575
576 RemoveComments::default().flawless_process(
577 &mut block,
578 &ContextBuilder::new(".", &Resources::from_memory(), code).build(),
579 );
580
581 let mut generator = TokenBasedLuaGenerator::new(code);
582
583 generator.write_block(&block);
584
585 let code_output = &generator.into_string();
586
587 insta::assert_snapshot!("remove_comments_in_code", code_output);
588 }
589}