Affects StyLua 2.5.2 (latest release); the relevant code is unchanged on current main. Found while adopting solver-V2 read syntax in a Roblox project.
Repro
Input:
type A = { read Service }
type B = { write Service }
Output (default config):
type A = { readService }
type B = { writeService }
The space after the access modifier is dropped, merging the two tokens into a single type reference readService — the output parses but means something different (and typically breaks analysis with an unknown-type error). --verify correctly refuses to write:
INTERNAL WARNING: Output AST may be different to input AST. Code correctness may have changed.
The equivalent property and indexer positions are unaffected: { read Name: string } and { read [number]: Service } both format correctly (the latter is a working workaround for the array shorthand).
Multiline variant
The multiline path misplaces indentation as well — the element type's leading indent lands between the modifier and the type, and the modifier gets no indent:
-- input
type C = {
-- keep
read Service
}
-- output
type C = {
-- keep
read Service
}
Likely cause
In src/formatters/luau.rs, the TypeInfo::Array arm formats the access token with format_token_reference only, never re-appending trailing trivia. The sibling TypeField access handling in format_type_field appends an explicit space:
.update_trailing_trivia(FormatTriviaType::Append(vec![Token::new(
TokenType::spaces(1),
)]))
(access_shape_increment in the Array arm already budgets len() + 1 for that space — it just isn't emitted.) For the multiline case, the leading indent trivia is attached to type_info even when an access token precedes it.
Affects StyLua 2.5.2 (latest release); the relevant code is unchanged on current
main. Found while adopting solver-V2readsyntax in a Roblox project.Repro
Input:
Output (default config):
The space after the access modifier is dropped, merging the two tokens into a single type reference
readService— the output parses but means something different (and typically breaks analysis with an unknown-type error).--verifycorrectly refuses to write:The equivalent property and indexer positions are unaffected:
{ read Name: string }and{ read [number]: Service }both format correctly (the latter is a working workaround for the array shorthand).Multiline variant
The multiline path misplaces indentation as well — the element type's leading indent lands between the modifier and the type, and the modifier gets no indent:
Likely cause
In
src/formatters/luau.rs, theTypeInfo::Arrayarm formats theaccesstoken withformat_token_referenceonly, never re-appending trailing trivia. The siblingTypeFieldaccess handling informat_type_fieldappends an explicit space:(
access_shape_incrementin the Array arm already budgetslen() + 1for that space — it just isn't emitted.) For the multiline case, the leading indent trivia is attached totype_infoeven when anaccesstoken precedes it.