1use regex::Regex;
2
3use crate::nodes::*;
4use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
5use crate::rules::{
6 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleMetadata, 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_type_function(&mut self, function: &mut TypeFunctionStatement) {
82 function.clear_comments();
83 }
84
85 fn process_attributes(&mut self, attributes: &mut Attributes) {
86 attributes.clear_comments();
87 }
88
89 fn process_literal_expression(&mut self, expression: &mut LiteralExpression) {
90 match expression {
91 LiteralExpression::True(token)
92 | LiteralExpression::False(token)
93 | LiteralExpression::Nil(token) => {
94 if let Some(token) = token {
95 token.clear_comments()
96 }
97 }
98 LiteralExpression::Number(_)
99 | LiteralExpression::String(_)
100 | LiteralExpression::Table(_) => {}
101 }
102 }
103
104 fn process_literal_table(&mut self, table: &mut LiteralTable) {
105 table.clear_comments();
106 }
107
108 fn process_expression(&mut self, expression: &mut Expression) {
109 match expression {
110 Expression::False(token)
111 | Expression::Nil(token)
112 | Expression::True(token)
113 | Expression::VariableArguments(token) => {
114 if let Some(token) = token {
115 token.clear_comments()
116 }
117 }
118 Expression::Binary(_)
119 | Expression::Call(_)
120 | Expression::Field(_)
121 | Expression::Function(_)
122 | Expression::Identifier(_)
123 | Expression::If(_)
124 | Expression::Index(_)
125 | Expression::Number(_)
126 | Expression::Parenthese(_)
127 | Expression::String(_)
128 | Expression::InterpolatedString(_)
129 | Expression::Table(_)
130 | Expression::Unary(_)
131 | Expression::TypeCast(_) => {}
132 }
133 }
134
135 fn process_binary_expression(&mut self, binary: &mut BinaryExpression) {
136 binary.clear_comments();
137 }
138
139 fn process_field_expression(&mut self, field: &mut FieldExpression) {
140 field.clear_comments();
141 }
142
143 fn process_function_expression(&mut self, function: &mut FunctionExpression) {
144 function.clear_comments();
145 }
146
147 fn process_if_expression(&mut self, if_expression: &mut IfExpression) {
148 if_expression.clear_comments();
149 }
150
151 fn process_variable_expression(&mut self, identifier: &mut Identifier) {
152 identifier.clear_comments();
153 }
154
155 fn process_index_expression(&mut self, index: &mut IndexExpression) {
156 index.clear_comments();
157 }
158
159 fn process_number_expression(&mut self, number: &mut NumberExpression) {
160 number.clear_comments();
161 }
162
163 fn process_parenthese_expression(&mut self, expression: &mut ParentheseExpression) {
164 expression.clear_comments();
165 }
166
167 fn process_string_expression(&mut self, string: &mut StringExpression) {
168 string.clear_comments();
169 }
170
171 fn process_table_expression(&mut self, table: &mut TableExpression) {
172 table.clear_comments();
173 }
174
175 fn process_unary_expression(&mut self, unary: &mut UnaryExpression) {
176 unary.clear_comments();
177 }
178
179 fn process_interpolated_string_expression(
180 &mut self,
181 string: &mut InterpolatedStringExpression,
182 ) {
183 string.clear_comments();
184 }
185
186 fn process_type_cast_expression(&mut self, type_cast: &mut TypeCastExpression) {
187 type_cast.clear_comments();
188 }
189
190 fn process_prefix_expression(&mut self, _: &mut Prefix) {}
191
192 fn process_type(&mut self, r#type: &mut Type) {
193 match r#type {
194 Type::True(token) | Type::False(token) | Type::Nil(token) => {
195 if let Some(token) = token {
196 token.clear_comments();
197 }
198 }
199 _ => {}
200 }
201 }
202
203 fn process_type_name(&mut self, type_name: &mut TypeName) {
204 type_name.clear_comments();
205 }
206
207 fn process_type_field(&mut self, type_field: &mut TypeField) {
208 type_field.clear_comments();
209 }
210
211 fn process_string_type(&mut self, string_type: &mut StringType) {
212 string_type.clear_comments();
213 }
214
215 fn process_array_type(&mut self, array: &mut ArrayType) {
216 array.clear_comments();
217 }
218
219 fn process_table_type(&mut self, table: &mut TableType) {
220 table.clear_comments();
221 }
222
223 fn process_expression_type(&mut self, expression_type: &mut ExpressionType) {
224 expression_type.clear_comments();
225 }
226
227 fn process_parenthese_type(&mut self, parenthese_type: &mut ParentheseType) {
228 parenthese_type.clear_comments();
229 }
230
231 fn process_function_type(&mut self, function_type: &mut FunctionType) {
232 function_type.clear_comments();
233 }
234
235 fn process_optional_type(&mut self, optional: &mut OptionalType) {
236 optional.clear_comments();
237 }
238
239 fn process_intersection_type(&mut self, intersection: &mut IntersectionType) {
240 intersection.clear_comments();
241 }
242
243 fn process_union_type(&mut self, union: &mut UnionType) {
244 union.clear_comments();
245 }
246
247 fn process_type_pack(&mut self, type_pack: &mut TypePack) {
248 type_pack.clear_comments();
249 }
250
251 fn process_generic_type_pack(&mut self, generic_type_pack: &mut GenericTypePack) {
252 generic_type_pack.clear_comments();
253 }
254
255 fn process_variadic_type_pack(&mut self, variadic_type_pack: &mut VariadicTypePack) {
256 variadic_type_pack.clear_comments();
257 }
258}
259
260#[derive(Debug)]
261pub(crate) struct FilterCommentProcessor<'a> {
262 original_code: &'a str,
263 except: &'a Vec<Regex>,
264}
265
266impl<'a> FilterCommentProcessor<'a> {
267 pub(crate) fn new(original_code: &'a str, except: &'a Vec<Regex>) -> Self {
268 Self {
269 original_code,
270 except,
271 }
272 }
273
274 fn ignore_trivia(&self, trivia: &Trivia) -> bool {
275 let content = trivia.read(self.original_code);
276 self.except.iter().any(|pattern| pattern.is_match(content))
277 }
278}
279
280impl NodeProcessor for FilterCommentProcessor<'_> {
281 fn process_block(&mut self, block: &mut Block) {
282 block.filter_comments(|trivia| self.ignore_trivia(trivia));
283 }
284
285 fn process_function_call(&mut self, call: &mut FunctionCall) {
286 call.filter_comments(|trivia| self.ignore_trivia(trivia));
287 call.mutate_arguments()
288 .filter_comments(|trivia| self.ignore_trivia(trivia));
289 }
290
291 fn process_assign_statement(&mut self, assign: &mut AssignStatement) {
292 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
293 }
294
295 fn process_compound_assign_statement(&mut self, assign: &mut CompoundAssignStatement) {
296 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
297 }
298
299 fn process_do_statement(&mut self, statement: &mut DoStatement) {
300 statement.filter_comments(|trivia| self.ignore_trivia(trivia));
301 }
302
303 fn process_function_statement(&mut self, function: &mut FunctionStatement) {
304 function.filter_comments(|trivia| self.ignore_trivia(trivia));
305 }
306
307 fn process_generic_for_statement(&mut self, generic_for: &mut GenericForStatement) {
308 generic_for.filter_comments(|trivia| self.ignore_trivia(trivia));
309 }
310
311 fn process_if_statement(&mut self, if_statement: &mut IfStatement) {
312 if_statement.filter_comments(|trivia| self.ignore_trivia(trivia));
313 }
314
315 fn process_last_statement(&mut self, statement: &mut LastStatement) {
316 match statement {
317 LastStatement::Break(token) | LastStatement::Continue(token) => {
318 if let Some(token) = token {
319 token.filter_comments(|trivia| self.ignore_trivia(trivia));
320 }
321 }
322 LastStatement::Return(statement) => {
323 statement.filter_comments(|trivia| self.ignore_trivia(trivia))
324 }
325 }
326 }
327
328 fn process_local_assign_statement(&mut self, assign: &mut LocalAssignStatement) {
329 assign.filter_comments(|trivia| self.ignore_trivia(trivia));
330 }
331
332 fn process_local_function_statement(&mut self, function: &mut LocalFunctionStatement) {
333 function.filter_comments(|trivia| self.ignore_trivia(trivia));
334 }
335
336 fn process_numeric_for_statement(&mut self, numeric_for: &mut NumericForStatement) {
337 numeric_for.filter_comments(|trivia| self.ignore_trivia(trivia));
338 }
339
340 fn process_repeat_statement(&mut self, repeat: &mut RepeatStatement) {
341 repeat.filter_comments(|trivia| self.ignore_trivia(trivia));
342 }
343
344 fn process_while_statement(&mut self, statement: &mut WhileStatement) {
345 statement.filter_comments(|trivia| self.ignore_trivia(trivia));
346 }
347
348 fn process_type_declaration(&mut self, type_declaration: &mut TypeDeclarationStatement) {
349 type_declaration.filter_comments(|trivia| self.ignore_trivia(trivia));
350 }
351
352 fn process_type_function(&mut self, type_function: &mut TypeFunctionStatement) {
353 type_function.filter_comments(|trivia| self.ignore_trivia(trivia));
354 }
355
356 fn process_attributes(&mut self, attributes: &mut Attributes) {
357 attributes.filter_comments(|trivia| self.ignore_trivia(trivia));
358 }
359
360 fn process_literal_expression(&mut self, expression: &mut LiteralExpression) {
361 match expression {
362 LiteralExpression::True(token)
363 | LiteralExpression::False(token)
364 | LiteralExpression::Nil(token) => {
365 if let Some(token) = token {
366 token.filter_comments(|trivia| self.ignore_trivia(trivia))
367 }
368 }
369 LiteralExpression::Number(_)
370 | LiteralExpression::String(_)
371 | LiteralExpression::Table(_) => {}
372 }
373 }
374
375 fn process_literal_table(&mut self, table: &mut LiteralTable) {
376 table.filter_comments(|trivia| self.ignore_trivia(trivia));
377 }
378
379 fn process_expression(&mut self, expression: &mut Expression) {
380 match expression {
381 Expression::False(token)
382 | Expression::Nil(token)
383 | Expression::True(token)
384 | Expression::VariableArguments(token) => {
385 if let Some(token) = token {
386 token.filter_comments(|trivia| self.ignore_trivia(trivia))
387 }
388 }
389 Expression::Binary(_)
390 | Expression::Call(_)
391 | Expression::Field(_)
392 | Expression::Function(_)
393 | Expression::Identifier(_)
394 | Expression::If(_)
395 | Expression::Index(_)
396 | Expression::Number(_)
397 | Expression::Parenthese(_)
398 | Expression::String(_)
399 | Expression::InterpolatedString(_)
400 | Expression::Table(_)
401 | Expression::Unary(_)
402 | Expression::TypeCast(_) => {}
403 }
404 }
405
406 fn process_binary_expression(&mut self, binary: &mut BinaryExpression) {
407 binary.filter_comments(|trivia| self.ignore_trivia(trivia));
408 }
409
410 fn process_field_expression(&mut self, field: &mut FieldExpression) {
411 field.filter_comments(|trivia| self.ignore_trivia(trivia));
412 }
413
414 fn process_function_expression(&mut self, function: &mut FunctionExpression) {
415 function.filter_comments(|trivia| self.ignore_trivia(trivia));
416 }
417
418 fn process_if_expression(&mut self, if_expression: &mut IfExpression) {
419 if_expression.filter_comments(|trivia| self.ignore_trivia(trivia));
420 }
421
422 fn process_variable_expression(&mut self, identifier: &mut Identifier) {
423 identifier.filter_comments(|trivia| self.ignore_trivia(trivia));
424 }
425
426 fn process_index_expression(&mut self, index: &mut IndexExpression) {
427 index.filter_comments(|trivia| self.ignore_trivia(trivia));
428 }
429
430 fn process_number_expression(&mut self, number: &mut NumberExpression) {
431 number.filter_comments(|trivia| self.ignore_trivia(trivia));
432 }
433
434 fn process_parenthese_expression(&mut self, expression: &mut ParentheseExpression) {
435 expression.filter_comments(|trivia| self.ignore_trivia(trivia));
436 }
437
438 fn process_string_expression(&mut self, string: &mut StringExpression) {
439 string.filter_comments(|trivia| self.ignore_trivia(trivia));
440 }
441
442 fn process_table_expression(&mut self, table: &mut TableExpression) {
443 table.filter_comments(|trivia| self.ignore_trivia(trivia));
444 }
445
446 fn process_unary_expression(&mut self, unary: &mut UnaryExpression) {
447 unary.filter_comments(|trivia| self.ignore_trivia(trivia));
448 }
449
450 fn process_interpolated_string_expression(
451 &mut self,
452 string: &mut InterpolatedStringExpression,
453 ) {
454 string.filter_comments(|trivia| self.ignore_trivia(trivia));
455 }
456
457 fn process_type_cast_expression(&mut self, type_cast: &mut TypeCastExpression) {
458 type_cast.filter_comments(|trivia| self.ignore_trivia(trivia));
459 }
460
461 fn process_prefix_expression(&mut self, _: &mut Prefix) {}
462
463 fn process_type(&mut self, r#type: &mut Type) {
464 match r#type {
465 Type::True(token) | Type::False(token) | Type::Nil(token) => {
466 if let Some(token) = token {
467 token.filter_comments(|trivia| self.ignore_trivia(trivia));
468 }
469 }
470 _ => {}
471 }
472 }
473
474 fn process_type_name(&mut self, type_name: &mut TypeName) {
475 type_name.filter_comments(|trivia| self.ignore_trivia(trivia));
476 }
477
478 fn process_type_field(&mut self, type_field: &mut TypeField) {
479 type_field.filter_comments(|trivia| self.ignore_trivia(trivia));
480 }
481
482 fn process_string_type(&mut self, string_type: &mut StringType) {
483 string_type.filter_comments(|trivia| self.ignore_trivia(trivia));
484 }
485
486 fn process_array_type(&mut self, array: &mut ArrayType) {
487 array.filter_comments(|trivia| self.ignore_trivia(trivia));
488 }
489
490 fn process_table_type(&mut self, table: &mut TableType) {
491 table.filter_comments(|trivia| self.ignore_trivia(trivia));
492 }
493
494 fn process_expression_type(&mut self, expression_type: &mut ExpressionType) {
495 expression_type.filter_comments(|trivia| self.ignore_trivia(trivia));
496 }
497
498 fn process_parenthese_type(&mut self, parenthese_type: &mut ParentheseType) {
499 parenthese_type.filter_comments(|trivia| self.ignore_trivia(trivia));
500 }
501
502 fn process_function_type(&mut self, function_type: &mut FunctionType) {
503 function_type.filter_comments(|trivia| self.ignore_trivia(trivia));
504 }
505
506 fn process_optional_type(&mut self, optional: &mut OptionalType) {
507 optional.filter_comments(|trivia| self.ignore_trivia(trivia));
508 }
509
510 fn process_intersection_type(&mut self, intersection: &mut IntersectionType) {
511 intersection.filter_comments(|trivia| self.ignore_trivia(trivia));
512 }
513
514 fn process_union_type(&mut self, union: &mut UnionType) {
515 union.filter_comments(|trivia| self.ignore_trivia(trivia));
516 }
517
518 fn process_type_pack(&mut self, type_pack: &mut TypePack) {
519 type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
520 }
521
522 fn process_generic_type_pack(&mut self, generic_type_pack: &mut GenericTypePack) {
523 generic_type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
524 }
525
526 fn process_variadic_type_pack(&mut self, variadic_type_pack: &mut VariadicTypePack) {
527 variadic_type_pack.filter_comments(|trivia| self.ignore_trivia(trivia));
528 }
529}
530
531pub const REMOVE_COMMENTS_RULE_NAME: &str = "remove_comments";
532
533#[derive(Debug, Default)]
535pub struct RemoveComments {
536 metadata: RuleMetadata,
537 except: Vec<Regex>,
538}
539
540impl RemoveComments {
541 pub fn with_exception(mut self, exception_pattern: &str) -> Self {
542 match Regex::new(exception_pattern) {
543 Ok(regex_value) => {
544 self.except.push(regex_value);
545 }
546 Err(err) => {
547 log::warn!(
548 "unable to compile regex pattern '{}': {}",
549 exception_pattern,
550 err
551 );
552 }
553 };
554
555 self
556 }
557}
558
559impl FlawlessRule for RemoveComments {
560 fn flawless_process(&self, block: &mut Block, context: &Context) {
561 if self.except.is_empty() {
562 let mut processor = RemoveCommentProcessor::default();
563 DefaultVisitor::visit_block(block, &mut processor);
564 } else {
565 let mut processor = FilterCommentProcessor::new(context.original_code(), &self.except);
566 DefaultVisitor::visit_block(block, &mut processor);
567 }
568 }
569}
570
571impl RuleConfiguration for RemoveComments {
572 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
573 for (key, value) in properties {
574 match key.as_str() {
575 "except" => {
576 self.except = value.expect_regex_list(&key)?;
577 }
578 _ => return Err(RuleConfigurationError::UnexpectedProperty(key)),
579 }
580 }
581
582 Ok(())
583 }
584
585 fn get_name(&self) -> &'static str {
586 REMOVE_COMMENTS_RULE_NAME
587 }
588
589 fn serialize_to_properties(&self) -> RuleProperties {
590 RuleProperties::new()
591 }
592
593 fn set_metadata(&mut self, metadata: RuleMetadata) {
594 self.metadata = metadata;
595 }
596
597 fn metadata(&self) -> &RuleMetadata {
598 &self.metadata
599 }
600}
601
602#[cfg(test)]
603mod test {
604 use super::*;
605 use crate::{
606 generator::{LuaGenerator, TokenBasedLuaGenerator},
607 rules::{ContextBuilder, Rule},
608 Parser, Resources,
609 };
610
611 use insta::assert_json_snapshot;
612
613 fn new_rule() -> RemoveComments {
614 RemoveComments::default()
615 }
616
617 #[test]
618 fn serialize_default_rule() {
619 let rule: Box<dyn Rule> = Box::new(new_rule());
620
621 assert_json_snapshot!(rule, @r###""remove_comments""###);
622 }
623
624 #[test]
625 fn configure_with_extra_field_error() {
626 let result = json5::from_str::<Box<dyn Rule>>(
627 r#"{
628 rule: 'remove_comments',
629 prop: "something",
630 }"#,
631 );
632 insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1");
633 }
634
635 #[test]
636 fn configure_with_invalid_regex_error() {
637 let result = json5::from_str::<Box<dyn Rule>>(
638 r#"{
639 rule: 'remove_comments',
640 except: ["^[0-9"],
641 }"#,
642 );
643
644 insta::assert_snapshot!(
645 result.unwrap_err().to_string(),
646 @r###"
647 unexpected value for field 'except': invalid regex provided `^[0-9`
648 regex parse error:
649 ^[0-9
650 ^
651 error: unclosed character class at line 1 column 1
652 "###
653 );
654 }
655
656 #[test]
657 fn remove_comments_in_code() {
658 let code = include_str!("../../tests/test_cases/spaces_and_comments.lua");
659
660 let parser = Parser::default().preserve_tokens();
661
662 let mut block = parser.parse(code).expect("unable to parse code");
663
664 RemoveComments::default().flawless_process(
665 &mut block,
666 &ContextBuilder::new(".", &Resources::from_memory(), code).build(),
667 );
668
669 let mut generator = TokenBasedLuaGenerator::new(code);
670
671 generator.write_block(&block);
672
673 let code_output = &generator.into_string();
674
675 insta::assert_snapshot!("remove_comments_in_code", code_output);
676 }
677
678 #[test]
679 fn remove_comments_in_code_with_exception() {
680 let code = include_str!("../../tests/test_cases/spaces_and_comments.lua");
681
682 let parser = Parser::default().preserve_tokens();
683
684 let mut block = parser.parse(code).expect("unable to parse code");
685
686 RemoveComments::default()
687 .with_exception("this.*")
688 .flawless_process(
689 &mut block,
690 &ContextBuilder::new(".", &Resources::from_memory(), code).build(),
691 );
692
693 let mut generator = TokenBasedLuaGenerator::new(code);
694
695 generator.write_block(&block);
696
697 let code_output = &generator.into_string();
698
699 insta::assert_snapshot!("remove_comments_in_code_with_exception", code_output);
700 }
701}