Skip to content

Commit 8b22575

Browse files
Target first attribute at_sign in stmt_remove_leading_newlines for LocalFunction/ConstFunction (#1115)
* fix: target first attribute at_sign in stmt_remove_leading_newlines for LocalFunction/ConstFunction When a LocalFunction or ConstFunction has a leading Luau attribute (e.g. @Native), the actual first token of the statement is the attribute's at_sign, not the local/const keyword. stmt_remove_leading_newlines was targeting the wrong token, leaving stray newlines before the attribute at the start of a block. Fixes #1109 * simplify: extract strip_attribute_leading_newlines helper, avoid allocation on no-attribute path - Deduplicate the identical 6-line attribute-stripping logic from LocalFunction and ConstFunction arms into a single helper function - Use peekable iterator so no Vec is allocated when the function has no attributes (the common case) - Make both arms use a consistent if-let pattern instead of early-return vs if/else - Remove redundant comment from test input file * refactor: use next() to own first attribute, eliminating index and clone Replace peek()+collect()+[0]+clone() with cloned().next()? which takes the first element as an owned value so with_at_sign can consume it directly. * style: rustfmt * docs: add changelog entry for #1109
1 parent dfc5d9e commit 8b22575

4 files changed

Lines changed: 71 additions & 12 deletions

File tree

CHANGELOG.md

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

1616
- Fixed npm publishing by bumping Node.js from 16 to 22 in CI workflows to support npm trusted publishing
1717
- Luau: Fixed union/intersection type definitions not being hung when the type alone fits within the column width but the full line (including ` = `) exceeds it ([#1104](https://github.com/JohnnyMorganz/StyLua/issues/1104))
18+
- Luau: Fixed stray leading newlines not being removed from `local function` and `const function` declarations that have attributes (e.g. `@native`) at the start of a block ([#1109](https://github.com/JohnnyMorganz/StyLua/issues/1109))
1819

1920
## [2.4.1] - 2026-04-06
2021

src/formatters/block.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use crate::{
1818
},
1919
shape::Shape,
2020
};
21+
#[cfg(feature = "luau")]
22+
use full_moon::ast::luau::LuauAttribute;
2123
use full_moon::ast::{
2224
punctuated::Punctuated, Block, Expression, LastStmt, Prefix, Return, Stmt, Var,
2325
};
@@ -317,6 +319,20 @@ fn var_remove_leading_newline(var: Var) -> Var {
317319
}
318320
}
319321

322+
#[cfg(feature = "luau")]
323+
fn strip_attribute_leading_newlines<'a>(
324+
attributes: impl Iterator<Item = &'a LuauAttribute>,
325+
) -> Option<Vec<LuauAttribute>> {
326+
let mut cloned = attributes.cloned();
327+
let first = cloned.next()?;
328+
let at_sign = first.at_sign();
329+
let leading_trivia = trivia_remove_leading_newlines(at_sign.leading_trivia().collect());
330+
let new_at_sign = at_sign.update_leading_trivia(FormatTriviaType::Replace(leading_trivia));
331+
let mut result = vec![first.with_at_sign(new_at_sign)];
332+
result.extend(cloned);
333+
Some(result)
334+
}
335+
320336
fn stmt_remove_leading_newlines(stmt: Stmt) -> Stmt {
321337
match stmt {
322338
Stmt::Assignment(assignment) => {
@@ -363,12 +379,19 @@ fn stmt_remove_leading_newlines(stmt: Stmt) -> Stmt {
363379
local_assignment.local_token(),
364380
with_local_token
365381
),
366-
Stmt::LocalFunction(local_function) => update_first_token!(
367-
LocalFunction,
368-
local_function,
369-
local_function.local_token(),
370-
with_local_token
371-
),
382+
Stmt::LocalFunction(local_function) => {
383+
#[cfg(feature = "luau")]
384+
if let Some(attributes) = strip_attribute_leading_newlines(local_function.attributes())
385+
{
386+
return Stmt::LocalFunction(local_function.with_attributes(attributes));
387+
}
388+
update_first_token!(
389+
LocalFunction,
390+
local_function,
391+
local_function.local_token(),
392+
with_local_token
393+
)
394+
}
372395
Stmt::NumericFor(numeric_for) => update_first_token!(
373396
NumericFor,
374397
numeric_for,
@@ -404,12 +427,19 @@ fn stmt_remove_leading_newlines(stmt: Stmt) -> Stmt {
404427
with_const_token
405428
),
406429
#[cfg(feature = "luau")]
407-
Stmt::ConstFunction(const_function) => update_first_token!(
408-
ConstFunction,
409-
const_function,
410-
const_function.const_token(),
411-
with_const_token
412-
),
430+
Stmt::ConstFunction(const_function) => {
431+
if let Some(attributes) = strip_attribute_leading_newlines(const_function.attributes())
432+
{
433+
Stmt::ConstFunction(const_function.with_attributes(attributes))
434+
} else {
435+
update_first_token!(
436+
ConstFunction,
437+
const_function,
438+
const_function.const_token(),
439+
with_const_token
440+
)
441+
}
442+
}
413443

414444
#[cfg(feature = "luau")]
415445
Stmt::ExportedTypeDeclaration(exported_type_declaration) => update_first_token!(

tests/inputs-luau/attributes-4.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
do
2+
3+
@native
4+
local function foo()
5+
end
6+
end
7+
8+
do
9+
10+
@native
11+
const function bar()
12+
end
13+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
source: tests/tests.rs
3+
assertion_line: 36
4+
expression: "format(&contents, LuaVersion::Luau)"
5+
input_file: tests/inputs-luau/attributes-4.lua
6+
---
7+
do
8+
@native
9+
local function foo() end
10+
end
11+
12+
do
13+
@native
14+
const function bar() end
15+
end

0 commit comments

Comments
 (0)