Files
SwiftLint/Source/SwiftLintBuiltInRules/Rules/Lint/NSObjectPreferIsEqualRule.swift
T
Danny Mösch 58928b7e40 Enforce any on existential types (#5273)
This makes syntactically clear which types are rather expensive.
2023-10-12 08:37:23 +02:00

86 lines
2.5 KiB
Swift

import SwiftSyntax
@SwiftSyntaxRule
struct NSObjectPreferIsEqualRule: ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)
static let description = RuleDescription(
identifier: "nsobject_prefer_isequal",
name: "NSObject Prefer isEqual",
description: "NSObject subclasses should implement isEqual instead of ==",
kind: .lint,
nonTriggeringExamples: NSObjectPreferIsEqualRuleExamples.nonTriggeringExamples,
triggeringExamples: NSObjectPreferIsEqualRuleExamples.triggeringExamples
)
}
private extension NSObjectPreferIsEqualRule {
final class Visitor: ViolationsSyntaxVisitor {
override var skippableDeclarations: [any DeclSyntaxProtocol.Type] { .extensionsAndProtocols }
override func visitPost(_ node: FunctionDeclSyntax) {
if node.isSelfEqualFunction, node.isInObjcClass {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
}
}
private extension ClassDeclSyntax {
var isObjC: Bool {
if attributes.isObjc {
return true
}
guard let inheritanceList = inheritanceClause?.inheritedTypes else {
return false
}
return inheritanceList.contains { type in
type.type.as(IdentifierTypeSyntax.self)?.name.text == "NSObject"
}
}
}
private extension FunctionDeclSyntax {
var isSelfEqualFunction: Bool {
guard
modifiers.contains(keyword: .static),
name.text == "==",
returnsBool,
case let parameterList = signature.parameterClause.parameters,
parameterList.count == 2,
let lhs = parameterList.first,
let rhs = parameterList.last,
lhs.firstName.text == "lhs",
rhs.firstName.text == "rhs",
lhs.type.trimmedDescription == rhs.type.trimmedDescription
else {
return false
}
return true
}
var returnsBool: Bool {
signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text == "Bool"
}
}
private extension SyntaxProtocol {
var isInObjcClass: Bool {
if let parentClass = parent?.as(ClassDeclSyntax.self) {
return parentClass.isObjC
} else if parent?.as(DeclSyntax.self) != nil {
return false
}
return parent?.isInObjcClass ?? false
}
}
private extension AttributeListSyntax {
var isObjc: Bool {
contains(attributeNamed: "objc") || contains(attributeNamed: "objcMembers")
}
}