Skip to content

Commit b799f2a

Browse files
Don't add semicolons unnecessarily for non-ambiguous statements (#980)
* Add tests * Check before adding semicolons * Update snapshots * Update changelog
1 parent d547861 commit b799f2a

File tree

7 files changed

+126
-12
lines changed

7 files changed

+126
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323

2424
- 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))
2525
- 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))
26+
- Fixed unnecessary semicolons being introduced at the end of statements when incorrectly determined as ambiguous ([#963](https://github.com/JohnnyMorganz/StyLua/issues/963))
2627

2728
## [2.0.2] - 2024-12-07
2829

src/formatters/block.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -473,18 +473,46 @@ fn var_has_parentheses(var: &Var) -> bool {
473473
}
474474
}
475475

476+
fn expression_ends_with_identifier_or_parentheses(expression: &Expression) -> bool {
477+
match expression {
478+
Expression::Parentheses { .. } => true,
479+
Expression::FunctionCall(_) => true,
480+
Expression::Var(_) => true,
481+
Expression::BinaryOperator { rhs, .. } => {
482+
expression_ends_with_identifier_or_parentheses(rhs)
483+
}
484+
Expression::UnaryOperator { expression, .. } => {
485+
expression_ends_with_identifier_or_parentheses(expression)
486+
}
487+
_ => false,
488+
}
489+
}
490+
491+
// Ambiguous syntax can only occur if the current statement ends with an identifier, function call, or parentheses
492+
fn stmt_ends_with_identifier_or_parentheses(stmt: &Stmt) -> bool {
493+
match stmt {
494+
Stmt::Assignment(assignment) => match assignment.expressions().last() {
495+
Some(pair) => expression_ends_with_identifier_or_parentheses(pair.value()),
496+
None => false,
497+
},
498+
Stmt::LocalAssignment(local_assignment) => match local_assignment.expressions().last() {
499+
Some(pair) => expression_ends_with_identifier_or_parentheses(pair.value()),
500+
None => false,
501+
},
502+
Stmt::FunctionCall(_) => true,
503+
Stmt::Repeat(repeat) => expression_ends_with_identifier_or_parentheses(repeat.until()),
504+
_ => false,
505+
}
506+
}
507+
476508
fn check_stmt_requires_semicolon(
477509
stmt: &Stmt,
478510
next_stmt: Option<&&(Stmt, Option<TokenReference>)>,
479511
) -> bool {
480512
// Need to check next statement if it is a function call, with a parameters expression as the prefix
481513
// If so, removing a semicolon may lead to ambiguous syntax
482-
// Ambiguous syntax can only occur if the current statement is a (Local)Assignment, FunctionCall or a Repeat block
483-
match stmt {
484-
Stmt::Assignment(_)
485-
| Stmt::LocalAssignment(_)
486-
| Stmt::FunctionCall(_)
487-
| Stmt::Repeat(_) => match next_stmt {
514+
stmt_ends_with_identifier_or_parentheses(stmt)
515+
&& match next_stmt {
488516
Some((Stmt::FunctionCall(function_call), _)) => match function_call.prefix() {
489517
Prefix::Expression(expression) => {
490518
matches!(&**expression, Expression::Parentheses { .. })
@@ -500,9 +528,7 @@ fn check_stmt_requires_semicolon(
500528
var_has_parentheses(compound_assignment.lhs())
501529
}
502530
_ => false,
503-
},
504-
_ => false,
505-
}
531+
}
506532
}
507533

508534
/// Formats a block node. Note: the given shape to the block formatter should already be at the correct indentation level
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- https://github.com/JohnnyMorganz/StyLua/issues/963
2+
local value = nil
3+
4+
(Foo):Call()
5+
6+
local tbl = {}
7+
8+
(Foo):Call()
9+
10+
local x = 1
11+
12+
(Foo):Call()
13+
14+
local x = "value"
15+
16+
(Foo):Call()
17+
18+
local x = function() end
19+
20+
(Foo):Call()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
local x = call "";
2+
(foo or bar and baz)(bar)
3+
4+
local x = call {};
5+
(foo or bar and baz)(bar)
6+
7+
local x = identifier;
8+
(foo or bar and baz)(bar)
9+
10+
local x = (identifier);
11+
(foo or bar and baz)(bar)
12+
13+
local x = x.y;
14+
(foo or bar and baz)(bar)
15+
16+
local x = x["y"];
17+
(foo or bar and baz)(bar)

tests/snapshots/[email protected]

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
source: tests/tests.rs
3+
expression: "format(&contents, LuaVersion::Lua51)"
4+
input_file: tests/inputs/ambiguous-syntax-2.lua
5+
snapshot_kind: text
6+
---
7+
-- https://github.com/JohnnyMorganz/StyLua/issues/963
8+
local value = nil
9+
10+
(Foo):Call()
11+
12+
local tbl = {}
13+
14+
(Foo):Call()
15+
16+
local x = 1
17+
18+
(Foo):Call()
19+
20+
local x = "value"
21+
22+
(Foo):Call()
23+
24+
local x = function() end
25+
26+
(Foo):Call()

tests/snapshots/[email protected]

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
source: tests/tests.rs
3+
expression: "format(&contents, LuaVersion::Lua51)"
4+
input_file: tests/inputs/ambiguous-syntax-3.lua
5+
snapshot_kind: text
6+
---
7+
local x = call("");
8+
(foo or bar and baz)(bar)
9+
10+
local x = call({});
11+
(foo or bar and baz)(bar)
12+
13+
local x = identifier;
14+
(foo or bar and baz)(bar)
15+
16+
local x = identifier;
17+
(foo or bar and baz)(bar)
18+
19+
local x = x.y;
20+
(foo or bar and baz)(bar)
21+
22+
local x = x["y"];
23+
(foo or bar and baz)(bar)

tests/snapshots/[email protected]

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
source: tests/tests.rs
3-
expression: format(&contents)
3+
expression: "format(&contents, LuaVersion::Lua51)"
4+
input_file: tests/inputs/excess-parentheses.lua
5+
snapshot_kind: text
46
---
57
local x
68
something(x)
@@ -11,7 +13,7 @@ local z = (...) == nil and foo or bar
1113
local foo = not (bar and baz)
1214
local bar = #bar and baz
1315
local cond = condition and (not object or object.Value == y)
14-
local baz = (-4 + 3) * 2;
16+
local baz = (-4 + 3) * 2
1517

1618
({}):foo();
1719
("hello"):format()
@@ -34,4 +36,3 @@ local y = "hello"
3436
local z = function()
3537
return true
3638
end
37-

0 commit comments

Comments
 (0)