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:
parent
393318d903
commit
2f0e537f9b
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -87,6 +87,6 @@ private class TypeNameCollectingVisitor: SyntaxVisitor {
|
|||
}
|
||||
|
||||
override func visitPost(_ node: ExtensionDeclSyntax) {
|
||||
names.insert(node.extendedType.withoutTrivia().description)
|
||||
names.insert(node.extendedType.trimmedDescription)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ private extension JoinedDefaultParameterRule {
|
|||
}
|
||||
|
||||
correctionPositions.append(violationPosition)
|
||||
let newNode = node.withArgumentList(nil)
|
||||
let newNode = node.with(\.argumentList, [])
|
||||
return super.visit(newNode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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("!=")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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("??")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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? {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ private extension FunctionDeclSyntax {
|
|||
|
||||
var isOperator: Bool {
|
||||
switch identifier.tokenKind {
|
||||
case .spacedBinaryOperator:
|
||||
case .binaryOperator:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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([]))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ private extension DynamicInlineRule {
|
|||
|
||||
private extension AttributeSyntax {
|
||||
var isInlineAlways: Bool {
|
||||
attributeName.text == "inline" &&
|
||||
attributeNameText == "inline" &&
|
||||
argument?.firstToken?.tokenKind == .identifier("__always")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(">")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -63,7 +63,7 @@ private extension ClosingBraceRule {
|
|||
}
|
||||
|
||||
correctionPositions.append(node.positionAfterSkippingLeadingTrivia)
|
||||
return super.visit(node.withTrailingTrivia(.zero))
|
||||
return super.visit(node.with(\.trailingTrivia, .zero))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ private extension EmptyParametersRule {
|
|||
}
|
||||
|
||||
correctionPositions.append(violationPosition)
|
||||
return super.visit(node.withArguments(TupleTypeElementListSyntax([])))
|
||||
return super.visit(node.with(\.arguments, TupleTypeElementListSyntax([])))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue