From 1d747c005406643a3546e704a034a8c61066020e Mon Sep 17 00:00:00 2001 From: jiwonz Date: Wed, 29 Jan 2025 18:58:32 +0900 Subject: [PATCH 1/2] Add remove_number_literals rule --- src/rules/mod.rs | 5 ++ src/rules/remove_number_literals.rs | 90 +++++++++++++++++++ ..._test__default_remove_number_literals.snap | 5 ++ ...lua_core__rules__test__all_rule_names.snap | 3 +- ...klua_core__rules__test__default_rules.snap | 3 +- tests/rule_tests/mod.rs | 1 + tests/rule_tests/remove_number_literals.rs | 27 ++++++ 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 src/rules/remove_number_literals.rs create mode 100644 src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap create mode 100644 tests/rule_tests/remove_number_literals.rs diff --git a/src/rules/mod.rs b/src/rules/mod.rs index e0050181..cd65e0a4 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -23,6 +23,7 @@ mod remove_floor_division; mod remove_if_expression; mod remove_interpolated_string; mod remove_nil_declarations; +mod remove_number_literals; mod remove_spaces; mod remove_types; mod remove_unused_variable; @@ -55,6 +56,7 @@ pub use remove_floor_division::*; pub use remove_if_expression::*; pub use remove_interpolated_string::*; pub use remove_nil_declarations::*; +pub use remove_number_literals::*; pub use remove_spaces::*; pub use remove_types::*; pub use remove_unused_variable::*; @@ -235,6 +237,7 @@ pub fn get_default_rules() -> Vec> { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), ] } @@ -265,6 +268,7 @@ pub fn get_all_rule_names() -> Vec<&'static str> { RENAME_VARIABLES_RULE_NAME, REMOVE_IF_EXPRESSION_RULE_NAME, REMOVE_CONTINUE_RULE_NAME, + REMOVE_NUMBER_LITERALS_RULE_NAME, ] } @@ -301,6 +305,7 @@ impl FromStr for Box { RENAME_VARIABLES_RULE_NAME => Box::::default(), REMOVE_IF_EXPRESSION_RULE_NAME => Box::::default(), REMOVE_CONTINUE_RULE_NAME => Box::::default(), + REMOVE_NUMBER_LITERALS_RULE_NAME => Box::::default(), _ => return Err(format!("invalid rule name: {}", string)), }; diff --git a/src/rules/remove_number_literals.rs b/src/rules/remove_number_literals.rs new file mode 100644 index 00000000..5c11b94c --- /dev/null +++ b/src/rules/remove_number_literals.rs @@ -0,0 +1,90 @@ +use crate::{ + nodes::{Block, DecimalNumber, Expression, NumberExpression}, + process::{DefaultVisitor, NodeProcessor, NodeVisitor}, + rules::{Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties}, +}; + +use super::verify_no_rule_properties; + +#[derive(Default)] +struct Processor {} + +impl NodeProcessor for Processor { + fn process_expression(&mut self, exp: &mut Expression) { + if let Expression::Number(num_exp) = exp { + match num_exp { + NumberExpression::Binary(binary) => { + let value = binary.compute_value(); + *exp = DecimalNumber::new(value).into(); + } + NumberExpression::Decimal(decimal) => { + let value = decimal.compute_value(); + *exp = DecimalNumber::new(value).into(); + } + NumberExpression::Hex(hex) => { + let value = hex.compute_value(); + *exp = DecimalNumber::new(value).into(); + } + } + } + } +} + +pub const REMOVE_NUMBER_LITERALS_RULE_NAME: &str = "remove_number_literals"; + +/// A rule that removes number literals. +#[derive(Default, Debug, PartialEq, Eq)] +pub struct RemoveNumberLiterals {} + +impl FlawlessRule for RemoveNumberLiterals { + fn flawless_process(&self, block: &mut Block, _: &Context) { + let mut processor = Processor {}; + DefaultVisitor::visit_block(block, &mut processor); + } +} + +impl RuleConfiguration for RemoveNumberLiterals { + fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> { + verify_no_rule_properties(&properties)?; + + Ok(()) + } + + fn get_name(&self) -> &'static str { + REMOVE_NUMBER_LITERALS_RULE_NAME + } + + fn serialize_to_properties(&self) -> RuleProperties { + RuleProperties::new() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::rules::Rule; + + use insta::assert_json_snapshot; + + fn new_rule() -> RemoveNumberLiterals { + RemoveNumberLiterals::default() + } + + #[test] + fn serialize_default_rule() { + let rule: Box = Box::new(new_rule()); + + assert_json_snapshot!("default_remove_number_literals", rule); + } + + #[test] + fn configure_with_extra_field_error() { + let result = json5::from_str::>( + r#"{ + rule: 'remove_number_literals', + prop: "something", + }"#, + ); + pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'"); + } +} diff --git a/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap b/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap new file mode 100644 index 00000000..492fc9b0 --- /dev/null +++ b/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap @@ -0,0 +1,5 @@ +--- +source: src/rules/remove_number_literals.rs +expression: rule +--- +"remove_number_literals" diff --git a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap index b7cf2528..16f9e433 100644 --- a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap +++ b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap @@ -27,5 +27,6 @@ expression: rule_names "remove_unused_while", "rename_variables", "remove_if_expression", - "remove_continue" + "remove_continue", + "remove_number_literals" ] diff --git a/src/rules/snapshots/darklua_core__rules__test__default_rules.snap b/src/rules/snapshots/darklua_core__rules__test__default_rules.snap index d87f8de3..d4b1448c 100644 --- a/src/rules/snapshots/darklua_core__rules__test__default_rules.snap +++ b/src/rules/snapshots/darklua_core__rules__test__default_rules.snap @@ -15,5 +15,6 @@ expression: rules "convert_index_to_field", "remove_nil_declaration", "rename_variables", - "remove_function_call_parens" + "remove_function_call_parens", + "remove_number_literals" ] diff --git a/tests/rule_tests/mod.rs b/tests/rule_tests/mod.rs index f73e5580..ff6585f6 100644 --- a/tests/rule_tests/mod.rs +++ b/tests/rule_tests/mod.rs @@ -435,6 +435,7 @@ mod remove_if_expression; mod remove_interpolated_string; mod remove_method_definition; mod remove_nil_declaration; +mod remove_number_literals; mod remove_types; mod remove_unused_if_branch; mod remove_unused_variable; diff --git a/tests/rule_tests/remove_number_literals.rs b/tests/rule_tests/remove_number_literals.rs new file mode 100644 index 00000000..252e6b69 --- /dev/null +++ b/tests/rule_tests/remove_number_literals.rs @@ -0,0 +1,27 @@ +use darklua_core::rules::{RemoveNumberLiterals, Rule}; + +test_rule!( + remove_number_literals, + RemoveNumberLiterals::default(), + hexadecimal_integer_literals("local a = 0xABC local b = 0XABC") + => "local a = 2748 local b = 2748", + binary_integer_literals("local a = 0b01010101 local b = 0B01010101") + => "local a = 85 local b = 85", + decimal_separators("local a = 1_048_576 local b = 0xFFFF_FFFF local c = 0b_0101_0101") + => "local a = 1048576 local b = 4294967295 local c = 85", +); + +#[test] +fn deserialize_from_object_notation() { + json5::from_str::>( + r#"{ + rule: 'remove_number_literals', + }"#, + ) + .unwrap(); +} + +#[test] +fn deserialize_from_string() { + json5::from_str::>("'remove_number_literals'").unwrap(); +} From 2ba52a0f04d2e81a522e6140e8cf28de2150e5ec Mon Sep 17 00:00:00 2001 From: jiwonz Date: Thu, 13 Mar 2025 19:46:35 +0900 Subject: [PATCH 2/2] Reflect the feedbacks and suggestions from reviews (the rule name has changed to convert_luau_numbers and only converts binary to decimal and removes underscores from token) --- src/rules/convert_luau_numbers.rs | 106 ++++++++++++++++++ src/rules/mod.rs | 9 +- src/rules/remove_number_literals.rs | 90 --------------- ...s__test__default_convert_luau_numbers.snap | 5 + ...lua_core__rules__test__all_rule_names.snap | 2 +- ...klua_core__rules__test__default_rules.snap | 3 +- tests/rule_tests/convert_luau_numbers.rs | 25 +++++ tests/rule_tests/mod.rs | 2 +- tests/rule_tests/remove_number_literals.rs | 27 ----- 9 files changed, 143 insertions(+), 126 deletions(-) create mode 100644 src/rules/convert_luau_numbers.rs delete mode 100644 src/rules/remove_number_literals.rs create mode 100644 src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap create mode 100644 tests/rule_tests/convert_luau_numbers.rs delete mode 100644 tests/rule_tests/remove_number_literals.rs diff --git a/src/rules/convert_luau_numbers.rs b/src/rules/convert_luau_numbers.rs new file mode 100644 index 00000000..35350018 --- /dev/null +++ b/src/rules/convert_luau_numbers.rs @@ -0,0 +1,106 @@ +use crate::{ + nodes::{Block, DecimalNumber, NumberExpression}, + process::{DefaultVisitor, NodeProcessor, NodeVisitor}, + rules::{Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties}, +}; + +use super::verify_no_rule_properties; + +#[derive(Default)] +struct Processor<'a> { + code: &'a str, +} + +impl NodeProcessor for Processor<'_> { + fn process_number_expression(&mut self, num_exp: &mut NumberExpression) { + if let NumberExpression::Binary(binary) = num_exp { + let value = binary.compute_value(); + *num_exp = DecimalNumber::new(value).into(); + return; + } + if let Some(token) = num_exp.get_token() { + let content = token.read(self.code); + let mut underscore_removed = String::with_capacity(content.len()); + let mut changed = false; + + for c in content.chars() { + if c != '_' { + underscore_removed.push(c); + } else { + changed = true; + } + } + + if changed { + let mut new_token = token.clone(); + new_token.replace_with_content(underscore_removed); + num_exp.set_token(new_token); + } + } + } +} + +impl<'a> Processor<'a> { + fn new(code: &'a str) -> Self { + Self { code } + } +} + +pub const CONVERT_LUAU_NUMBERS_RULE_NAME: &str = "convert_luau_numbers"; + +/// A rule that converts Luau number literals to decimal numbers. +#[derive(Default, Debug, PartialEq, Eq)] +pub struct ConvertLuauNumbers {} + +impl FlawlessRule for ConvertLuauNumbers { + fn flawless_process(&self, block: &mut Block, context: &Context) { + let mut processor = Processor::new(context.original_code()); + DefaultVisitor::visit_block(block, &mut processor); + } +} + +impl RuleConfiguration for ConvertLuauNumbers { + fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> { + verify_no_rule_properties(&properties)?; + + Ok(()) + } + + fn get_name(&self) -> &'static str { + CONVERT_LUAU_NUMBERS_RULE_NAME + } + + fn serialize_to_properties(&self) -> RuleProperties { + RuleProperties::new() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::rules::Rule; + + use insta::assert_json_snapshot; + + fn new_rule() -> ConvertLuauNumbers { + ConvertLuauNumbers::default() + } + + #[test] + fn serialize_default_rule() { + let rule: Box = Box::new(new_rule()); + + assert_json_snapshot!("default_convert_luau_numbers", rule); + } + + #[test] + fn configure_with_extra_field_error() { + let result = json5::from_str::>( + r#"{ + rule: 'convert_luau_numbers', + prop: "something", + }"#, + ); + pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'"); + } +} diff --git a/src/rules/mod.rs b/src/rules/mod.rs index cd65e0a4..0ea21db0 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -6,6 +6,7 @@ mod call_parens; mod compute_expression; mod configuration_error; mod convert_index_to_field; +mod convert_luau_numbers; mod convert_require; mod empty_do; mod filter_early_return; @@ -23,7 +24,6 @@ mod remove_floor_division; mod remove_if_expression; mod remove_interpolated_string; mod remove_nil_declarations; -mod remove_number_literals; mod remove_spaces; mod remove_types; mod remove_unused_variable; @@ -40,6 +40,7 @@ pub use call_parens::*; pub use compute_expression::*; pub use configuration_error::RuleConfigurationError; pub use convert_index_to_field::*; +pub use convert_luau_numbers::*; pub use convert_require::*; pub use empty_do::*; pub use filter_early_return::*; @@ -56,7 +57,6 @@ pub use remove_floor_division::*; pub use remove_if_expression::*; pub use remove_interpolated_string::*; pub use remove_nil_declarations::*; -pub use remove_number_literals::*; pub use remove_spaces::*; pub use remove_types::*; pub use remove_unused_variable::*; @@ -237,7 +237,6 @@ pub fn get_default_rules() -> Vec> { Box::::default(), Box::::default(), Box::::default(), - Box::::default(), ] } @@ -268,7 +267,7 @@ pub fn get_all_rule_names() -> Vec<&'static str> { RENAME_VARIABLES_RULE_NAME, REMOVE_IF_EXPRESSION_RULE_NAME, REMOVE_CONTINUE_RULE_NAME, - REMOVE_NUMBER_LITERALS_RULE_NAME, + CONVERT_LUAU_NUMBERS_RULE_NAME, ] } @@ -305,7 +304,7 @@ impl FromStr for Box { RENAME_VARIABLES_RULE_NAME => Box::::default(), REMOVE_IF_EXPRESSION_RULE_NAME => Box::::default(), REMOVE_CONTINUE_RULE_NAME => Box::::default(), - REMOVE_NUMBER_LITERALS_RULE_NAME => Box::::default(), + CONVERT_LUAU_NUMBERS_RULE_NAME => Box::::default(), _ => return Err(format!("invalid rule name: {}", string)), }; diff --git a/src/rules/remove_number_literals.rs b/src/rules/remove_number_literals.rs deleted file mode 100644 index 5c11b94c..00000000 --- a/src/rules/remove_number_literals.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{ - nodes::{Block, DecimalNumber, Expression, NumberExpression}, - process::{DefaultVisitor, NodeProcessor, NodeVisitor}, - rules::{Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties}, -}; - -use super::verify_no_rule_properties; - -#[derive(Default)] -struct Processor {} - -impl NodeProcessor for Processor { - fn process_expression(&mut self, exp: &mut Expression) { - if let Expression::Number(num_exp) = exp { - match num_exp { - NumberExpression::Binary(binary) => { - let value = binary.compute_value(); - *exp = DecimalNumber::new(value).into(); - } - NumberExpression::Decimal(decimal) => { - let value = decimal.compute_value(); - *exp = DecimalNumber::new(value).into(); - } - NumberExpression::Hex(hex) => { - let value = hex.compute_value(); - *exp = DecimalNumber::new(value).into(); - } - } - } - } -} - -pub const REMOVE_NUMBER_LITERALS_RULE_NAME: &str = "remove_number_literals"; - -/// A rule that removes number literals. -#[derive(Default, Debug, PartialEq, Eq)] -pub struct RemoveNumberLiterals {} - -impl FlawlessRule for RemoveNumberLiterals { - fn flawless_process(&self, block: &mut Block, _: &Context) { - let mut processor = Processor {}; - DefaultVisitor::visit_block(block, &mut processor); - } -} - -impl RuleConfiguration for RemoveNumberLiterals { - fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> { - verify_no_rule_properties(&properties)?; - - Ok(()) - } - - fn get_name(&self) -> &'static str { - REMOVE_NUMBER_LITERALS_RULE_NAME - } - - fn serialize_to_properties(&self) -> RuleProperties { - RuleProperties::new() - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::rules::Rule; - - use insta::assert_json_snapshot; - - fn new_rule() -> RemoveNumberLiterals { - RemoveNumberLiterals::default() - } - - #[test] - fn serialize_default_rule() { - let rule: Box = Box::new(new_rule()); - - assert_json_snapshot!("default_remove_number_literals", rule); - } - - #[test] - fn configure_with_extra_field_error() { - let result = json5::from_str::>( - r#"{ - rule: 'remove_number_literals', - prop: "something", - }"#, - ); - pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'"); - } -} diff --git a/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap b/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap new file mode 100644 index 00000000..43809221 --- /dev/null +++ b/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap @@ -0,0 +1,5 @@ +--- +source: src/rules/convert_luau_numbers.rs +expression: rule +--- +"convert_luau_numbers" diff --git a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap index 16f9e433..800aa4e4 100644 --- a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap +++ b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap @@ -28,5 +28,5 @@ expression: rule_names "rename_variables", "remove_if_expression", "remove_continue", - "remove_number_literals" + "convert_luau_numbers" ] diff --git a/src/rules/snapshots/darklua_core__rules__test__default_rules.snap b/src/rules/snapshots/darklua_core__rules__test__default_rules.snap index d4b1448c..d87f8de3 100644 --- a/src/rules/snapshots/darklua_core__rules__test__default_rules.snap +++ b/src/rules/snapshots/darklua_core__rules__test__default_rules.snap @@ -15,6 +15,5 @@ expression: rules "convert_index_to_field", "remove_nil_declaration", "rename_variables", - "remove_function_call_parens", - "remove_number_literals" + "remove_function_call_parens" ] diff --git a/tests/rule_tests/convert_luau_numbers.rs b/tests/rule_tests/convert_luau_numbers.rs new file mode 100644 index 00000000..d381d559 --- /dev/null +++ b/tests/rule_tests/convert_luau_numbers.rs @@ -0,0 +1,25 @@ +use darklua_core::rules::{ConvertLuauNumbers, Rule}; + +test_rule!( + convert_luau_numbers, + ConvertLuauNumbers::default(), + binary_integer_literals("local a = 0b01010101 local b = 0B01010101 local c = 0b_0101_0101 local d = 0B_0101_0101 local e = 0b__________0101_0101") + => "local a = 85 local b = 85 local c = 85 local d = 85 local e = 85", + decimal_separators("local a = 1_048_576 local a1 = 1___048__576__ local b = 0xFFFF_FFFF local c = 0b_0101_0101 local d = 0B_0101_0101") + => "local a = 1048576 local a1 = 1048576 local b = 0xFFFFFFFF local c = 85 local d = 85", +); + +#[test] +fn deserialize_from_object_notation() { + json5::from_str::>( + r#"{ + rule: 'convert_luau_numbers', + }"#, + ) + .unwrap(); +} + +#[test] +fn deserialize_from_string() { + json5::from_str::>("'convert_luau_numbers'").unwrap(); +} diff --git a/tests/rule_tests/mod.rs b/tests/rule_tests/mod.rs index ff6585f6..674a760d 100644 --- a/tests/rule_tests/mod.rs +++ b/tests/rule_tests/mod.rs @@ -418,6 +418,7 @@ macro_rules! test_rule_snapshot { mod append_text_comment; mod compute_expression; mod convert_index_to_field; +mod convert_luau_numbers; mod convert_require; mod filter_early_return; mod group_local_assignment; @@ -435,7 +436,6 @@ mod remove_if_expression; mod remove_interpolated_string; mod remove_method_definition; mod remove_nil_declaration; -mod remove_number_literals; mod remove_types; mod remove_unused_if_branch; mod remove_unused_variable; diff --git a/tests/rule_tests/remove_number_literals.rs b/tests/rule_tests/remove_number_literals.rs deleted file mode 100644 index 252e6b69..00000000 --- a/tests/rule_tests/remove_number_literals.rs +++ /dev/null @@ -1,27 +0,0 @@ -use darklua_core::rules::{RemoveNumberLiterals, Rule}; - -test_rule!( - remove_number_literals, - RemoveNumberLiterals::default(), - hexadecimal_integer_literals("local a = 0xABC local b = 0XABC") - => "local a = 2748 local b = 2748", - binary_integer_literals("local a = 0b01010101 local b = 0B01010101") - => "local a = 85 local b = 85", - decimal_separators("local a = 1_048_576 local b = 0xFFFF_FFFF local c = 0b_0101_0101") - => "local a = 1048576 local b = 4294967295 local c = 85", -); - -#[test] -fn deserialize_from_object_notation() { - json5::from_str::>( - r#"{ - rule: 'remove_number_literals', - }"#, - ) - .unwrap(); -} - -#[test] -fn deserialize_from_string() { - json5::from_str::>("'remove_number_literals'").unwrap(); -}