From 07a083665aebbb4c5290449704bc1600fe2908d3 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 18:34:15 +0200 Subject: [PATCH 1/4] Add tests --- tests/inputs/ambiguous-syntax-2.lua | 20 ++++++++++++++++++++ tests/inputs/ambiguous-syntax-3.lua | 17 +++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/inputs/ambiguous-syntax-2.lua create mode 100644 tests/inputs/ambiguous-syntax-3.lua diff --git a/tests/inputs/ambiguous-syntax-2.lua b/tests/inputs/ambiguous-syntax-2.lua new file mode 100644 index 00000000..0069cea3 --- /dev/null +++ b/tests/inputs/ambiguous-syntax-2.lua @@ -0,0 +1,20 @@ +-- https://github.com/JohnnyMorganz/StyLua/issues/963 +local value = nil + +(Foo):Call() + +local tbl = {} + +(Foo):Call() + +local x = 1 + +(Foo):Call() + +local x = "value" + +(Foo):Call() + +local x = function() end + +(Foo):Call() diff --git a/tests/inputs/ambiguous-syntax-3.lua b/tests/inputs/ambiguous-syntax-3.lua new file mode 100644 index 00000000..bab928bc --- /dev/null +++ b/tests/inputs/ambiguous-syntax-3.lua @@ -0,0 +1,17 @@ +local x = call ""; +(foo or bar and baz)(bar) + +local x = call {}; +(foo or bar and baz)(bar) + +local x = identifier; +(foo or bar and baz)(bar) + +local x = (identifier); +(foo or bar and baz)(bar) + +local x = x.y; +(foo or bar and baz)(bar) + +local x = x["y"]; +(foo or bar and baz)(bar) From b710ff3dcb983d51b2f0d7c499571ba3bd3447ce Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 18:34:21 +0200 Subject: [PATCH 2/4] Check before adding semicolons --- src/formatters/block.rs | 44 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/formatters/block.rs b/src/formatters/block.rs index a5e0db69..6d19a86e 100644 --- a/src/formatters/block.rs +++ b/src/formatters/block.rs @@ -473,18 +473,46 @@ fn var_has_parentheses(var: &Var) -> bool { } } +fn expression_ends_with_identifier_or_parentheses(expression: &Expression) -> bool { + match expression { + Expression::Parentheses { .. } => true, + Expression::FunctionCall(_) => true, + Expression::Var(_) => true, + Expression::BinaryOperator { rhs, .. } => { + expression_ends_with_identifier_or_parentheses(rhs) + } + Expression::UnaryOperator { expression, .. } => { + expression_ends_with_identifier_or_parentheses(expression) + } + _ => false, + } +} + +// Ambiguous syntax can only occur if the current statement ends with an identifier, function call, or parentheses +fn stmt_ends_with_identifier_or_parentheses(stmt: &Stmt) -> bool { + match stmt { + Stmt::Assignment(assignment) => match assignment.expressions().last() { + Some(pair) => expression_ends_with_identifier_or_parentheses(pair.value()), + None => false, + }, + Stmt::LocalAssignment(local_assignment) => match local_assignment.expressions().last() { + Some(pair) => expression_ends_with_identifier_or_parentheses(pair.value()), + None => false, + }, + Stmt::FunctionCall(_) => true, + Stmt::Repeat(repeat) => expression_ends_with_identifier_or_parentheses(repeat.until()), + _ => 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 { + stmt_ends_with_identifier_or_parentheses(stmt) + && match next_stmt { Some((Stmt::FunctionCall(function_call), _)) => match function_call.prefix() { Prefix::Expression(expression) => { matches!(&**expression, Expression::Parentheses { .. }) @@ -500,9 +528,7 @@ fn check_stmt_requires_semicolon( 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 From ab0ffb28a049f6f775b244fa123a6b135673ed6f Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 18:34:26 +0200 Subject: [PATCH 3/4] Update snapshots --- ...ests__standard@ambiguous-syntax-2.lua.snap | 26 +++++++++++++++++++ ...ests__standard@ambiguous-syntax-3.lua.snap | 23 ++++++++++++++++ ...ests__standard@excess-parentheses.lua.snap | 7 ++--- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 tests/snapshots/tests__standard@ambiguous-syntax-2.lua.snap create mode 100644 tests/snapshots/tests__standard@ambiguous-syntax-3.lua.snap diff --git a/tests/snapshots/tests__standard@ambiguous-syntax-2.lua.snap b/tests/snapshots/tests__standard@ambiguous-syntax-2.lua.snap new file mode 100644 index 00000000..2d903745 --- /dev/null +++ b/tests/snapshots/tests__standard@ambiguous-syntax-2.lua.snap @@ -0,0 +1,26 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::Lua51)" +input_file: tests/inputs/ambiguous-syntax-2.lua +snapshot_kind: text +--- +-- https://github.com/JohnnyMorganz/StyLua/issues/963 +local value = nil + +(Foo):Call() + +local tbl = {} + +(Foo):Call() + +local x = 1 + +(Foo):Call() + +local x = "value" + +(Foo):Call() + +local x = function() end + +(Foo):Call() diff --git a/tests/snapshots/tests__standard@ambiguous-syntax-3.lua.snap b/tests/snapshots/tests__standard@ambiguous-syntax-3.lua.snap new file mode 100644 index 00000000..277a926a --- /dev/null +++ b/tests/snapshots/tests__standard@ambiguous-syntax-3.lua.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::Lua51)" +input_file: tests/inputs/ambiguous-syntax-3.lua +snapshot_kind: text +--- +local x = call(""); +(foo or bar and baz)(bar) + +local x = call({}); +(foo or bar and baz)(bar) + +local x = identifier; +(foo or bar and baz)(bar) + +local x = identifier; +(foo or bar and baz)(bar) + +local x = x.y; +(foo or bar and baz)(bar) + +local x = x["y"]; +(foo or bar and baz)(bar) diff --git a/tests/snapshots/tests__standard@excess-parentheses.lua.snap b/tests/snapshots/tests__standard@excess-parentheses.lua.snap index cb6e4d75..e3acdc98 100644 --- a/tests/snapshots/tests__standard@excess-parentheses.lua.snap +++ b/tests/snapshots/tests__standard@excess-parentheses.lua.snap @@ -1,6 +1,8 @@ --- source: tests/tests.rs -expression: format(&contents) +expression: "format(&contents, LuaVersion::Lua51)" +input_file: tests/inputs/excess-parentheses.lua +snapshot_kind: text --- local x something(x) @@ -11,7 +13,7 @@ local z = (...) == nil and foo or bar local foo = not (bar and baz) local bar = #bar and baz local cond = condition and (not object or object.Value == y) -local baz = (-4 + 3) * 2; +local baz = (-4 + 3) * 2 ({}):foo(); ("hello"):format() @@ -34,4 +36,3 @@ local y = "hello" local z = function() return true end - From 7f46ac9f8e975012f001e6f5d7c98584fb9e497e Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 18:34:31 +0200 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c822fb..4fbbd9c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Luau: fixed parentheses incorrectly removed in `(expr :: assertion) < foo` when multilining the expression, leading to a syntax error ([#940](https://github.com/JohnnyMorganz/StyLua/issues/940)) - Fixed panic when attempting to format a file outside of the current working directory when `--respect-ignores` is enabled ([#969](https://github.com/JohnnyMorganz/StyLua/pull/969)) +- Fixed unnecessary semicolons being introduced at the end of statements when incorrectly determined as ambiguous ([#963](https://github.com/JohnnyMorganz/StyLua/issues/963)) ## [2.0.2] - 2024-12-07