diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9757a7..3a6162f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed formatting of method call chain when there is a comment between the colon token `:` and the function name ([#890](https://github.com/JohnnyMorganz/StyLua/issues/890)) +- Luau: Fixed incorrect removal of semicolon before compound assignment with parentheses leading to ambiguous syntax error ([#885](https://github.com/JohnnyMorganz/StyLua/issues/885)) ## [0.20.0] - 2024-01-20 diff --git a/src/formatters/block.rs b/src/formatters/block.rs index df2d45f9..4d48f4b2 100644 --- a/src/formatters/block.rs +++ b/src/formatters/block.rs @@ -448,6 +448,50 @@ fn last_stmt_remove_leading_newlines(last_stmt: LastStmt) -> LastStmt { } } +fn var_has_parentheses(var: &Var) -> bool { + match var { + Var::Expression(var_expression) => match var_expression.prefix() { + Prefix::Expression(expression) => { + matches!(&**expression, Expression::Parentheses { .. }) + } + _ => false, + }, + _ => false, + } +} + +fn check_stmt_requires_semicolon( + stmt: &Stmt, + next_stmt: Option<&&(Stmt, Option)>, +) -> bool { + // Need to check next statement if it is a function call, with a parameters expression as the prefix + // If so, removing a semicolon may lead to ambiguous syntax + // Ambiguous syntax can only occur if the current statement is a (Local)Assignment, FunctionCall or a Repeat block + match stmt { + Stmt::Assignment(_) + | Stmt::LocalAssignment(_) + | Stmt::FunctionCall(_) + | Stmt::Repeat(_) => match next_stmt { + Some((Stmt::FunctionCall(function_call), _)) => match function_call.prefix() { + Prefix::Expression(expression) => { + matches!(&**expression, Expression::Parentheses { .. }) + } + _ => false, + }, + Some((Stmt::Assignment(assignment), _)) => match assignment.variables().iter().next() { + Some(var) => var_has_parentheses(var), + _ => false, + }, + #[cfg(feature = "luau")] + Some((Stmt::CompoundAssignment(compound_assignment), _)) => { + var_has_parentheses(compound_assignment.lhs()) + } + _ => false, + }, + _ => false, + } +} + /// Formats a block node. Note: the given shape to the block formatter should already be at the correct indentation level pub fn format_block(ctx: &Context, block: &Block, shape: Shape) -> Block { let mut ctx = *ctx; @@ -469,44 +513,9 @@ pub fn format_block(ctx: &Context, block: &Block, shape: Shape) -> Block { found_first_stmt = true; } - // Need to check next statement if it is a function call, with a parameters expression as the prefix - // If so, removing a semicolon may lead to ambiguous syntax - // Ambiguous syntax can only occur if the current statement is a (Local)Assignment, FunctionCall or a Repeat block - let require_semicolon = match stmt { - Stmt::Assignment(_) - | Stmt::LocalAssignment(_) - | Stmt::FunctionCall(_) - | Stmt::Repeat(_) => { - let next_stmt = stmt_iterator.peek(); - match next_stmt { - Some((Stmt::FunctionCall(function_call), _)) => match function_call.prefix() { - Prefix::Expression(expression) => { - matches!(&**expression, Expression::Parentheses { .. }) - } - _ => false, - }, - Some((Stmt::Assignment(assignment), _)) => { - match assignment.variables().iter().next() { - Some(Var::Expression(var_expression)) => { - match var_expression.prefix() { - Prefix::Expression(expression) => { - matches!(&**expression, Expression::Parentheses { .. }) - } - _ => false, - } - } - _ => false, - } - } - _ => false, - } - } - _ => false, - }; - // If we have a semicolon, we need to push all the trailing trivia from the statement // and move it to the end of the semicolon - let semicolon = match require_semicolon { + let semicolon = match check_stmt_requires_semicolon(&stmt, stmt_iterator.peek()) { true => { let (updated_stmt, trivia) = trivia_util::get_stmt_trailing_trivia(stmt); stmt = updated_stmt; diff --git a/tests/inputs-luau/compound-assignment-ambiguous-syntax.lua b/tests/inputs-luau/compound-assignment-ambiguous-syntax.lua new file mode 100644 index 00000000..d427efa5 --- /dev/null +++ b/tests/inputs-luau/compound-assignment-ambiguous-syntax.lua @@ -0,0 +1,8 @@ +-- https://github.com/JohnnyMorganz/StyLua/issues/885 + +local function foo() + return { b = "foo" } +end + +local a = foo(); +(a :: any).b ..= "bar" \ No newline at end of file diff --git a/tests/snapshots/tests__luau@compound-assignment-ambiguous-syntax.lua.snap b/tests/snapshots/tests__luau@compound-assignment-ambiguous-syntax.lua.snap new file mode 100644 index 00000000..f0297170 --- /dev/null +++ b/tests/snapshots/tests__luau@compound-assignment-ambiguous-syntax.lua.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests.rs +expression: format(&contents) +input_file: tests/inputs-luau/compound-assignment-ambiguous-syntax.lua +--- +-- https://github.com/JohnnyMorganz/StyLua/issues/885 + +local function foo() + return { b = "foo" } +end + +local a = foo(); +(a :: any).b ..= "bar"