Update SwiftSyntax to latest development snapshot (#4759)

* Merge `spacedBinaryOperator` and `unspacedBinaryOperator`

* New contextual keyword enums

* Update for removed `UnavailabilityConditionSyntax`

* Handle how attributes are now defined

This partially reverts commit 325d0ee1e4.

* Handle removal of `TokenListSyntax`

* Update `Package.swift`

* Extract some SwiftSyntax helpers

* Update to `0.50900.0-swift-DEVELOPMENT-SNAPSHOT-2023-02-06-a`

* Skip attributes with keypath arguments in `attributes` rule

To preserve the rule's existing behavior.

* Limit unowned_variable_capture violations to capture lists

* Add changelog entries
This commit is contained in:
JP Simard 2023-02-20 10:51:31 -05:00 committed by GitHub
parent 393318d903
commit 2f0e537f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 452 additions and 475 deletions

View File

@ -73,6 +73,16 @@
[#3399](https://github.com/realm/SwiftLint/issues/3399)
[#3605](https://github.com/realm/SwiftLint/issues/3605)
* Speed up linting by up to 6% updating to use a newer version of
`SwiftSyntax`.
[JP Simard](https://github.com/jpsim)
* Catch more valid `legacy_multiple` violations.
[JP Simard](https://github.com/jpsim)
* Catch more valid `no_magic_numbers` violations.
[JP Simard](https://github.com/jpsim)
#### Bug Fixes
* Report violations in all `<scope>_length` rules when the error threshold is
@ -125,6 +135,10 @@
[Mathias Schreck](https://github.com/lo1tuma)
[#4772](https://github.com/realm/SwiftLint/issues/4772)
* Fix false positives in `attributes` rule when using property wrappers
with keypath arguments.
[JP Simard](https://github.com/jpsim)
## 0.50.3: Bundle of Towels
#### Breaking

View File

@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "edd2d0cdb988ac45e2515e0dd0624e4a6de54a94",
"version" : "0.50800.0-SNAPSHOT-2022-12-29-a"
"revision" : "013a48e2312e57b7b355db25bd3ea75282ebf274",
"version" : "0.50900.0-swift-DEVELOPMENT-SNAPSHOT-2023-02-06-a"
}
},
{

View File

@ -30,7 +30,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.1")),
.package(url: "https://github.com/apple/swift-syntax.git", exact: "0.50800.0-SNAPSHOT-2022-12-29-a"),
.package(url: "https://github.com/apple/swift-syntax.git", exact: "0.50900.0-swift-DEVELOPMENT-SNAPSHOT-2023-02-06-a"),
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.34.0")),
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.4"),
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),

View File

@ -44,7 +44,9 @@ extension SwiftLintFile {
.tokens(viewMode: .sourceAccurate)
.reduce(into: []) { linesWithTokens, token in
if case .stringSegment = token.tokenKind {
let sourceRange = token.withoutTrivia().sourceRange(converter: locationConverter)
let sourceRange = token
.trimmed
.sourceRange(converter: locationConverter)
let startLine = sourceRange.start.line!
let endLine = sourceRange.end.line!
linesWithTokens.formUnion(startLine...endLine)

View File

@ -8,7 +8,7 @@ extension SyntaxVisitor: SwiftLintSyntaxVisitor {}
extension SwiftLintSyntaxVisitor {
func walk<T, SyntaxType: SyntaxProtocol>(tree: SyntaxType, handler: (Self) -> T) -> T {
#if DEBUG
#if DEBUG
// workaround for stack overflow when running in debug
// https://bugs.swift.org/browse/SR-11170
let lock = NSLock()
@ -31,10 +31,10 @@ extension SwiftLintSyntaxVisitor {
}
return handler(self)
#else
#else
walk(tree)
return handler(self)
#endif
#endif
}
func walk<T>(file: SwiftLintFile, handler: (Self) -> [T]) -> [T] {
@ -107,19 +107,17 @@ extension StringLiteralExprSyntax {
extension TokenKind {
var isEqualityComparison: Bool {
self == .spacedBinaryOperator("==") ||
self == .spacedBinaryOperator("!=") ||
self == .unspacedBinaryOperator("==")
self == .binaryOperator("==") || self == .binaryOperator("!=")
}
}
extension ModifierListSyntax? {
var containsLazy: Bool {
contains(tokenKind: .contextualKeyword("lazy"))
contains(tokenKind: .keyword(.lazy))
}
var containsOverride: Bool {
contains(tokenKind: .contextualKeyword("override"))
contains(tokenKind: .keyword(.override))
}
var containsStaticOrClass: Bool {
@ -127,15 +125,15 @@ extension ModifierListSyntax? {
}
var isStatic: Bool {
contains(tokenKind: .staticKeyword)
contains(tokenKind: .keyword(.static))
}
var isClass: Bool {
contains(tokenKind: .classKeyword)
contains(tokenKind: .keyword(.class))
}
var isFileprivate: Bool {
contains(tokenKind: .fileprivateKeyword)
contains(tokenKind: .keyword(.fileprivate))
}
var isPrivateOrFileprivate: Bool {
@ -144,13 +142,13 @@ extension ModifierListSyntax? {
}
return modifiers.contains { elem in
(elem.name.tokenKind == .privateKeyword || elem.name.tokenKind == .fileprivateKeyword) &&
(elem.name.tokenKind == .keyword(.private) || elem.name.tokenKind == .keyword(.fileprivate)) &&
elem.detail == nil
}
}
var isFinal: Bool {
contains(tokenKind: .contextualKeyword("final"))
contains(tokenKind: .keyword(.final))
}
private func contains(tokenKind: TokenKind) -> Bool {
@ -162,17 +160,34 @@ extension ModifierListSyntax? {
}
}
extension AttributeSyntax {
var attributeNameText: String {
attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text ??
attributeName.description
}
}
extension AttributeListSyntax? {
func contains(attributeNamed attributeName: String) -> Bool {
self?.contains { $0.as(AttributeSyntax.self)?.attributeNameText == attributeName } == true
}
}
extension TokenKind {
var isUnavailableKeyword: Bool {
self == .keyword(.unavailable) || self == .identifier("unavailable")
}
}
extension VariableDeclSyntax {
var isIBOutlet: Bool {
attributes?.contains { attr in
attr.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("IBOutlet")
} ?? false
attributes.contains(attributeNamed: "IBOutlet")
}
var weakOrUnownedModifier: DeclModifierSyntax? {
modifiers?.first { decl in
decl.name.tokenKind == .contextualKeyword("weak") ||
decl.name.tokenKind == .contextualKeyword("unowned")
decl.name.tokenKind == .keyword(.weak) ||
decl.name.tokenKind == .keyword(.unowned)
}
}
@ -207,9 +222,7 @@ public extension EnumDeclSyntax {
extension FunctionDeclSyntax {
var isIBAction: Bool {
attributes?.contains { attr in
attr.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("IBAction")
} ?? false
attributes.contains(attributeNamed: "IBAction")
}
/// Returns the signature including arguments, e.g "setEditing(_:animated:)"
@ -241,13 +254,13 @@ extension FunctionDeclSyntax {
extension AccessorBlockSyntax {
var getAccessor: AccessorDeclSyntax? {
accessors.first { accessor in
accessor.accessorKind.tokenKind == .contextualKeyword("get")
accessor.accessorKind.tokenKind == .keyword(.get)
}
}
var setAccessor: AccessorDeclSyntax? {
accessors.first { accessor in
accessor.accessorKind.tokenKind == .contextualKeyword("set")
accessor.accessorKind.tokenKind == .keyword(.set)
}
}

View File

@ -62,7 +62,7 @@ enum LegacyFunctionRuleHelper {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let trimmedArguments = node.argumentList.map { $0.trimmed() }
let trimmedArguments = node.argumentList.map { $0.trimmingTrailingComma() }
let rewriteStrategy = legacyFunctions[funcName]
let expr: ExprSyntax
@ -82,8 +82,8 @@ enum LegacyFunctionRuleHelper {
}
return expr
.withLeadingTrivia(node.leadingTrivia ?? .zero)
.withTrailingTrivia(node.trailingTrivia ?? .zero)
.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
.with(\.trailingTrivia, node.trailingTrivia ?? .zero)
}
}
}
@ -103,10 +103,7 @@ private extension FunctionCallExprSyntax {
}
private extension TupleExprElementSyntax {
func trimmed() -> TupleExprElementSyntax {
self
.withoutTrivia()
.withTrailingComma(nil)
.withoutTrivia()
func trimmingTrailingComma() -> TupleExprElementSyntax {
self.trimmed.with(\.trailingComma, nil).trimmed
}
}

View File

@ -53,7 +53,7 @@ private extension BlockBasedKVORule {
}
let types = parameterList
.compactMap { $0.type?.withoutTrivia().description.replacingOccurrences(of: " ", with: "") }
.compactMap { $0.type?.trimmedDescription.replacingOccurrences(of: " ", with: "") }
let firstTypes = ["String?", "Any?", "[NSKeyValueChangeKey:Any]?", "UnsafeMutableRawPointer?"]
let secondTypes = ["String?", "Any?", "Dictionary<NSKeyValueChangeKey,Any>?", "UnsafeMutableRawPointer?"]
if types == firstTypes || types == secondTypes {

View File

@ -198,15 +198,11 @@ private extension TypeInheritanceClauseSyntax? {
private extension AttributeListSyntax? {
var containsObjcMembers: Bool {
self?.contains { elem in
elem.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("objcMembers")
} ?? false
contains(attributeNamed: "objcMembers")
}
var containsObjc: Bool {
self?.contains { elem in
elem.as(AttributeSyntax.self)?.attributeName.tokenKind == .contextualKeyword("objc")
} ?? false
contains(attributeNamed: "objc")
}
}
@ -222,8 +218,8 @@ private extension AttributeListSyntax? {
return false
}
return attr.attributeName.tokenKind == .contextualKeyword("available") && arguments.contains { arg in
arg.entry.as(TokenSyntax.self)?.tokenKind == .contextualKeyword("unavailable")
return attr.attributeNameText == "available" && arguments.contains { arg in
arg.entry.as(TokenSyntax.self)?.tokenKind.isUnavailableKeyword == true
}
}
}

View File

@ -39,11 +39,11 @@ struct DiscouragedAssertRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderR
private extension DiscouragedAssertRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: FunctionCallExprSyntax) {
guard node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.withoutTrivia().text == "assert",
guard node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.text == "assert",
let firstArg = node.argumentList.first,
firstArg.label == nil,
let boolExpr = firstArg.expression.as(BooleanLiteralExprSyntax.self),
boolExpr.booleanLiteral.tokenKind == .falseKeyword else {
boolExpr.booleanLiteral.tokenKind == .keyword(.false) else {
return
}

View File

@ -218,7 +218,8 @@ private extension ExplicitInitRule {
}
correctionPositions.append(violationPosition)
return super.visit(node.withCalledExpression("\(calledBase.withoutTrivia())"))
let newNode = node.with(\.calledExpression, calledBase.trimmed)
return super.visit(newNode)
}
}
}

View File

@ -108,11 +108,11 @@ private extension ExplicitTopLevelACLRule {
private extension DeclModifierSyntax {
var isACLModifier: Bool {
let aclModifiers: Set<TokenKind> = [
.privateKeyword,
.fileprivateKeyword,
.internalKeyword,
.publicKeyword,
.contextualKeyword("open")
.keyword(.private),
.keyword(.fileprivate),
.keyword(.internal),
.keyword(.public),
.keyword(.open)
]
return detail == nil && aclModifiers.contains(name.tokenKind)

View File

@ -132,7 +132,7 @@ private extension InitializerClauseSyntax {
}
var isTypeReference: Bool {
value.as(MemberAccessExprSyntax.self)?.name.tokenKind == .selfKeyword
value.as(MemberAccessExprSyntax.self)?.name.tokenKind == .keyword(.self)
}
}

View File

@ -45,7 +45,7 @@ private extension FatalErrorMessageRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: FunctionCallExprSyntax) {
guard let expression = node.calledExpression.as(IdentifierExprSyntax.self),
expression.identifier.withoutTrivia().text == "fatalError",
expression.identifier.text == "fatalError",
node.argumentList.isEmptyOrEmptyString else {
return
}

View File

@ -87,6 +87,6 @@ private class TypeNameCollectingVisitor: SyntaxVisitor {
}
override func visitPost(_ node: ExtensionDeclSyntax) {
names.insert(node.extendedType.withoutTrivia().description)
names.insert(node.extendedType.trimmedDescription)
}
}

View File

@ -134,26 +134,26 @@ private extension ForWhereRule {
override func visitPost(_ node: ForInStmtSyntax) {
guard node.whereClause == nil,
case let statements = node.body.statements,
let ifStatement = statements.onlyElement?.item.as(IfStmtSyntax.self),
ifStatement.elseBody == nil,
!ifStatement.containsOptionalBinding,
!ifStatement.containsPatternCondition,
let condition = ifStatement.conditions.onlyElement,
let onlyExprStmt = node.body.statements.onlyElement?.item.as(ExpressionStmtSyntax.self),
let ifExpr = onlyExprStmt.expression.as(IfExprSyntax.self),
ifExpr.elseBody == nil,
!ifExpr.containsOptionalBinding,
!ifExpr.containsPatternCondition,
let condition = ifExpr.conditions.onlyElement,
!condition.containsMultipleConditions else {
return
}
if allowForAsFilter, ifStatement.containsReturnStatement {
if allowForAsFilter, ifExpr.containsReturnStatement {
return
}
violations.append(ifStatement.positionAfterSkippingLeadingTrivia)
violations.append(ifExpr.positionAfterSkippingLeadingTrivia)
}
}
}
private extension IfStmtSyntax {
private extension IfExprSyntax {
var containsOptionalBinding: Bool {
conditions.contains { element in
element.condition.is(OptionalBindingConditionSyntax.self)

View File

@ -129,8 +129,6 @@ private extension FunctionParameterSyntax {
return false
}
return attrType.attributes?.contains { attr in
attr.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("escaping")
} ?? false
return attrType.attributes.contains(attributeNamed: "escaping")
}
}

View File

@ -76,7 +76,7 @@ private extension JoinedDefaultParameterRule {
}
correctionPositions.append(violationPosition)
let newNode = node.withArgumentList(nil)
let newNode = node.with(\.argumentList, [])
return super.visit(newNode)
}
}

View File

@ -63,8 +63,8 @@ private extension LegacyConstantRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
return ("\(raw: correction)" as ExprSyntax)
.withLeadingTrivia(node.leadingTrivia ?? .zero)
.withTrailingTrivia(node.trailingTrivia ?? .zero)
.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
.with(\.trailingTrivia, node.trailingTrivia ?? .zero)
}
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
@ -78,8 +78,8 @@ private extension LegacyConstantRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
return ("\(raw: calledExpression.identifier.text).pi" as ExprSyntax)
.withLeadingTrivia(node.leadingTrivia ?? .zero)
.withTrailingTrivia(node.trailingTrivia ?? .zero)
.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
.with(\.trailingTrivia, node.trailingTrivia ?? .zero)
}
}
}

View File

@ -140,7 +140,7 @@ private extension LegacyConstructorRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: FunctionCallExprSyntax) {
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
constructorsToCorrectedNames[identifierExpr.identifier.withoutTrivia().text] != nil {
constructorsToCorrectedNames[identifierExpr.identifier.text] != nil {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
@ -158,7 +158,7 @@ private extension LegacyConstructorRule {
override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
guard let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
case let identifier = identifierExpr.identifier.withoutTrivia().text,
case let identifier = identifierExpr.identifier.text,
let correctedName = constructorsToCorrectedNames[identifier],
let args = constructorsToArguments[identifier],
!node.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) else {
@ -169,10 +169,11 @@ private extension LegacyConstructorRule {
let arguments = TupleExprElementListSyntax(node.argumentList.map { elem in
elem
.withLabel(.identifier(args[elem.indexInParent]))
.withColon(.colonToken(trailingTrivia: .space))
.with(\.label, .identifier(args[elem.indexInParent]))
.with(\.colon, .colonToken(trailingTrivia: .space))
})
let newExpression = identifierExpr.withIdentifier(
let newExpression = identifierExpr.with(
\.identifier,
.identifier(
correctedName,
leadingTrivia: identifierExpr.identifier.leadingTrivia,
@ -180,8 +181,8 @@ private extension LegacyConstructorRule {
)
)
let newNode = node
.withCalledExpression(ExprSyntax(newExpression))
.withArgumentList(arguments)
.with(\.calledExpression, ExprSyntax(newExpression))
.with(\.argumentList, arguments)
return super.visit(newNode)
}
}

View File

@ -85,7 +85,7 @@ extension LegacyHashingRule {
override func visitPost(_ node: VariableDeclSyntax) {
guard
node.parent?.is(MemberDeclListItemSyntax.self) == true,
node.letOrVarKeyword.tokenKind == .varKeyword,
node.letOrVarKeyword.tokenKind == .keyword(.var),
let binding = node.bindings.onlyElement,
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
identifier.identifier.text == "hashValue",

View File

@ -52,7 +52,7 @@ private extension LegacyMultipleRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: InfixOperatorExprSyntax) {
guard let operatorNode = node.operatorOperand.as(BinaryOperatorExprSyntax.self),
operatorNode.operatorToken.tokenKind == .spacedBinaryOperator("%"),
operatorNode.operatorToken.tokenKind == .binaryOperator("%"),
let parent = node.parent?.as(InfixOperatorExprSyntax.self),
let parentOperatorNode = parent.operatorOperand.as(BinaryOperatorExprSyntax.self),
parentOperatorNode.isEqualityOrInequalityOperator else {
@ -80,8 +80,7 @@ private extension LegacyMultipleRule {
private extension BinaryOperatorExprSyntax {
var isEqualityOrInequalityOperator: Bool {
operatorToken.tokenKind == .spacedBinaryOperator("==") ||
operatorToken.tokenKind == .unspacedBinaryOperator("==") ||
operatorToken.tokenKind == .spacedBinaryOperator("!=")
operatorToken.tokenKind == .binaryOperator("==") ||
operatorToken.tokenKind == .binaryOperator("!=")
}
}

View File

@ -36,7 +36,7 @@ private extension LegacyRandomRule {
]
override func visitPost(_ node: FunctionCallExprSyntax) {
if let function = node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.withoutTrivia().text,
if let function = node.calledExpression.as(IdentifierExprSyntax.self)?.identifier.text,
Self.legacyRandomFunctions.contains(function) {
violations.append(node.positionAfterSkippingLeadingTrivia)
}

View File

@ -54,7 +54,7 @@ private extension ObjectLiteralRule {
return
}
let name = node.calledExpression.withoutTrivia().description
let name = node.calledExpression.trimmedDescription
if validateImageLiteral, isImageNamedInit(node: node, name: name) {
violations.append(node.positionAfterSkippingLeadingTrivia)
} else if validateColorLiteral, isColorInit(node: node, name: name) {

View File

@ -76,11 +76,12 @@ private extension PreferZeroOverExplicitInitRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let newNode: MemberAccessExprSyntax = "\(raw: name).zero"
let newNode = MemberAccessExprSyntax(name: "zero")
.with(\.base, "\(raw: name)")
return super.visit(
newNode
.withLeadingTrivia(node.leadingTrivia ?? .zero)
.withTrailingTrivia(node.trailingTrivia ?? .zero)
.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
.with(\.trailingTrivia, node.trailingTrivia ?? .zero)
)
}
}

View File

@ -155,7 +155,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -166,7 +166,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -177,7 +177,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -188,7 +188,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -199,7 +199,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -210,7 +210,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -221,7 +221,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
@ -232,7 +232,7 @@ private extension PrivateOverFilePrivateRule {
}
correctionPositions.append(modifier.positionAfterSkippingLeadingTrivia)
let newNode = node.withModifiers(node.modifiers?.replacing(fileprivateModifier: modifier))
let newNode = node.with(\.modifiers, node.modifiers?.replacing(fileprivateModifier: modifier))
return DeclSyntax(newNode)
}
}
@ -240,7 +240,7 @@ private extension PrivateOverFilePrivateRule {
private extension ModifierListSyntax? {
var fileprivateModifier: DeclModifierSyntax? {
self?.first { $0.name.tokenKind == .fileprivateKeyword }
self?.first { $0.name.tokenKind == .keyword(.fileprivate) }
}
}
@ -248,8 +248,10 @@ private extension ModifierListSyntax {
func replacing(fileprivateModifier: DeclModifierSyntax) -> ModifierListSyntax? {
replacing(
childAt: fileprivateModifier.indexInParent,
with: fileprivateModifier.withName(
.privateKeyword(
with: fileprivateModifier.with(
\.name,
.keyword(
.private,
leadingTrivia: fileprivateModifier.leadingTrivia ?? .zero,
trailingTrivia: fileprivateModifier.trailingTrivia ?? .zero
)

View File

@ -38,7 +38,7 @@ struct RedundantNilCoalescingRule: OptInRule, SwiftSyntaxCorrectableRule, Config
private extension RedundantNilCoalescingRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: TokenSyntax) {
if node.tokenKind.isNilCoalescingOperator && node.nextToken?.tokenKind == .nilKeyword {
if node.tokenKind.isNilCoalescingOperator && node.nextToken?.tokenKind == .keyword(.nil) {
violations.append(node.position)
}
}
@ -66,7 +66,7 @@ private extension RedundantNilCoalescingRule {
return super.visit(node)
}
let newNode = node.removingLast().removingLast().withoutTrailingTrivia()
let newNode = node.removingLast().removingLast().with(\.trailingTrivia, [])
correctionPositions.append(newNode.endPosition)
return super.visit(newNode)
}
@ -75,6 +75,6 @@ private extension RedundantNilCoalescingRule {
private extension TokenKind {
var isNilCoalescingOperator: Bool {
self == .spacedBinaryOperator("??") || self == .unspacedBinaryOperator("??")
self == .binaryOperator("??")
}
}

View File

@ -42,22 +42,15 @@ struct RedundantObjcAttributeRule: SwiftSyntaxRule, SubstitutionCorrectableRule,
}
private extension AttributeListSyntax {
var hasObjCMembers: Bool {
contains { $0.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("objcMembers") }
}
var objCAttribute: AttributeSyntax? {
lazy
.compactMap { $0.as(AttributeSyntax.self) }
.first { attribute in
attribute.attributeName.tokenKind == .contextualKeyword("objc") &&
attribute.argument == nil
}
.first { $0.attributeNameText == "objc" && $0.argument == nil }
}
var hasAttributeImplyingObjC: Bool {
contains { element in
guard case let .identifier(attributeName) = element.as(AttributeSyntax.self)?.attributeName.tokenKind else {
guard let attributeName = element.as(AttributeSyntax.self)?.attributeNameText else {
return false
}
@ -89,7 +82,7 @@ private extension AttributeListSyntax {
return objcAttribute
} else if parent?.isFunctionOrStoredProperty == true,
let parentClassDecl = parent?.parent?.parent?.parent?.parent?.as(ClassDeclSyntax.self),
parentClassDecl.attributes?.hasObjCMembers == true {
parentClassDecl.attributes.contains(attributeNamed: "objcMembers") {
return objcAttribute
} else if let parentExtensionDecl = parent?.parent?.parent?.parent?.parent?.as(ExtensionDeclSyntax.self),
parentExtensionDecl.attributes?.objCAttribute != nil {

View File

@ -121,7 +121,7 @@ struct RedundantOptionalInitializationRule: SwiftSyntaxCorrectableRule, Configur
private extension RedundantOptionalInitializationRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: VariableDeclSyntax) {
guard node.letOrVarKeyword.tokenKind == .varKeyword,
guard node.letOrVarKeyword.tokenKind == .keyword(.var),
!node.modifiers.containsLazy else {
return
}
@ -141,7 +141,7 @@ private extension RedundantOptionalInitializationRule {
}
override func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
guard node.letOrVarKeyword.tokenKind == .varKeyword,
guard node.letOrVarKeyword.tokenKind == .keyword(.var),
!node.modifiers.containsLazy else {
return super.visit(node)
}
@ -166,16 +166,16 @@ private extension RedundantOptionalInitializationRule {
return binding
}
let newBinding = binding.withInitializer(nil)
let newBinding = binding.with(\.initializer, nil)
if newBinding.accessor == nil {
return newBinding.withTrailingTrivia(binding.initializer?.trailingTrivia ?? .zero)
return newBinding.with(\.trailingTrivia, binding.initializer?.trailingTrivia ?? .zero)
} else {
return newBinding
}
})
return super.visit(node.withBindings(newBindings))
return super.visit(node.with(\.bindings, newBindings))
}
}
}

View File

@ -82,11 +82,11 @@ private extension RedundantSetAccessControlRule {
return
}
if setAccessor.name.tokenKind == .fileprivateKeyword,
if setAccessor.name.tokenKind == .keyword(.fileprivate),
modifiers.getAccessor == nil,
let closestDeclModifiers = node.closestDecl()?.modifiers {
let closestDeclIsFilePrivate = closestDeclModifiers.contains {
$0.name.tokenKind == .fileprivateKeyword
$0.name.tokenKind == .keyword(.fileprivate)
}
if closestDeclIsFilePrivate {
@ -95,12 +95,12 @@ private extension RedundantSetAccessControlRule {
}
}
if setAccessor.name.tokenKind == .internalKeyword,
if setAccessor.name.tokenKind == .keyword(.internal),
modifiers.getAccessor == nil,
let closesDecl = node.closestDecl(),
let closestDeclModifiers = closesDecl.modifiers {
let closestDeclIsInternal = closestDeclModifiers.isEmpty || closestDeclModifiers.contains {
$0.name.tokenKind == .internalKeyword
$0.name.tokenKind == .keyword(.internal)
}
if closestDeclIsInternal {
@ -144,7 +144,7 @@ private extension DeclSyntax {
private extension ModifierListSyntax {
var setAccessor: DeclModifierSyntax? {
first { $0.detail?.detail.tokenKind == .contextualKeyword("set") }
first { $0.detail?.detail.tokenKind == .keyword(.set) }
}
var getAccessor: DeclModifierSyntax? {

View File

@ -121,8 +121,8 @@ private class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let newNode = node
.withInitializer(nil)
.withPattern(node.pattern.withTrailingTrivia(node.trailingTrivia ?? .zero))
.with(\.initializer, nil)
.with(\.pattern, node.pattern.with(\.trailingTrivia, node.trailingTrivia ?? .zero))
return super.visit(newNode)
}
}

View File

@ -102,7 +102,7 @@ private extension FunctionDeclSyntax {
var isOperator: Bool {
switch identifier.tokenKind {
case .spacedBinaryOperator:
case .binaryOperator:
return true
default:
return false

View File

@ -11,7 +11,10 @@ struct StrictFilePrivateRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxR
description: "`fileprivate` should be avoided",
kind: .idiomatic,
nonTriggeringExamples: [
Example("extension String {}"),
Example("private extension String {}"),
Example("""
public
extension String {
var i: Int { 1 }
}
@ -180,7 +183,7 @@ private extension StrictFilePrivateRule {
}()
override func visitPost(_ node: DeclModifierSyntax) {
guard node.name.tokenKind == .fileprivateKeyword, let grandparent = node.parent?.parent else {
guard node.name.tokenKind == .keyword(.fileprivate), let grandparent = node.parent?.parent else {
return
}
guard grandparent.is(FunctionDeclSyntax.self) || grandparent.is(VariableDeclSyntax.self) else {
@ -193,7 +196,7 @@ private extension StrictFilePrivateRule {
return
}
if let varDecl = grandparent.as(VariableDeclSyntax.self) {
let isSpecificForSetter = node.detail?.detail.tokenKind == .contextualKeyword("set")
let isSpecificForSetter = node.detail?.detail.tokenKind == .keyword(.set)
let firstImplementingProtocol = varDecl.bindings
.flatMap { binding in
let pattern = binding.pattern

View File

@ -166,11 +166,11 @@ private final class SyntacticSugarRuleVisitor: SyntaxVisitor {
// Skip checks for 'self' or \T Dictionary<Key, Value>.self
if let parent = node.parent?.as(MemberAccessExprSyntax.self),
let lastToken = Array(parent.tokens(viewMode: .sourceAccurate)).last?.tokenKind,
[.selfKeyword, .identifier("Type"), .identifier("none"), .identifier("Index")].contains(lastToken) {
[.keyword(.self), .identifier("Type"), .identifier("none"), .identifier("Index")].contains(lastToken) {
return
}
let typeName = node.expression.withoutTrivia().description
let typeName = node.expression.trimmedDescription
if SugaredType(typeName: typeName) != nil {
if let violation = violation(from: node) {

View File

@ -73,11 +73,14 @@ private extension ToggleBoolRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let newNode = node
.replacing(childAt: 0, with: "\(node.first!.withoutTrivia()).toggle()")
.replacing(
childAt: 0,
with: "\(node.first!.trimmed).toggle()"
)
.removingLast()
.removingLast()
.withLeadingTrivia(node.leadingTrivia ?? .zero)
.withTrailingTrivia(node.trailingTrivia ?? .zero)
.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
.with(\.trailingTrivia, node.trailingTrivia ?? .zero)
return super.visit(newNode)
}
@ -90,8 +93,8 @@ private extension ExprListSyntax {
count == 3,
dropFirst().first?.is(AssignmentExprSyntax.self) == true,
last?.is(PrefixOperatorExprSyntax.self) == true,
let lhs = first?.withoutTrivia().description,
let rhs = last?.withoutTrivia().description,
let lhs = first?.trimmedDescription,
let rhs = last?.trimmedDescription,
rhs == "!\(lhs)"
else {
return false

View File

@ -77,8 +77,8 @@ struct UnavailableConditionRule: ConfigurationProviderRule, SwiftSyntaxRule {
}
private final class UnavailableConditionRuleVisitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: IfStmtSyntax) {
guard node.body.statements.withoutTrivia().isEmpty else {
override func visitPost(_ node: IfExprSyntax) {
guard node.body.statements.isEmpty else {
return
}
@ -101,13 +101,13 @@ private final class UnavailableConditionRuleVisitor: ViolationsSyntaxVisitor {
)
}
private func asAvailabilityCondition(_ condition: ConditionElementSyntax.Condition) -> SyntaxProtocol? {
condition.as(AvailabilityConditionSyntax.self) ??
condition.as(UnavailabilityConditionSyntax.self)
private func asAvailabilityCondition(_ condition: ConditionElementSyntax.Condition)
-> AvailabilityConditionSyntax? {
condition.as(AvailabilityConditionSyntax.self)
}
private func otherAvailabilityCheckInvolved(ifStmt: IfStmtSyntax) -> Bool {
if let elseBody = ifStmt.elseBody, let nestedIfStatement = elseBody.as(IfStmtSyntax.self) {
private func otherAvailabilityCheckInvolved(ifStmt: IfExprSyntax) -> Bool {
if let elseBody = ifStmt.elseBody, let nestedIfStatement = elseBody.as(IfExprSyntax.self) {
if nestedIfStatement.conditions.map(\.condition).compactMap(asAvailabilityCondition).isNotEmpty {
return true
}
@ -116,11 +116,11 @@ private final class UnavailableConditionRuleVisitor: ViolationsSyntaxVisitor {
return false
}
private func reason(for check: SyntaxProtocol) -> String {
switch check {
case is AvailabilityConditionSyntax:
private func reason(for condition: AvailabilityConditionSyntax) -> String {
switch condition.availabilityKeyword.tokenKind {
case .poundAvailableKeyword:
return "Use #unavailable instead of #available with an empty body"
case is UnavailabilityConditionSyntax:
case .poundUnavailableKeyword:
return "Use #available instead of #unavailable with an empty body"
default:
queuedFatalError("Unknown availability check type.")

View File

@ -105,7 +105,7 @@ private extension UnavailableFunctionRule {
private extension FunctionDeclSyntax {
var returnsNever: Bool {
if let expr = signature.output?.returnType.as(SimpleTypeIdentifierSyntax.self) {
return expr.name.withoutTrivia().text == "Never"
return expr.name.text == "Never"
}
return false
}
@ -123,8 +123,9 @@ private extension AttributeListSyntax? {
return false
}
return attr.attributeName.tokenKind == .contextualKeyword("available") && arguments.contains { arg in
arg.entry.as(TokenSyntax.self)?.tokenKind == .contextualKeyword("unavailable")
let attributeName = attr.attributeNameText
return attributeName == "available" && arguments.contains { arg in
arg.entry.as(TokenSyntax.self)?.tokenKind.isUnavailableKeyword == true
}
}
}
@ -148,7 +149,7 @@ private extension CodeBlockSyntax? {
return false
}
return terminatingFunctions.contains(identifierExpr.identifier.withoutTrivia().text)
return terminatingFunctions.contains(identifierExpr.identifier.text)
}
}

View File

@ -154,8 +154,8 @@ private final class UntypedErrorInCatchRuleRewriter: SyntaxRewriter, ViolationsS
correctionPositions.append(node.catchKeyword.positionAfterSkippingLeadingTrivia)
return super.visit(
node
.withCatchKeyword(node.catchKeyword.withTrailingTrivia(.spaces(1)))
.withCatchItems(CatchItemListSyntax([]))
.with(\.catchKeyword, node.catchKeyword.with(\.trailingTrivia, .spaces(1)))
.with(\.catchItems, CatchItemListSyntax([]))
)
}
}

View File

@ -68,7 +68,7 @@ private extension FunctionCallExprSyntax {
var isEnumerated: Bool {
guard let memberAccess = calledExpression.as(MemberAccessExprSyntax.self),
memberAccess.base != nil,
memberAccess.name.withoutTrivia().text == "enumerated",
memberAccess.name.text == "enumerated",
hasNoArguments else {
return false
}

View File

@ -44,7 +44,7 @@ private extension XCTSpecificMatcherRule {
*/
let arguments = node.argumentList
.prefix(2)
.map { $0.expression.withoutTrivia().description }
.map { $0.expression.trimmedDescription }
.sorted { arg1, _ -> Bool in
return Self.protectedArguments.contains(arg1)
}

View File

@ -83,11 +83,12 @@ private extension AnyObjectProtocolRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
return super.visit(
node.withTypeName(
node.with(
\.typeName,
TypeSyntax(
SimpleTypeIdentifierSyntax(name: .identifier("AnyObject"), genericArgumentClause: nil)
.withLeadingTrivia(typeName.leadingTrivia ?? .zero)
.withTrailingTrivia(typeName.trailingTrivia ?? .zero)
.with(\.leadingTrivia, typeName.leadingTrivia ?? .zero)
.with(\.trailingTrivia, typeName.trailingTrivia ?? .zero)
)
)
)

View File

@ -55,7 +55,7 @@ private extension ClassDelegateProtocolRule {
private extension ProtocolDeclSyntax {
func hasObjCAttribute() -> Bool {
attributes?.contains { $0.as(AttributeSyntax.self)?.attributeName.text == "objc" } == true
attributes.contains(attributeNamed: "objc")
}
func isClassRestricted() -> Bool {

View File

@ -83,36 +83,28 @@ private extension DeploymentTargetRule {
}
}
override func visitPost(_ node: UnavailabilityConditionSyntax) {
for elem in node.availabilitySpec {
guard let restriction = elem.entry.as(AvailabilityVersionRestrictionSyntax.self),
let versionString = restriction.version?.description,
let reason = reason(platform: restriction.platform, version: versionString,
violationType: .negativeCondition) else {
continue
}
violations.append(
ReasonedRuleViolation(
position: node.poundUnavailableKeyword.positionAfterSkippingLeadingTrivia,
reason: reason
)
)
}
}
override func visitPost(_ node: AvailabilityConditionSyntax) {
let violationType: AvailabilityType
switch node.availabilityKeyword.tokenKind {
case .poundUnavailableKeyword:
violationType = .negativeCondition
case .poundAvailableKeyword:
violationType = .condition
default:
queuedFatalError("Unknown availability check type.")
}
for elem in node.availabilitySpec {
guard let restriction = elem.entry.as(AvailabilityVersionRestrictionSyntax.self),
let versionString = restriction.version?.description,
let reason = reason(platform: restriction.platform, version: versionString,
violationType: .condition) else {
violationType: violationType) else {
continue
}
violations.append(
ReasonedRuleViolation(
position: node.poundAvailableKeyword.positionAfterSkippingLeadingTrivia,
position: node.availabilityKeyword.positionAfterSkippingLeadingTrivia,
reason: reason
)
)

View File

@ -73,7 +73,7 @@ private extension DiscardedNotificationCenterObserverRule {
let thirdParent = secondParent.parent?.as(CodeBlockItemListSyntax.self),
let fourthParent = thirdParent.parent?.as(CodeBlockSyntax.self),
let fifthParent = fourthParent.parent?.as(FunctionDeclSyntax.self),
fifthParent.attributes?.hasDiscardableResultAttribute != true
!fifthParent.attributes.hasDiscardableResultAttribute
{
return // result is returned from a function
} else if node.parent?.is(TupleExprElementSyntax.self) == true {
@ -83,7 +83,7 @@ private extension DiscardedNotificationCenterObserverRule {
} else if
let previousToken = node.previousToken,
case .equal = previousToken.tokenKind,
previousToken.previousToken?.tokenKind != .wildcardKeyword
previousToken.previousToken?.tokenKind != .wildcard
{
return // result is assigned to something other than the wildcard keyword (`_`)
}
@ -93,8 +93,8 @@ private extension DiscardedNotificationCenterObserverRule {
}
}
private extension AttributeListSyntax {
private extension AttributeListSyntax? {
var hasDiscardableResultAttribute: Bool {
contains { $0.as(AttributeSyntax.self)?.attributeName.tokenKind == .identifier("discardableResult") } == true
contains(attributeNamed: "discardableResult")
}
}

View File

@ -53,7 +53,7 @@ private extension DiscouragedDirectInitRule {
override func visitPost(_ node: FunctionCallExprSyntax) {
guard node.argumentList.isEmpty, node.trailingClosure == nil,
discouragedInits.contains(node.calledExpression.withoutTrivia().description) else {
discouragedInits.contains(node.calledExpression.trimmedDescription) else {
return
}

View File

@ -47,7 +47,7 @@ private extension DynamicInlineRule {
private extension AttributeSyntax {
var isInlineAlways: Bool {
attributeName.text == "inline" &&
attributeNameText == "inline" &&
argument?.firstToken?.tokenKind == .identifier("__always")
}
}

View File

@ -38,7 +38,7 @@ private extension IBInspectableInExtensionRule {
}
override func visitPost(_ node: AttributeSyntax) {
if node.attributeName.text == "IBInspectable" {
if node.attributeNameText == "IBInspectable" {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}

View File

@ -85,7 +85,7 @@ private extension IdenticalOperandsRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: InfixOperatorExprSyntax) {
guard let operatorNode = node.operatorOperand.as(BinaryOperatorExprSyntax.self),
IdenticalOperandsRule.operators.contains(operatorNode.operatorToken.withoutTrivia().text) else {
IdenticalOperandsRule.operators.contains(operatorNode.operatorToken.text) else {
return
}

View File

@ -112,18 +112,17 @@ private extension LowerACLThanParentRule {
}
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let keyword: String
let trailingTrivia: Trivia
if node.name.tokenKind == .contextualKeyword("open") {
keyword = "public"
trailingTrivia = .space
} else {
keyword = ""
trailingTrivia = .zero
}
let newNode = node.withName(
.contextualKeyword(keyword, leadingTrivia: node.leadingTrivia ?? .zero, trailingTrivia: trailingTrivia)
let newNode: DeclModifierSyntax
if node.name.tokenKind == .keyword(.open) {
newNode = DeclModifierSyntax(
leadingTrivia: node.leadingTrivia ?? .zero,
name: .keyword(.public),
trailingTrivia: .space
)
} else {
newNode = DeclModifierSyntax(name: .keyword(.internal, presence: .missing))
}
return super.visit(newNode)
}
}
@ -136,29 +135,29 @@ private extension DeclModifierSyntax {
}
switch name.tokenKind {
case .internalKeyword
case .keyword(.internal)
where nearestNominalParent.modifiers.isPrivate ||
nearestNominalParent.modifiers.isFileprivate:
return true
case .internalKeyword
case .keyword(.internal)
where !nearestNominalParent.modifiers.containsACLModifier:
guard let nominalExtension = nearestNominalParent.nearestNominalExtensionDeclParent() else {
return false
}
return nominalExtension.modifiers.isPrivate ||
nominalExtension.modifiers.isFileprivate
case .publicKeyword
case .keyword(.public)
where nearestNominalParent.modifiers.isPrivate ||
nearestNominalParent.modifiers.isFileprivate ||
nearestNominalParent.modifiers.isInternal:
return true
case .publicKeyword
case .keyword(.public)
where !nearestNominalParent.modifiers.containsACLModifier:
guard let nominalExtension = nearestNominalParent.nearestNominalExtensionDeclParent() else {
return true
}
return !nominalExtension.modifiers.isPublic
case .contextualKeyword("open") where !nearestNominalParent.modifiers.isOpen:
case .keyword(.open) where !nearestNominalParent.modifiers.isOpen:
return true
default:
return false
@ -215,32 +214,33 @@ private extension Syntax {
private extension ModifierListSyntax? {
var isPrivate: Bool {
self?.contains(where: { $0.name.tokenKind == .privateKeyword }) == true
self?.contains(where: { $0.name.tokenKind == .keyword(.private) }) == true
}
var isInternal: Bool {
self?.contains(where: { $0.name.tokenKind == .internalKeyword }) == true
self?.contains(where: { $0.name.tokenKind == .keyword(.internal) }) == true
}
var isPublic: Bool {
self?.contains(where: { $0.name.tokenKind == .publicKeyword }) == true
self?.contains(where: { $0.name.tokenKind == .keyword(.public) }) == true
}
var isOpen: Bool {
self?.contains(where: { $0.name.tokenKind == .contextualKeyword("open") }) == true
self?.contains(where: { $0.name.tokenKind == .keyword(.open) }) == true
}
var containsACLModifier: Bool {
guard self?.isEmpty == false else {
return false
}
let aclTokens: [TokenKind] = [
.fileprivateKeyword,
.privateKeyword,
.internalKeyword,
.publicKeyword,
.contextualKeyword("open")
let aclTokens: Set<TokenKind> = [
.keyword(.private),
.keyword(.fileprivate),
.keyword(.internal),
.keyword(.public),
.keyword(.open)
]
return self?.contains(where: {
aclTokens.contains($0.name.tokenKind)
}) == true

View File

@ -33,7 +33,7 @@ private extension NSObjectPreferIsEqualRule {
private extension ClassDeclSyntax {
var isObjC: Bool {
if attributes?.isObjc == true {
if attributes.isObjc {
return true
}
@ -58,8 +58,8 @@ private extension FunctionDeclSyntax {
let rhs = parameterList.last,
lhs.firstName?.text == "lhs",
rhs.firstName?.text == "rhs",
let lhsTypeDescription = lhs.type?.withoutTrivia().description,
let rhsTypeDescription = rhs.type?.withoutTrivia().description,
let lhsTypeDescription = lhs.type?.trimmedDescription,
let rhsTypeDescription = rhs.type?.trimmedDescription,
lhsTypeDescription == rhsTypeDescription
else {
return false
@ -85,8 +85,8 @@ private extension SyntaxProtocol {
}
}
private extension AttributeListSyntax {
private extension AttributeListSyntax? {
var isObjc: Bool {
contains { ["objc", "objcMembers"].contains($0.as(AttributeSyntax.self)?.attributeName.text) }
contains(attributeNamed: "objc") || contains(attributeNamed: "objcMembers")
}
}

View File

@ -26,7 +26,7 @@ private extension NotificationCenterDetachmentRule {
let arg = node.argumentList.first,
arg.label == nil,
let expr = arg.expression.as(IdentifierExprSyntax.self),
expr.identifier.tokenKind == .selfKeyword else {
expr.identifier.tokenKind == .keyword(.self) else {
return
}

View File

@ -95,7 +95,7 @@ private extension PrivateOutletRule {
override func visitPost(_ node: MemberDeclListItemSyntax) {
guard
let decl = node.decl.as(VariableDeclSyntax.self),
decl.attributes?.hasIBOutlet == true,
decl.attributes.contains(attributeNamed: "IBOutlet"),
decl.modifiers?.isPrivateOrFilePrivate != true
else {
return
@ -110,12 +110,6 @@ private extension PrivateOutletRule {
}
}
private extension AttributeListSyntax {
var hasIBOutlet: Bool {
contains { $0.as(AttributeSyntax.self)?.attributeName.text == "IBOutlet" }
}
}
private extension ModifierListSyntax {
var isPrivateOrFilePrivate: Bool {
contains(where: \.isPrivateOrFilePrivate)

View File

@ -201,7 +201,7 @@ private class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let (modifiers, declKeyword) = withoutPrivate(modifiers: node.modifiers, declKeyword: node.classKeyword)
return super.visit(node.withModifiers(modifiers).withClassKeyword(declKeyword))
return super.visit(node.with(\.modifiers, modifiers).with(\.classKeyword, declKeyword))
}
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
@ -215,7 +215,7 @@ private class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let (modifiers, declKeyword) = withoutPrivate(modifiers: node.modifiers, declKeyword: node.funcKeyword)
return super.visit(node.withModifiers(modifiers).withFuncKeyword(declKeyword))
return super.visit(node.with(\.modifiers, modifiers).with(\.funcKeyword, declKeyword))
}
private func withoutPrivate(modifiers: ModifierListSyntax?,
@ -227,14 +227,14 @@ private class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
var leadingTrivia = Trivia.zero
for modifier in modifiers {
let accumulatedLeadingTrivia = leadingTrivia + (modifier.leadingTrivia ?? .zero)
if modifier.name.tokenKind == .privateKeyword {
if modifier.name.tokenKind == .keyword(.private) {
leadingTrivia = accumulatedLeadingTrivia
} else {
filteredModifiers.append(modifier.withLeadingTrivia(accumulatedLeadingTrivia))
filteredModifiers.append(modifier.with(\.leadingTrivia, accumulatedLeadingTrivia))
leadingTrivia = .zero
}
}
let declKeyword = declKeyword.withLeadingTrivia(leadingTrivia + (declKeyword.leadingTrivia ?? .zero))
let declKeyword = declKeyword.with(\.leadingTrivia, leadingTrivia + (declKeyword.leadingTrivia ?? .zero))
return (ModifierListSyntax(filteredModifiers), declKeyword)
}
}
@ -269,11 +269,11 @@ private extension FunctionDeclSyntax {
private extension ModifierListSyntax {
var hasPrivate: Bool {
contains { $0.name.tokenKind == .privateKeyword }
contains { $0.name.tokenKind == .keyword(.private) }
}
var hasStatic: Bool {
contains { $0.name.tokenKind == .staticKeyword }
contains { $0.name.tokenKind == .keyword(.static) }
}
}
@ -281,8 +281,6 @@ private func resultInPrivateProperty(modifiers: ModifierListSyntax?, attributes:
guard let modifiers, modifiers.hasPrivate else {
return false
}
guard let attributes else {
return true
}
return !attributes.contains { $0.as(AttributeSyntax.self)?.attributeName.tokenKind == .contextualKeyword("objc") }
return !attributes.contains(attributeNamed: "objc")
}

View File

@ -25,7 +25,7 @@ private extension QuickDiscouragedFocusedTestRule {
override func visitPost(_ node: FunctionCallExprSyntax) {
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
case let name = identifierExpr.identifier.withoutTrivia().text,
case let name = identifierExpr.identifier.text,
QuickFocusedCallKind(rawValue: name) != nil {
violations.append(node.positionAfterSkippingLeadingTrivia)
}

View File

@ -25,7 +25,7 @@ private extension QuickDiscouragedPendingTestRule {
override func visitPost(_ node: FunctionCallExprSyntax) {
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
case let name = identifierExpr.identifier.withoutTrivia().text,
case let name = identifierExpr.identifier.text,
QuickPendingCallKind(rawValue: name) != nil {
violations.append(node.positionAfterSkippingLeadingTrivia)
}

View File

@ -107,7 +107,7 @@ private extension SelfInPropertyInitializationRule {
return
}
let visitor = IdentifierUsageVisitor(identifier: .selfKeyword)
let visitor = IdentifierUsageVisitor(identifier: .keyword(.self))
for binding in node.bindings {
guard let initializer = binding.initializer,
visitor.walk(tree: initializer.value, handler: \.isTokenUsed) else {

View File

@ -70,7 +70,7 @@ private extension StrongIBOutletRule {
}
let newModifiers = ModifierListSyntax(modifiers.filter { $0 != weakOrUnownedModifier })
let newNode = node.withModifiers(newModifiers)
let newNode = node.with(\.modifiers, newModifiers)
correctionPositions.append(violationPosition)
return super.visit(newNode)
}

View File

@ -16,7 +16,16 @@ struct UnownedVariableCaptureRule: SwiftSyntaxRule, OptInRule, ConfigurationProv
Example("foo { [weak bar] in _ }"),
Example("foo { [weak bar] param in _ }"),
Example("foo { bar in _ }"),
Example("foo { $0 }")
Example("foo { $0 }"),
Example("""
final class First {}
final class Second {
unowned var value: First
init(value: First) {
self.value = value
}
}
""")
],
triggeringExamples: [
Example("foo { [↓unowned self] in _ }"),
@ -31,23 +40,9 @@ struct UnownedVariableCaptureRule: SwiftSyntaxRule, OptInRule, ConfigurationProv
}
private final class UnownedVariableCaptureRuleVisitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: ClosureCaptureItemSyntax) {
if let token = node.unownedToken {
violations.append(token.positionAfterSkippingLeadingTrivia)
}
}
override func visitPost(_ node: TokenListSyntax) {
if case .contextualKeyword("unowned") = node.first?.tokenKind {
override func visitPost(_ node: TokenSyntax) {
if case .keyword(.unowned) = node.tokenKind, node.parent?.is(ClosureCaptureItemSpecifierSyntax.self) == true {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
}
private extension ClosureCaptureItemSyntax {
var unownedToken: TokenSyntax? {
specifier?.first { token in
token.tokenKind == .identifier("unowned")
}
}
}

View File

@ -152,12 +152,14 @@ private extension UnusedCaptureListRule {
return (name.text, item)
} else if let expr = item.expression.as(IdentifierExprSyntax.self) {
// allow "[unowned self]"
if expr.identifier.tokenKind == .selfKeyword && item.specifier.containsUnowned {
if expr.identifier.tokenKind == .keyword(.self),
item.specifier?.specifier.tokenKind == .keyword(.unowned) {
return nil
}
// allow "[self]" capture (SE-0269)
if expr.identifier.tokenKind == .selfKeyword && item.specifier.isNilOrEmpty {
if expr.identifier.tokenKind == .keyword(.self),
item.specifier == nil {
return nil
}
@ -203,15 +205,3 @@ private final class IdentifierReferenceVisitor: SyntaxVisitor {
}
}
}
private extension TokenListSyntax? {
var containsUnowned: Bool {
self?.contains { token in
token.tokenKind == .contextualKeyword("unowned")
} ?? false
}
var isNilOrEmpty: Bool {
self?.isEmpty ?? true
}
}

View File

@ -79,27 +79,26 @@ private extension UnusedClosureParameterRule {
continue
}
if name.tokenKind == .wildcardKeyword {
if name.tokenKind == .wildcard {
continue
} else if referencedIdentifiers.contains(name.text.removingDollarsAndBackticks) {
continue
}
correctionPositions.append(name.positionAfterSkippingLeadingTrivia)
newParams = newParams.withParameterList(
newParams.parameterList.replacing(
let newParameterList = newParams.parameterList.replacing(
childAt: index,
with: param.withFirstName(name.withKind(.wildcardKeyword))
)
with: param.with(\.firstName, name.withKind(.wildcard))
)
newParams = newParams.with(\.parameterList, newParameterList)
}
let newNode = node.withSignature(signature.withInput(.init(newParams)))
let newNode = node.with(\.signature, signature.with(\.input, .init(newParams)))
return super.visit(newNode)
}
var newParams = params
for (index, param) in params.enumerated() {
if param.name.tokenKind == .wildcardKeyword {
if param.name.tokenKind == .wildcard {
continue
} else if referencedIdentifiers.contains(param.name.text.removingDollarsAndBackticks) {
continue
@ -108,10 +107,10 @@ private extension UnusedClosureParameterRule {
correctionPositions.append(param.name.positionAfterSkippingLeadingTrivia)
newParams = newParams.replacing(
childAt: index,
with: param.withName(param.name.withKind(.wildcardKeyword))
with: param.with(\.name, param.name.withKind(.wildcard))
)
}
let newNode = node.withSignature(signature.withInput(.init(newParams)))
let newNode = node.with(\.signature, signature.with(\.input, .init(newParams)))
return super.visit(newNode)
}
}
@ -141,7 +140,7 @@ private extension ClosureExprSyntax {
var namedParameters: [ClosureParam] {
if let params = signature?.input?.as(ClosureParamListSyntax.self) {
return params.compactMap { param in
if param.name.tokenKind == .wildcardKeyword {
if param.name.tokenKind == .wildcard {
return nil
}
return ClosureParam(
@ -151,7 +150,7 @@ private extension ClosureExprSyntax {
}
} else if let params = signature?.input?.as(ParameterClauseSyntax.self)?.parameterList {
return params.compactMap { param in
if param.firstName?.tokenKind == .wildcardKeyword {
if param.firstName?.tokenKind == .wildcard {
return nil
}
return param.firstName.map { name in

View File

@ -122,7 +122,7 @@ private extension UnusedControlFlowLabelRule {
return super.visit(node)
}
let newNode = node.statement.withLeadingTrivia(node.leadingTrivia ?? .zero)
let newNode = node.statement.with(\.leadingTrivia, node.leadingTrivia ?? .zero)
correctionPositions.append(violationPosition)
return visit(newNode).as(StmtSyntax.self) ?? newNode
}
@ -133,7 +133,7 @@ private extension LabeledStmtSyntax {
var violationPosition: AbsolutePosition? {
let visitor = BreakAndContinueLabelCollector(viewMode: .sourceAccurate)
let labels = visitor.walk(tree: self, handler: \.labels)
guard !labels.contains(labelName.withoutTrivia().text) else {
guard !labels.contains(labelName.text) else {
return nil
}
@ -145,13 +145,13 @@ private class BreakAndContinueLabelCollector: SyntaxVisitor {
private(set) var labels: Set<String> = []
override func visitPost(_ node: BreakStmtSyntax) {
if let label = node.label?.withoutTrivia().text {
if let label = node.label?.text {
labels.insert(label)
}
}
override func visitPost(_ node: ContinueStmtSyntax) {
if let label = node.label?.withoutTrivia().text {
if let label = node.label?.text {
labels.insert(label)
}
}

View File

@ -139,11 +139,11 @@ private extension UnusedSetterValueRule {
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { [ProtocolDeclSyntax.self] }
override func visitPost(_ node: AccessorDeclSyntax) {
guard node.accessorKind.tokenKind == .contextualKeyword("set") else {
guard node.accessorKind.tokenKind == .keyword(.set) else {
return
}
let variableName = node.parameter?.name.withoutTrivia().text ?? "newValue"
let variableName = node.parameter?.name.text ?? "newValue"
let visitor = NewValueUsageVisitor(variableName: variableName)
if !visitor.walk(tree: node, handler: \.isVariableUsed) {
if (Syntax(node).closestVariableOrSubscript()?.modifiers).containsOverride,
@ -166,7 +166,7 @@ private extension UnusedSetterValueRule {
}
override func visitPost(_ node: IdentifierExprSyntax) {
if node.identifier.withoutTrivia().text == variableName {
if node.identifier.text == variableName {
isVariableUsed = true
}
}

View File

@ -170,9 +170,7 @@ private extension ValidIBInspectableRule {
private extension VariableDeclSyntax {
var isIBInspectable: Bool {
attributes?.contains { attr in
attr.as(AttributeSyntax.self)?.attributeName.text == "IBInspectable"
} ?? false
attributes.contains(attributeNamed: "IBInspectable")
}
var hasViolation: Bool {
@ -180,7 +178,7 @@ private extension VariableDeclSyntax {
}
var isReadOnlyProperty: Bool {
if letOrVarKeyword.tokenKind == .letKeyword {
if letOrVarKeyword.tokenKind == .keyword(.let) {
return true
}
@ -212,7 +210,7 @@ private extension VariableDeclSyntax {
return false
}
return ValidIBInspectableRule.supportedTypes.contains(type.type.withoutTrivia().description)
return ValidIBInspectableRule.supportedTypes.contains(type.type.trimmedDescription)
}
}
}

View File

@ -114,7 +114,7 @@ private extension VariableDeclSyntax {
return false
}
return pattern.identifier.withoutTrivia().text.lowercased().hasSuffix("delegate")
return pattern.identifier.text.lowercased().hasSuffix("delegate")
}
}
@ -142,12 +142,12 @@ private extension VariableDeclSyntax {
]
return attributes?.contains { attr in
guard let customAttr = attr.as(CustomAttributeSyntax.self),
guard case let .attribute(customAttr) = attr,
let typeIdentifier = customAttr.attributeName.as(SimpleTypeIdentifierSyntax.self) else {
return false
}
return ignoredAttributes.contains(typeIdentifier.name.withoutTrivia().text)
return ignoredAttributes.contains(typeIdentifier.name.text)
} ?? false
}
}

View File

@ -42,7 +42,7 @@ struct YodaConditionRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxRule
}
private final class YodaConditionRuleVisitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: IfStmtSyntax) {
override func visitPost(_ node: IfExprSyntax) {
visit(conditions: node.conditions)
}

View File

@ -67,7 +67,7 @@ private extension EnumCaseAssociatedValuesLengthRule {
violationSeverity = .warning
}
let reason = "Enum case \(node.identifier.withoutTrivia().text) should contain "
let reason = "Enum case \(node.identifier.text) should contain "
+ "less than \(configuration.warning) associated values: "
+ "currently contains \(enumCaseAssociatedValueCount)"
violations.append(

View File

@ -60,10 +60,8 @@ private extension ContainsOverFilterCountRule {
private extension TokenKind {
var isZeroComparison: Bool {
self == .spacedBinaryOperator("==") ||
self == .spacedBinaryOperator("!=") ||
self == .unspacedBinaryOperator("==") ||
self == .spacedBinaryOperator(">") ||
self == .unspacedBinaryOperator(">")
self == .binaryOperator("==") ||
self == .binaryOperator("!=") ||
self == .binaryOperator(">")
}
}

View File

@ -98,7 +98,7 @@ private extension ExprSyntax {
private extension TokenSyntax {
var binaryOperator: String? {
switch tokenKind {
case .spacedBinaryOperator(let str), .unspacedBinaryOperator(let str):
case .binaryOperator(let str):
return str
default:
return nil

View File

@ -43,7 +43,7 @@ private extension ReduceBooleanRule {
return
}
let suggestedFunction = bool.booleanLiteral.tokenKind == .trueKeyword ? "allSatisfy" : "contains"
let suggestedFunction = bool.booleanLiteral.tokenKind == .keyword(.true) ? "allSatisfy" : "contains"
violations.append(
ReasonedRuleViolation(
position: calledExpression.name.positionAfterSkippingLeadingTrivia,

View File

@ -121,7 +121,7 @@ private struct RuleHelper {
func hasViolation(
locationConverter: SourceLocationConverter,
attributesAndPlacements: [(SyntaxProtocol, AttributePlacement)]
attributesAndPlacements: [(AttributeSyntax, AttributePlacement)]
) -> Bool {
var linesWithAttributes: Set<Int> = [keywordLine]
for (attribute, placement) in attributesAndPlacements {
@ -147,75 +147,40 @@ private struct RuleHelper {
}
}
private enum Attribute {
case builtIn(AttributeSyntax)
case custom(CustomAttributeSyntax)
static func from(syntax: SyntaxProtocol) -> Self? {
if let attribute = syntax.as(AttributeSyntax.self) {
return .builtIn(attribute)
}
if let attribute = syntax.as(CustomAttributeSyntax.self) {
return .custom(attribute)
}
return nil
}
var hasArguments: Bool {
switch self {
case let .builtIn(attribute):
return attribute.argument != nil
case let .custom(attribute):
return attribute.argumentList != nil
}
}
var name: String? {
switch self {
case let .builtIn(attribute):
return attribute.attributeName.text
case let .custom(attribute):
return attribute.attributeName.as(SimpleTypeIdentifierSyntax.self)?.typeName
}
}
var syntaxNode: SyntaxProtocol {
switch self {
case let .builtIn(attribute):
return attribute
case let .custom(attribute):
return attribute
}
}
}
private extension AttributeListSyntax {
func attributesAndPlacements(configuration: AttributesConfiguration, shouldBeOnSameLine: Bool)
-> [(SyntaxProtocol, AttributePlacement)] {
self.children(viewMode: .sourceAccurate)
.compactMap { Attribute.from(syntax: $0) }
.compactMap { attribute in
guard let attributeName = attribute.name else {
return nil
}
let atPrefixedName = "@\(attributeName)"
-> [(AttributeSyntax, AttributePlacement)] {
self
.children(viewMode: .sourceAccurate)
.compactMap { $0.as(AttributeSyntax.self) }
.map { attribute in
let atPrefixedName = "@\(attribute.attributeNameText)"
if configuration.alwaysOnSameLine.contains(atPrefixedName) {
return (attribute.syntaxNode, .sameLineAsDeclaration)
return (attribute, .sameLineAsDeclaration)
} else if configuration.alwaysOnNewLine.contains(atPrefixedName) {
return (attribute.syntaxNode, .dedicatedLine)
} else if attribute.hasArguments {
return (attribute.syntaxNode, .dedicatedLine)
return (attribute, .dedicatedLine)
} else if attribute.argument != nil {
return (attribute, .dedicatedLine)
}
return shouldBeOnSameLine
? (attribute.syntaxNode, .sameLineAsDeclaration)
: (attribute.syntaxNode, .dedicatedLine)
return shouldBeOnSameLine ? (attribute, .sameLineAsDeclaration) : (attribute, .dedicatedLine)
}
}
var hasAttributeWithKeypathArgument: Bool {
contains { element in
switch element {
case .attribute(let attribute):
return attribute.hasKeypathArgument
case .ifConfigDecl:
return false
}
}
}
// swiftlint:disable:next cyclomatic_complexity
func makeHelper(locationConverter: SourceLocationConverter) -> RuleHelper? {
guard let parent else {
guard let parent, !hasAttributeWithKeypathArgument else {
return nil
}
@ -263,3 +228,9 @@ private extension AttributeListSyntax {
)
}
}
private extension AttributeSyntax {
var hasKeypathArgument: Bool {
argument?.as(TupleExprElementListSyntax.self)?.first?.expression.is(KeyPathExprSyntax.self) == true
}
}

View File

@ -80,7 +80,17 @@ internal struct AttributesRuleExamples {
@NSApplicationMain
@MainActor
final class AppDelegate: NSAppDelegate {}
""")
"""),
Example(#"""
final class MyView: View {
@SwiftUI.Environment(\.colorScheme) var colorScheme: ColorScheme
}
"""#),
Example(#"""
final class MyView: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
}
"""#)
]
static let triggeringExamples = [

View File

@ -63,7 +63,7 @@ private extension ClosingBraceRule {
}
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
return super.visit(node.withTrailingTrivia(.zero))
return super.visit(node.with(\.trailingTrivia, .zero))
}
}
}

View File

@ -103,16 +103,16 @@ private final class ClosureSpacingRuleRewriter: SyntaxRewriter, ViolationsSyntax
let violations = node.violations
if violations.leftBraceLeftSpace {
node.leftBrace = node.leftBrace.withLeadingTrivia(.spaces(1))
node.leftBrace = node.leftBrace.with(\.leadingTrivia, .spaces(1))
}
if violations.leftBraceRightSpace {
node.leftBrace = node.leftBrace.withTrailingTrivia(.spaces(1))
node.leftBrace = node.leftBrace.with(\.trailingTrivia, .spaces(1))
}
if violations.rightBraceLeftSpace {
node.rightBrace = node.rightBrace.withLeadingTrivia(.spaces(1))
node.rightBrace = node.rightBrace.with(\.leadingTrivia, .spaces(1))
}
if violations.rightBraceRightSpace {
node.rightBrace = node.rightBrace.withTrailingTrivia(.spaces(1))
node.rightBrace = node.rightBrace.with(\.trailingTrivia, .spaces(1))
}
if violations.hasViolations {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)

View File

@ -67,11 +67,11 @@ private extension AccessorBlockSyntax {
}
let tokens = accessors.map(\.accessorKind.tokenKind)
if tokens == [.contextualKeyword("get"), .contextualKeyword("set")] {
if tokens == [.keyword(.get), .keyword(.set)] {
return .getSet
}
if tokens == [.contextualKeyword("set"), .contextualKeyword("get")] {
if tokens == [.keyword(.set), .keyword(.get)] {
return .setGet
}

View File

@ -53,7 +53,7 @@ private extension ConditionalReturnsOnNewlineRule {
super.init(viewMode: .sourceAccurate)
}
override func visitPost(_ node: IfStmtSyntax) {
override func visitPost(_ node: IfExprSyntax) {
if isReturn(node.body.statements.lastReturn, onTheSameLineAs: node.ifKeyword) {
violations.append(node.ifKeyword.positionAfterSkippingLeadingTrivia)
return

View File

@ -210,22 +210,30 @@ private class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
.enumerated()
.map { index, item in
if index == bindingList.count - 2 {
return item.withTrailingComma(false)
return item.with(\.trailingComma, nil)
}
return item
}
if newBindingList.isNotEmpty {
newStmtList.append(CodeBlockItemSyntax(
item: .decl(DeclSyntax(varDecl.withBindings(PatternBindingListSyntax(newBindingList))))
item: .decl(DeclSyntax(varDecl.with(\.bindings, PatternBindingListSyntax(newBindingList))))
))
newStmtList.append(CodeBlockItemSyntax(
item: .stmt(StmtSyntax(returnStmt.withExpression(initExpression)))
item: .stmt(StmtSyntax(returnStmt.with(\.expression, initExpression)))
))
} else {
let leadingTrivia = (binding.trailingTrivia ?? .zero) + (returnStmt.leadingTrivia ?? .zero)
newStmtList.append(CodeBlockItemSyntax(
item: .stmt(StmtSyntax(returnStmt.withExpression(initExpression).withLeadingTrivia(leadingTrivia)))
))
newStmtList.append(
CodeBlockItemSyntax(
item: .stmt(
StmtSyntax(
returnStmt
.with(\.expression, initExpression)
.with(\.leadingTrivia, leadingTrivia)
)
)
)
)
}
return super.visit(CodeBlockItemListSyntax(newStmtList))
}

View File

@ -159,7 +159,7 @@ private extension EmptyEnumArgumentsRule {
}
correctionPositions.append(violationPosition)
return super.visit(node.withPattern(newPattern))
return super.visit(node.with(\.pattern, newPattern))
}
override func visit(_ node: MatchingPatternConditionSyntax) -> MatchingPatternConditionSyntax {
@ -171,7 +171,7 @@ private extension EmptyEnumArgumentsRule {
}
correctionPositions.append(violationPosition)
return super.visit(node.withPattern(newPattern))
return super.visit(node.with(\.pattern, newPattern))
}
}
}
@ -200,7 +200,7 @@ private extension PatternSyntax {
private extension FunctionCallExprSyntax {
var argumentsHasViolation: Bool {
!calledExpression.is(IdentifierExprSyntax.self) &&
calledExpression.as(MemberAccessExprSyntax.self)?.lastToken?.tokenKind != .initKeyword &&
calledExpression.as(MemberAccessExprSyntax.self)?.lastToken?.tokenKind != .keyword(.`init`) &&
argumentList.allSatisfy(\.expression.isDiscardAssignmentOrFunction)
}
@ -222,19 +222,19 @@ private extension FunctionCallExprSyntax {
if argumentList.allSatisfy({ $0.expression.is(DiscardAssignmentExprSyntax.self) }) {
let newCalledExpression = calledExpression
.withTrailingTrivia(rightParen?.trailingTrivia ?? .zero)
.with(\.trailingTrivia, rightParen?.trailingTrivia ?? .zero)
let newExpression = self
.withCalledExpression(ExprSyntax(newCalledExpression))
.withLeftParen(nil)
.withArgumentList(nil)
.withRightParen(nil)
.with(\.calledExpression, ExprSyntax(newCalledExpression))
.with(\.leftParen, nil)
.with(\.argumentList, [])
.with(\.rightParen, nil)
return ExprSyntax(newExpression)
}
var copy = self
for (index, arg) in argumentList.enumerated() {
if let newArgExpr = arg.expression.as(FunctionCallExprSyntax.self) {
let newArg = arg.withExpression(newArgExpr.removingInnermostDiscardArguments)
let newArg = arg.with(\.expression, newArgExpr.removingInnermostDiscardArguments)
copy.argumentList = copy.argumentList.replacing(childAt: index, with: newArg)
}
}

View File

@ -75,7 +75,7 @@ private extension EmptyParametersRule {
}
correctionPositions.append(violationPosition)
return super.visit(node.withArguments(TupleTypeElementListSyntax([])))
return super.visit(node.with(\.arguments, TupleTypeElementListSyntax([])))
}
}
}

View File

@ -88,9 +88,9 @@ private extension EmptyParenthesesWithTrailingClosureRule {
}
let newNode = node
.withLeftParen(nil)
.withRightParen(nil)
.withTrailingClosure(node.trailingClosure?.withLeadingTrivia(.spaces(1)))
.with(\.leftParen, nil)
.with(\.rightParen, nil)
.with(\.trailingClosure, node.trailingClosure?.with(\.leadingTrivia, .spaces(1)))
correctionPositions.append(violationPosition)
return super.visit(newNode)
}

View File

@ -36,8 +36,7 @@ private final class ImplicitGetterRuleVisitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: AccessorBlockSyntax) {
guard let getAccessor = node.getAccessor,
node.setAccessor == nil,
getAccessor.asyncKeyword == nil,
getAccessor.throwsKeyword == nil,
getAccessor.effectSpecifiers == nil,
getAccessor.modifier == nil,
(getAccessor.attributes == nil || getAccessor.attributes?.isEmpty == true),
getAccessor.body != nil else {

View File

@ -89,7 +89,7 @@ private extension NoSpaceInMethodCallRule {
correctionPositions.append(node.calledExpression.endPositionBeforeTrailingTrivia)
let newNode = node
.withCalledExpression(node.calledExpression.withoutTrailingTrivia())
.with(\.calledExpression, node.calledExpression.with(\.trailingTrivia, []))
return super.visit(newNode)
}

View File

@ -88,7 +88,8 @@ private extension NumberSeparatorRule {
return super.visit(node)
}
let newNode = node.withFloatingDigits(node.floatingDigits.withKind(.floatingLiteral(violation.correction)))
let newNode = node.with(\.floatingDigits,
node.floatingDigits.withKind(.floatingLiteral(violation.correction)))
correctionPositions.append(violation.position)
return super.visit(newNode)
}
@ -101,7 +102,7 @@ private extension NumberSeparatorRule {
return super.visit(node)
}
let newNode = node.withDigits(node.digits.withKind(.integerLiteral(violation.correction)))
let newNode = node.with(\.digits, node.digits.withKind(.integerLiteral(violation.correction)))
correctionPositions.append(violation.position)
return super.visit(newNode)
}
@ -140,7 +141,7 @@ private enum NumberSeparatorViolation {
private extension NumberSeparatorValidator {
func violation(token: TokenSyntax) -> NumberSeparatorViolation? {
let content = token.withoutTrivia().text
let content = token.text
guard isDecimal(number: content),
!isInValidRanges(number: content)
else {

View File

@ -45,7 +45,7 @@ private extension OperatorFunctionWhitespaceRule {
private extension FunctionDeclSyntax {
var isOperatorDeclaration: Bool {
switch identifier.tokenKind {
case .spacedBinaryOperator, .unspacedBinaryOperator:
case .binaryOperator:
return true
default:
return false

View File

@ -183,7 +183,7 @@ private class OperatorUsageWhitespaceVisitor: SyntaxVisitor {
let noSpacingAfter = operatorToken.trailingTrivia.isEmpty && nextToken.leadingTrivia.isEmpty
let noSpacing = noSpacingBefore || noSpacingAfter
let operatorText = operatorToken.withoutTrivia().text
let operatorText = operatorToken.text
if noSpacing && allowedNoSpaceOperators.contains(operatorText) {
return nil
}
@ -230,7 +230,7 @@ private extension Trivia {
switch element {
case .blockComment, .docLineComment, .docBlockComment, .lineComment:
return true
case .carriageReturnLineFeeds, .carriageReturns, .formfeeds, .newlines,
case .backslashes, .carriageReturnLineFeeds, .carriageReturns, .formfeeds, .newlines, .pounds,
.shebang, .spaces, .tabs, .unexpectedText, .verticalTabs:
return false
}

View File

@ -209,11 +209,11 @@ private extension OptionalEnumCaseMatchingRule {
!expression.expression.isDiscardAssignmentOrBoolLiteral {
let violationPosition = expression.questionMark.positionAfterSkippingLeadingTrivia
correctionPositions.append(violationPosition)
let newExpression = ExprSyntax(expression.withQuestionMark(nil))
let newPattern = PatternSyntax(pattern.withExpression(newExpression))
let newPattern = PatternSyntax(pattern.with(\.expression, expression.expression))
let newNode = node
.withPattern(newPattern)
.withWhereClause(node.whereClause?.withLeadingTrivia(expression.questionMark.trailingTrivia))
.with(\.pattern, newPattern)
.with(\.whereClause,
node.whereClause?.with(\.leadingTrivia, expression.questionMark.trailingTrivia))
return super.visit(newNode)
} else if let expression = pattern.expression.as(TupleExprSyntax.self) {
var newExpression = expression
@ -228,14 +228,13 @@ private extension OptionalEnumCaseMatchingRule {
let violationPosition = optionalChainingExpression.questionMark.positionAfterSkippingLeadingTrivia
correctionPositions.append(violationPosition)
let newElementExpression = ExprSyntax(optionalChainingExpression.withQuestionMark(nil))
let newElement = element.withExpression(newElementExpression)
let newElement = element.with(\.expression, optionalChainingExpression.expression)
newExpression.elementList = newExpression.elementList
.replacing(childAt: index, with: newElement)
}
let newPattern = PatternSyntax(pattern.withExpression(ExprSyntax(newExpression)))
let newNode = node.withPattern(newPattern)
let newPattern = PatternSyntax(pattern.with(\.expression, ExprSyntax(newExpression)))
let newNode = node.with(\.pattern, newPattern)
return super.visit(newNode)
}

View File

@ -296,7 +296,7 @@ private class Visitor: ViolationsSyntaxVisitor {
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
if case .likeClass = parentDeclScopes.last {
if node.name.tokenKind == .selfKeyword {
if node.name.tokenKind == .keyword(.self) {
return .skipChildren
}
}

View File

@ -147,11 +147,12 @@ private extension PreferSelfTypeOverTypeOfSelfRule {
correctionPositions.append(function.positionAfterSkippingLeadingTrivia)
let base: IdentifierExprSyntax = "Self"
// swiftlint:disable:next rule_id
let base = IdentifierExprSyntax(identifier: "Self")
let baseWithTrivia = base
.withLeadingTrivia(function.leadingTrivia ?? .zero)
.withTrailingTrivia(function.trailingTrivia ?? .zero)
return super.visit(node.withBase(ExprSyntax(baseWithTrivia)))
.with(\.leadingTrivia, function.leadingTrivia ?? .zero)
.with(\.trailingTrivia, function.trailingTrivia ?? .zero)
return super.visit(node.with(\.base, ExprSyntax(baseWithTrivia)))
}
}
}
@ -160,7 +161,7 @@ private extension FunctionCallExprSyntax {
var hasViolation: Bool {
return isTypeOfSelfCall &&
argumentList.map(\.label?.text) == ["of"] &&
argumentList.first?.expression.as(IdentifierExprSyntax.self)?.identifier.tokenKind == .selfKeyword
argumentList.first?.expression.as(IdentifierExprSyntax.self)?.identifier.tokenKind == .keyword(.self)
}
var isTypeOfSelfCall: Bool {

View File

@ -87,7 +87,7 @@ private extension PrefixedTopLevelConstantRule {
override var skippableDeclarations: [DeclSyntaxProtocol.Type] { .all }
override func visitPost(_ node: VariableDeclSyntax) {
guard node.letOrVarKeyword.tokenKind == .letKeyword else {
guard node.letOrVarKeyword.tokenKind == .keyword(.let) else {
return
}

View File

@ -73,7 +73,7 @@ private extension ProtocolPropertyAccessorsOrderRule {
let reversedAccessors = AccessorListSyntax(Array(node.accessors.reversed()))
return super.visit(
node.withAccessors(reversedAccessors)
node.with(\.accessors, reversedAccessors)
)
}
}
@ -83,7 +83,7 @@ private extension AccessorBlockSyntax {
var hasViolation: Bool {
guard accessors.count == 2,
accessors.allSatisfy({ $0.body == nil }),
accessors.first?.accessorKind.tokenKind == .contextualKeyword("set") else {
accessors.first?.accessorKind.tokenKind == .keyword(.set) else {
return false
}

View File

@ -69,8 +69,8 @@ private extension RedundantDiscardableLetRule {
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
let newNode = node
.withLetOrVarKeyword(nil)
.withBindings(node.bindings.withLeadingTrivia(node.letOrVarKeyword.leadingTrivia))
.with(\.letOrVarKeyword, .keyword(.let, presence: .missing))
.with(\.bindings, node.bindings.with(\.leadingTrivia, node.letOrVarKeyword.leadingTrivia))
return super.visit(newNode)
}
}
@ -78,7 +78,7 @@ private extension RedundantDiscardableLetRule {
private extension VariableDeclSyntax {
var hasRedundantDiscardableLetViolation: Bool {
letOrVarKeyword.tokenKind == .letKeyword &&
letOrVarKeyword.tokenKind == .keyword(.let) &&
bindings.count == 1 &&
bindings.first!.pattern.is(WildcardPatternSyntax.self) &&
bindings.first!.typeAnnotation == nil &&

View File

@ -93,7 +93,7 @@ private extension ReturnArrowWhitespaceRule {
private(set) var corrections: [ArrowViolation] = []
override func visitPost(_ node: FunctionTypeSyntax) {
guard let violation = node.arrow.arrowViolation else {
guard let violation = node.output.arrow.arrowViolation else {
return
}

View File

@ -120,36 +120,36 @@ private final class SelfBindingRuleRewriter: SyntaxRewriter, ViolationsSyntaxRew
initializerIdentifier.identifier.text == "self" {
correctionPositions.append(identifierPattern.positionAfterSkippingLeadingTrivia)
return super.visit(
node.withPattern(
PatternSyntax(
identifierPattern.withIdentifier(
let newPattern = PatternSyntax(
identifierPattern
.with(\.identifier,
identifierPattern.identifier.withKind(.identifier(bindIdentifier))
)
)
)
)
return super.visit(node.with(\.pattern, newPattern))
} else if node.initializer == nil, identifierPattern.identifier.text == "self", bindIdentifier != "self" {
correctionPositions.append(identifierPattern.positionAfterSkippingLeadingTrivia)
let newNode = node
.withPattern(
PatternSyntax(
identifierPattern.withIdentifier(
identifierPattern.identifier.withKind(.identifier(bindIdentifier))
let newPattern = PatternSyntax(
identifierPattern
.with(\.identifier,
identifierPattern.identifier.withKind(.identifier(bindIdentifier)))
)
)
)
.withInitializer(
InitializerClauseSyntax(
let newInitializer = InitializerClauseSyntax(
value: IdentifierExprSyntax(
identifier: .selfKeyword(
identifier: .keyword(
.`self`,
leadingTrivia: .space,
trailingTrivia: identifierPattern.trailingTrivia ?? .space
)
)
)
)
let newNode = node
.with(\.pattern, newPattern)
.with(\.initializer, newInitializer)
return super.visit(newNode)
} else {
return super.visit(node)

View File

@ -62,8 +62,8 @@ private extension ShorthandOperatorRule {
guard node.operatorOperand.is(AssignmentExprSyntax.self),
let rightExpr = node.rightOperand.as(InfixOperatorExprSyntax.self),
let binaryOperatorExpr = rightExpr.operatorOperand.as(BinaryOperatorExprSyntax.self),
ShorthandOperatorRule.allOperators.contains(binaryOperatorExpr.operatorToken.withoutTrivia().text),
node.leftOperand.withoutTrivia().description == rightExpr.leftOperand.withoutTrivia().description
ShorthandOperatorRule.allOperators.contains(binaryOperatorExpr.operatorToken.text),
node.leftOperand.trimmedDescription == rightExpr.leftOperand.trimmedDescription
else {
return
}
@ -86,7 +86,7 @@ private extension ShorthandOperatorRule {
private extension TokenSyntax {
var binaryOperator: String? {
switch tokenKind {
case .spacedBinaryOperator(let str), .unspacedBinaryOperator(let str):
case .binaryOperator(let str):
return str
default:
return nil

View File

@ -56,7 +56,7 @@ extension SwitchCaseAlignmentRule {
super.init(viewMode: .sourceAccurate)
}
override func visitPost(_ node: SwitchStmtSyntax) {
override func visitPost(_ node: SwitchExprSyntax) {
let switchPosition = node.switchKeyword.positionAfterSkippingLeadingTrivia
guard
let switchColumn = locationConverter.location(for: switchPosition).column,

View File

@ -135,16 +135,15 @@ private extension TrailingCommaRule {
switch (lastElement.trailingComma, mandatoryComma) {
case (let commaToken?, false):
correctionPositions.append(commaToken.positionAfterSkippingLeadingTrivia)
let newTrailingTrivia = (lastElement.valueExpression.trailingTrivia ?? .zero)
.appending(trivia: commaToken.leadingTrivia)
.appending(trivia: commaToken.trailingTrivia)
let newNode = node
.replacing(
childAt: lastElement.indexInParent,
with: lastElement
.withTrailingComma(nil)
.withTrailingTrivia(
(lastElement.valueExpression.trailingTrivia ?? .zero)
.appending(trivia: commaToken.leadingTrivia)
.appending(trivia: commaToken.trailingTrivia)
)
.with(\.trailingComma, nil)
.with(\.trailingTrivia, newTrailingTrivia)
)
return super.visit(newNode)
case (nil, true) where !locationConverter.isSingleLine(node: node):
@ -153,9 +152,9 @@ private extension TrailingCommaRule {
.replacing(
childAt: lastElement.indexInParent,
with: lastElement
.withoutTrailingTrivia()
.withTrailingComma(.commaToken())
.withTrailingTrivia(lastElement.trailingTrivia ?? .zero)
.with(\.trailingTrivia, [])
.with(\.trailingComma, .commaToken())
.with(\.trailingTrivia, lastElement.trailingTrivia ?? .zero)
)
return super.visit(newNode)
case (_, true), (nil, false):
@ -176,8 +175,8 @@ private extension TrailingCommaRule {
.replacing(
childAt: lastElement.indexInParent,
with: lastElement
.withTrailingComma(nil)
.withTrailingTrivia(
.with(\.trailingComma, nil)
.with(\.trailingTrivia,
(lastElement.expression.trailingTrivia ?? .zero)
.appending(trivia: commaToken.leadingTrivia)
.appending(trivia: commaToken.trailingTrivia)
@ -189,9 +188,9 @@ private extension TrailingCommaRule {
let newNode = node.replacing(
childAt: lastElement.indexInParent,
with: lastElement
.withExpression(lastElement.expression.withoutTrailingTrivia())
.withTrailingComma(.commaToken())
.withTrailingTrivia(lastElement.expression.trailingTrivia ?? .zero)
.with(\.expression, lastElement.expression.with(\.trailingTrivia, []))
.with(\.trailingComma, .commaToken())
.with(\.trailingTrivia, lastElement.expression.trailingTrivia ?? .zero)
)
return super.visit(newNode)
case (_, true), (nil, false):

View File

@ -133,7 +133,7 @@ private final class Rewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
correctionPositions.append(clause.positionAfterSkippingLeadingTrivia)
let paramList = ClosureParamListSyntax(items).withTrailingTrivia(.spaces(1))
return super.visit(node.withInput(.init(paramList)))
let paramList = ClosureParamListSyntax(items).with(\.trailingTrivia, .spaces(1))
return super.visit(node.with(\.input, .init(paramList)))
}
}

View File

@ -20,10 +20,10 @@ def swiftlint_repos(bzlmod = False):
http_archive(
name = "com_github_apple_swift_syntax",
sha256 = "2024415299a487fcb3b2d7500d2e7c149b4bc4f3b94408edd601457ee27f6b11", # SwiftSyntax sha256
sha256 = "ef9701634ad34e2dd08a2cd85bb5437af17512175bf6b4623c7d2d28068b6786", # SwiftSyntax sha256
build_file = "@SwiftLint//bazel:SwiftSyntax.BUILD",
strip_prefix = "swift-syntax-0.50800.0-SNAPSHOT-2022-12-29-a",
url = "https://github.com/apple/swift-syntax/archive/refs/tags/0.50800.0-SNAPSHOT-2022-12-29-a.tar.gz",
strip_prefix = "swift-syntax-0.50900.0-swift-DEVELOPMENT-SNAPSHOT-2023-02-06-a",
url = "https://github.com/apple/swift-syntax/archive/refs/tags/0.50900.0-swift-DEVELOPMENT-SNAPSHOT-2023-02-06-a.tar.gz",
)
http_archive(