Repository: seaofvoices/darklua (rust)

Issue:
Application of 'remove_types' can change runtime behavior where a function call has a type assertion
Adding a type assertion to a function call has the (runtime) effect of truncating any returned values to a maximum of one value:
```Lua
local function multipleReturns()
    return 1, 2
end
print(multipleReturns() :: any)
```
This will output "1".

Currently, the 'remove_types' rule translates line 4 as:
```Lua
print(multipleReturns())
```
This will output both "1" and "2", incorrectly.

The 'remove_types' rule should wrap these type-asserted calls in parentheses:
```Lua
print((multipleReturns()))
```
This will output "1", correctly.

Pull request description:
Fix `remove_types` rule to handle type cast on expressions of multiple values
Closes #140 

- [x] add entry to the changelog


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c4f17f..28e793a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+* fix `remove_types` rule to handle type cast of expressions that could return multiple values ([#142](https://github.com/seaofvoices/darklua/pull/142))
+
 ## 0.11.1
 
 * fix type packs, function variadic types and variadic type packs ([#137](https://github.com/seaofvoices/darklua/pull/137))
diff --git a/src/rules/remove_types.rs b/src/rules/remove_types.rs
index 400d3af..38c269e 100644
--- a/src/rules/remove_types.rs
+++ b/src/rules/remove_types.rs
@@ -1,5 +1,5 @@
 use crate::nodes::*;
-use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
+use crate::process::{DefaultVisitor, Evaluator, NodeProcessor, NodeVisitor};
 use crate::rules::{
     Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
 };
@@ -7,7 +7,9 @@ use crate::rules::{
 use super::verify_no_rule_properties;
 
 #[derive(Default)]
-struct RemoveTypesProcessor {}
+struct RemoveTypesProcessor {
+    evaluator: Evaluator,
+}
 
 impl NodeProcessor for RemoveTypesProcessor {
     fn process_block(&mut self, block: &mut Block) {
@@ -41,7 +43,12 @@ impl NodeProcessor for RemoveTypesProcessor {
     fn process_expression(&mut self, expression: &mut Expression) {
         match expression {
             Expression::TypeCast(type_cast) => {
-                *expression = type_cast.get_expression().clone();
+                let value = type_cast.get_expression();
+                if self.evaluator.can_return_multiple_values(value) {
+                    *expression = value.clone().in_parentheses();
+                } else {
+                    *expression = value.clone();
+                }
             }
             Expression::Function(function) => {
                 function.clear_types();


Test patch:
diff --git a/tests/rule_tests/remove_types.rs b/tests/rule_tests/remove_types.rs
index 27ecf21..9236865 100644
--- a/tests/rule_tests/remove_types.rs
+++ b/tests/rule_tests/remove_types.rs
@@ -41,6 +41,14 @@ test_rule!(
         => "return function(...) end",
     remove_types_in_function_expression_return_type("return function(): boolean end")
         => "return function() end",
+    remove_types_in_type_cast_of_function_call("return call() :: any")
+        => "return (call())",
+    remove_types_in_type_cast_of_variadic_arguments("return ... :: any")
+        => "return (...)",
+    remove_types_in_type_cast_of_identifier("return value :: any")
+        => "return value",
+    remove_types_in_type_cast_of_table("return {} :: any")
+        => "return {}",
 );
 
 #[test]