Skip to content

Commit e85b8fe

Browse files
committed
Fix bug test helper functions mistaken for tests
1 parent d5cd168 commit e85b8fe

9 files changed

+68
-48
lines changed

Sources/ParsingHelpers.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3259,6 +3259,27 @@ extension Formatter {
32593259
parseFunctionCallArguments(startOfScope: startOfScope)
32603260
}
32613261

3262+
/// Is this a test function?
3263+
func isTestFunction(
3264+
at funcKeywordIndex: Int,
3265+
in functionDecl: FunctionDeclaration,
3266+
for testingFramework: TestingFramework
3267+
) -> Bool {
3268+
assert(token(at: funcKeywordIndex) == .keyword("func"))
3269+
switch testingFramework {
3270+
case .xcTest:
3271+
guard functionDecl.name?.starts(with: "test") == true,
3272+
functionDecl.returnType == nil,
3273+
functionDecl.arguments.isEmpty
3274+
else {
3275+
return false
3276+
}
3277+
return true
3278+
case .swiftTesting:
3279+
return modifiersForDeclaration(at: funcKeywordIndex, contains: "@Test")
3280+
}
3281+
}
3282+
32623283
/// Parses the list of conformances on this type, starting at
32633284
/// the index of the type keyword (`struct`, `class`, `extension`, etc).
32643285
func parseConformancesOfType(atKeywordIndex keywordIndex: Int) -> [(conformance: TypeName, index: Int)] {

Sources/Rules/NoForceTryInTests.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,10 @@ public extension FormatRule {
1414

1515
formatter.forEach(.keyword("func")) { funcKeywordIndex, _ in
1616
guard let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: funcKeywordIndex),
17-
functionDecl.returnType == nil
17+
formatter.isTestFunction(at: funcKeywordIndex, in: functionDecl, for: testFramework),
18+
let bodyRange = functionDecl.bodyRange
1819
else { return }
1920

20-
switch testFramework {
21-
case .xcTest:
22-
guard functionDecl.name?.starts(with: "test") == true else { return }
23-
case .swiftTesting:
24-
guard formatter.modifiersForDeclaration(at: funcKeywordIndex, contains: "@Test") else { return }
25-
}
26-
27-
guard let bodyRange = functionDecl.bodyRange else { return }
28-
2921
// Find all `try!` and remove the `!`
3022
var foundAnyTryExclamationMarks = false
3123
for index in bodyRange.reversed() {

Sources/Rules/NoForceUnwrapInTests.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,9 @@ public extension FormatRule {
1717
var testCases = [AutoUpdatingIndex]()
1818
formatter.forEach(.keyword("func")) { funcKeywordIndex, _ in
1919
guard let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: funcKeywordIndex),
20-
functionDecl.returnType == nil
20+
formatter.isTestFunction(at: funcKeywordIndex, in: functionDecl, for: testFramework)
2121
else { return }
2222

23-
switch testFramework {
24-
case .xcTest:
25-
guard functionDecl.name?.starts(with: "test") == true else { return }
26-
case .swiftTesting:
27-
guard formatter.modifiersForDeclaration(at: funcKeywordIndex, contains: "@Test") else { return }
28-
}
29-
3023
testCases.append(funcKeywordIndex.autoUpdating(in: formatter))
3124
}
3225

Sources/Rules/NoGuardInTests.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,11 @@ public extension FormatRule {
2222
}
2323

2424
formatter.forEach(.keyword("func")) { funcKeywordIndex, _ in
25-
guard let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: funcKeywordIndex)
25+
guard let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: funcKeywordIndex),
26+
formatter.isTestFunction(at: funcKeywordIndex, in: functionDecl, for: testFramework),
27+
let bodyRange = functionDecl.bodyRange
2628
else { return }
2729

28-
switch testFramework {
29-
case .xcTest:
30-
guard functionDecl.name?.starts(with: "test") == true else { return }
31-
case .swiftTesting:
32-
guard formatter.modifiersForDeclaration(at: funcKeywordIndex, contains: "@Test") else { return }
33-
}
34-
35-
guard let bodyRange = functionDecl.bodyRange else { return }
36-
3730
// Track if we made any changes that require adding throws
3831
var addedTryStatement = false
3932

Sources/Rules/RedundantAsync.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public extension FormatRule {
2020
}
2121

2222
formatter.forEach(.keyword) { keywordIndex, keyword in
23-
guard ["func", "init", "subscript"].contains(keyword.string),
23+
guard case let .keyword(keyword) = keyword,
24+
["func", "init", "subscript"].contains(keyword),
2425
let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: keywordIndex),
2526
functionDecl.effects.contains(where: { $0.hasPrefix("async") }),
2627
let bodyRange = functionDecl.bodyRange
@@ -33,14 +34,9 @@ public extension FormatRule {
3334

3435
if formatter.options.redundantAsync == .testsOnly {
3536
// Only process test functions
36-
guard let testFramework, functionDecl.returnType == nil else { return }
37-
38-
switch testFramework {
39-
case .xcTest:
40-
guard functionDecl.name?.starts(with: "test") == true else { return }
41-
case .swiftTesting:
42-
guard formatter.modifiersForDeclaration(at: keywordIndex, contains: "@Test") else { return }
43-
}
37+
guard keyword == "func", let testFramework,
38+
formatter.isTestFunction(at: keywordIndex, in: functionDecl, for: testFramework)
39+
else { return }
4440
}
4541

4642
// Check if the function body contains any await keywords

Sources/Rules/RedundantThrows.swift

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public extension FormatRule {
2121
}
2222

2323
formatter.forEach(.keyword) { keywordIndex, keyword in
24-
guard ["func", "init", "subscript"].contains(keyword.string),
24+
guard case let .keyword(keyword) = keyword, ["func", "init", "subscript"].contains(keyword),
2525
let functionDecl = formatter.parseFunctionDeclaration(keywordIndex: keywordIndex),
2626
functionDecl.effects.contains(where: { $0.hasPrefix("throws") }),
2727
let bodyRange = functionDecl.bodyRange
@@ -34,14 +34,9 @@ public extension FormatRule {
3434

3535
if formatter.options.redundantThrows == .testsOnly {
3636
// Only process test functions
37-
guard let testFramework, functionDecl.returnType == nil else { return }
38-
39-
switch testFramework {
40-
case .xcTest:
41-
guard functionDecl.name?.starts(with: "test") == true else { return }
42-
case .swiftTesting:
43-
guard formatter.modifiersForDeclaration(at: keywordIndex, contains: "@Test") else { return }
44-
}
37+
guard keyword == "func", let testFramework,
38+
formatter.isTestFunction(at: keywordIndex, in: functionDecl, for: testFramework)
39+
else { return }
4540
}
4641

4742
// Check if the function body contains any try keywords (excluding try! and try?) or throw statements

Tests/Rules/NoForceTryInTestsTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ final class NoForceTryInTestsTests: XCTestCase {
6161
testFormatting(for: input, output, rule: .noForceTryInTests)
6262
}
6363

64+
func testTestHelperIsNotUpdated_for_XCTest() {
65+
let input = """
66+
import XCTest
67+
68+
class TestCase: XCTestCase {
69+
func testHelper(arg: Bool) {
70+
try! somethingThatThrows(with: arg)
71+
}
72+
}
73+
"""
74+
testFormatting(for: input, rule: .noForceTryInTests)
75+
}
76+
6477
func test_nonTestCaseFunction_IsNotUpdated_for_XCTest() {
6578
let input = """
6679
import XCTest

Tests/Rules/NoForceUnwrapInTestsTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,19 @@ final class NoForceUnwrapInTestsTests: XCTestCase {
620620
testFormatting(for: input, output, rule: .noForceUnwrapInTests, exclude: [.hoistTry])
621621
}
622622

623+
func testTestHelperIsNotUpdated() {
624+
let input = """
625+
import XCTest
626+
627+
class TestCase: XCTestCase {
628+
func test_helper(arg: Bool) {
629+
let result = myOptional!.with.nested!.property! && arg
630+
}
631+
}
632+
"""
633+
testFormatting(for: input, rule: .noForceUnwrapInTests, exclude: [.hoistTry])
634+
}
635+
623636
func testDisableRule() {
624637
let input = """
625638
import XCTest

Tests/Rules/NoGuardInTestsTests.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,9 @@ final class NoGuardInTestsTests: XCTestCase {
866866
import XCTest
867867
868868
class TestCase: XCTestCase {
869-
func test_something(optionalValue: String?) {
869+
var optionalValue: String?
870+
871+
func test_something() {
870872
guard let optionalValue else {
871873
XCTFail()
872874
return
@@ -879,7 +881,9 @@ final class NoGuardInTestsTests: XCTestCase {
879881
import XCTest
880882
881883
class TestCase: XCTestCase {
882-
func test_something(optionalValue: String?) throws {
884+
var optionalValue: String?
885+
886+
func test_something() throws {
883887
let optionalValue = try XCTUnwrap(optionalValue)
884888
print(optionalValue)
885889
}

0 commit comments

Comments
 (0)