mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
110 lines
4.1 KiB
Swift
110 lines
4.1 KiB
Swift
//
|
|
// RedundantSwiftTestingSuite.swift
|
|
// SwiftFormat
|
|
//
|
|
// Created by Cal Stephens on 2/18/26.
|
|
// Copyright © 2026 Nick Lockwood. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public extension FormatRule {
|
|
static let redundantSwiftTestingSuite = FormatRule(
|
|
help: "Remove redundant @Suite attribute with no arguments.",
|
|
orderAfter: [.swiftTestingTestCaseNames]
|
|
) { formatter in
|
|
guard formatter.hasImport("Testing") else { return }
|
|
|
|
formatter.forEach(.attribute) { attrIndex, token in
|
|
guard token.string == "@Suite" else { return }
|
|
|
|
// Check what comes after @Suite
|
|
guard let nextTokenIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: attrIndex)
|
|
else { return }
|
|
|
|
let nextToken = formatter.tokens[nextTokenIndex]
|
|
|
|
// If there's a parenthesis after @Suite, check if it has arguments
|
|
if nextToken == .startOfScope("(") {
|
|
guard let endOfScope = formatter.endOfScope(at: nextTokenIndex) else { return }
|
|
|
|
// Check if there are any non-whitespace/comment tokens between the parens
|
|
if let firstTokenInParens = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: nextTokenIndex),
|
|
firstTokenInParens < endOfScope
|
|
{
|
|
// Has arguments - keep it
|
|
return
|
|
}
|
|
}
|
|
|
|
// Remove the @Suite attribute
|
|
var attributeEndIndex = attrIndex
|
|
|
|
// Check if there are parentheses after @Suite
|
|
if let nextTokenIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: attrIndex),
|
|
formatter.tokens[nextTokenIndex] == .startOfScope("("),
|
|
let endOfScope = formatter.endOfScope(at: nextTokenIndex)
|
|
{
|
|
attributeEndIndex = endOfScope
|
|
}
|
|
|
|
var startIndex = attrIndex
|
|
var endIndex = attributeEndIndex
|
|
|
|
// Check if there's a leading space (between another attribute and this one)
|
|
let hasLeadingSpace = attrIndex > 0 && formatter.tokens[attrIndex - 1].isSpace
|
|
let leadingSpaceIsAfterAttribute = hasLeadingSpace && attrIndex > 1 && !formatter.tokens[attrIndex - 2].isLinebreak
|
|
|
|
let nextNonSpaceIndex = formatter.index(of: .nonSpace, after: attributeEndIndex)
|
|
let hasTrailingLinebreak = nextNonSpaceIndex != nil && formatter.tokens[nextNonSpaceIndex!].isLinebreak
|
|
let hasTrailingSpace = attributeEndIndex + 1 < formatter.tokens.count && formatter.tokens[attributeEndIndex + 1].isSpace
|
|
|
|
if leadingSpaceIsAfterAttribute {
|
|
// Remove the space before @Suite (space between attributes)
|
|
startIndex = attrIndex - 1
|
|
// Don't remove trailing linebreak - preserve the line structure
|
|
// Don't remove trailing space - it separates from the next token
|
|
} else if hasTrailingLinebreak, let nextIndex = nextNonSpaceIndex {
|
|
// @Suite is at the start of the line (possibly with indentation)
|
|
endIndex = nextIndex
|
|
// Also remove leading indentation
|
|
if hasLeadingSpace, attrIndex > 1, formatter.tokens[attrIndex - 2].isLinebreak {
|
|
startIndex = attrIndex - 1
|
|
}
|
|
} else if hasTrailingSpace {
|
|
endIndex = attributeEndIndex + 1
|
|
}
|
|
|
|
formatter.removeTokens(in: startIndex ... endIndex)
|
|
}
|
|
} examples: {
|
|
"""
|
|
```diff
|
|
import Testing
|
|
|
|
- @Suite
|
|
struct MyFeatureTests {
|
|
@Test func myFeature() {
|
|
#expect(true)
|
|
}
|
|
}
|
|
|
|
- @Suite()
|
|
struct OtherTests {
|
|
@Test func otherFeature() {
|
|
#expect(true)
|
|
}
|
|
}
|
|
|
|
// Not redundant - @Suite has arguments
|
|
@Suite(.serialized)
|
|
struct SerializedTests {
|
|
@Test func feature() {
|
|
#expect(true)
|
|
}
|
|
}
|
|
```
|
|
"""
|
|
}
|
|
}
|