darklua_core/rules/
remove_comments.rs

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/// A rule that removes comments associated with AST nodes.
480#[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}