Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
44 changes: 35 additions & 9 deletions src/formatters/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenReference>)>,
) -> 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 { .. })
Expand All @@ -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
Expand Down
20 changes: 20 additions & 0 deletions tests/inputs/ambiguous-syntax-2.lua
Original file line number Diff line number Diff line change
@@ -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()
17 changes: 17 additions & 0 deletions tests/inputs/ambiguous-syntax-3.lua
Original file line number Diff line number Diff line change
@@ -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)
26 changes: 26 additions & 0 deletions tests/snapshots/tests__standard@ambiguous-syntax-2.lua.snap
Original file line number Diff line number Diff line change
@@ -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()
23 changes: 23 additions & 0 deletions tests/snapshots/tests__standard@ambiguous-syntax-3.lua.snap
Original file line number Diff line number Diff line change
@@ -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)
7 changes: 4 additions & 3 deletions tests/snapshots/tests__standard@excess-parentheses.lua.snap
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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()
Expand All @@ -34,4 +36,3 @@ local y = "hello"
local z = function()
return true
end