Skip to content

Commit 6e3d50c

Browse files
authored
Fix issue where modifiersOrder rule confused async effect for async modifier (#2237)
1 parent b0ca57a commit 6e3d50c

File tree

8 files changed

+29
-22
lines changed

8 files changed

+29
-22
lines changed

Sources/ParsingHelpers.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,14 @@ extension Formatter {
389389

390390
/// Returns true if the token at specified index is a modifier
391391
func isModifier(at index: Int) -> Bool {
392-
guard let token = token(at: index), token.isModifierKeyword else {
392+
guard let token = token(at: index), token._isModifierKeyword else {
393393
return false
394394
}
395395

396396
if token == .keyword("class"),
397397
let nextToken = next(.nonSpaceOrCommentOrLinebreak, after: index)
398398
{
399-
return nextToken.isDeclarationTypeKeyword || nextToken.isModifierKeyword
399+
return nextToken.isDeclarationTypeKeyword || nextToken._isModifierKeyword
400400
}
401401

402402
// Async is only a valid modifier on local let/var declarations.
@@ -2536,7 +2536,7 @@ extension Formatter {
25362536
if nextToken.isDeclarationTypeKeyword {
25372537
return nextToken.string
25382538
}
2539-
guard nextToken.isModifierKeyword else {
2539+
guard nextToken._isModifierKeyword else {
25402540
break
25412541
}
25422542
nextIndex = i
@@ -3648,7 +3648,11 @@ extension Token {
36483648
.contains(keyword)
36493649
}
36503650

3651-
var isModifierKeyword: Bool {
3651+
/// Whether or not this token represents a potential modifier keyword.
3652+
/// This doesn't necessarily mean that the keyword is a modifier: some modifiers
3653+
/// like `class` and `async` are contextual.
3654+
/// In rule implementations, prefer using the `Formatter.isModifier(at:)` helper.
3655+
var _isModifierKeyword: Bool {
36523656
switch self {
36533657
case let .keyword(keyword), let .identifier(keyword):
36543658
return _FormatRules.allModifiers.contains(keyword)

Sources/Rules/BlankLineAfterImports.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public extension FormatRule {
2121
return
2222
}
2323
var keyword: Token = formatter.tokens[nextIndex]
24-
while keyword == .startOfScope("#if") || keyword.isModifierKeyword || keyword.isAttribute,
24+
while keyword == .startOfScope("#if") || formatter.isModifier(at: nextIndex) || keyword.isAttribute,
2525
let index = formatter.index(of: .keyword, after: nextIndex)
2626
{
2727
nextIndex = index

Sources/Rules/DocComments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ extension Formatter {
163163

164164
// Check if this token defines a declaration that supports doc comments
165165
var declarationToken = tokens[nextDeclarationIndex]
166-
if declarationToken.isAttribute || declarationToken.isModifierKeyword,
166+
if declarationToken.isAttribute || isModifier(at: nextDeclarationIndex),
167167
let index = index(after: nextDeclarationIndex, where: { $0.isDeclarationTypeKeyword })
168168
{
169169
declarationToken = tokens[index]

Sources/Rules/EnumNamespaces.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ public extension FormatRule {
1919
) { formatter in
2020
formatter.forEachToken(where: { [.keyword("class"), .keyword("struct")].contains($0) }) { i, token in
2121
if token == .keyword("class") {
22-
guard let next = formatter.next(.nonSpaceOrCommentOrLinebreak, after: i),
22+
guard let nextIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: i),
2323
// exit if structs only
2424
formatter.options.enumNamespaces != .structsOnly,
2525
// exit if class is a type modifier
26-
!(next.isKeywordOrAttribute || next.isModifierKeyword),
26+
!(formatter.tokens[nextIndex].isKeywordOrAttribute || formatter.isModifier(at: nextIndex)),
2727
// exit for class as protocol conformance
2828
formatter.last(.nonSpaceOrCommentOrLinebreak, before: i) != .delimiter(":"),
2929
// exit if not closed for extension

Sources/Rules/ModifierOrder.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@ public extension FormatRule {
1515
options: ["modifier-order"]
1616
) { formatter in
1717
formatter.forEach(.keyword) { i, token in
18-
switch token.string {
19-
case "let", "func", "var", "class", "actor", "extension", "init", "enum",
20-
"struct", "typealias", "subscript", "associatedtype", "protocol":
21-
break
22-
default:
23-
return
24-
}
18+
guard token.isDeclarationTypeKeyword else { return }
2519
var modifiers = [String: [Token]]()
2620
var lastModifier: (name: String, tokens: [Token])?
2721
func pushModifier() {
@@ -36,7 +30,7 @@ public extension FormatRule {
3630
lastModifier = nil
3731
lastIndex = previousIndex
3832
break loop
39-
case let token where token.isModifierKeyword:
33+
case let token where formatter.isModifier(at: index):
4034
pushModifier()
4135
lastModifier = (token.string, [Token](formatter.tokens[index ..< lastIndex]))
4236
previousIndex = lastIndex
@@ -45,7 +39,7 @@ public extension FormatRule {
4539
if case let .identifier(param)? = formatter.last(.nonSpaceOrCommentOrLinebreak, before: index),
4640
let openParenIndex = formatter.index(of: .startOfScope("("), before: index),
4741
let index = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: openParenIndex),
48-
let token = formatter.token(at: index), token.isModifierKeyword
42+
let token = formatter.token(at: index), formatter.isModifier(at: index)
4943
{
5044
pushModifier()
5145
let modifier = token.string + (param == "set" ? "(set)" : "")

Sources/Rules/RedundantObjc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public extension FormatRule {
4343
nextIndex = endIndex
4444
}
4545
case let token:
46-
guard token.isModifierKeyword else {
46+
guard formatter.isModifier(at: nextIndex) else {
4747
break loop
4848
}
4949
}

Sources/Rules/WrapAttributes.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ public extension FormatRule {
1717
formatter.forEach(.attribute) { i, _ in
1818
// Ignore sequential attributes
1919
guard let endIndex = formatter.endOfAttribute(at: i),
20-
var keywordIndex = formatter.index(
21-
of: .nonSpaceOrCommentOrLinebreak,
22-
after: endIndex, if: { $0.isKeyword || $0.isModifierKeyword }
23-
)
20+
var keywordIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: endIndex),
21+
formatter.tokens[keywordIndex].isKeyword || formatter.isModifier(at: keywordIndex)
2422
else {
2523
return
2624
}

Tests/Rules/ModifierOrderTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,15 @@ final class ModifierOrderTests: XCTestCase {
143143
"""
144144
testFormatting(for: input, rule: .modifierOrder)
145145
}
146+
147+
func testAsyncFunctionBeforeNonisolatedVar() {
148+
let input = """
149+
protocol Test: Actor {
150+
func test() async
151+
nonisolated var test2: String
152+
}
153+
"""
154+
155+
testFormatting(for: input, rule: .modifierOrder)
156+
}
146157
}

0 commit comments

Comments
 (0)