Skip to content

Commit

Permalink
Respect nested types in redundant_type_annotation rule (realm#5537)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimplyDanny authored Apr 21, 2024
1 parent e643f21 commit 3276266
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 33 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,10 @@
[Martin Redington](https://github.com/mildm8nnered)
[#5305](https://github.com/realm/SwiftLint/pull/5305)

* Take array types into account in `redundant_type_annotation` rule.
* Take array and nested types into account in `redundant_type_annotation` rule.
[SimplyDanny](https://github.com/SimplyDanny)
[#3141](https://github.com/realm/SwiftLint/pull/3141)
[#3146](https://github.com/realm/SwiftLint/pull/3146)

* Silence `pattern_matching_keywords` rule when an identifier is referenced
in the argument list of a matching enum case.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule {
"""),
Example("var isEnabled↓: Bool = true"),
Example("let a↓: [Int] = [Int]()"),
Example("let a↓: A.B = A.B()"),
Example("""
enum Direction {
case up
Expand Down Expand Up @@ -199,9 +200,6 @@ private extension RedundantTypeAnnotationRule {

private extension TypeAnnotationSyntax {
func isRedundant(with initializerExpr: ExprSyntax) -> Bool {
guard let typeName = type.name else {
return false
}
var initializer = initializerExpr
if let forceUnwrap = initializer.as(ForceUnwrapExprSyntax.self) {
initializer = forceUnwrap.expression
Expand All @@ -210,44 +208,29 @@ private extension TypeAnnotationSyntax {
// If the initializer is a boolean expression, we consider using the `Bool` type
// annotation as redundant.
if initializer.is(BooleanLiteralExprSyntax.self) {
return typeName == "Bool"
return type.trimmedDescription == "Bool"
}
return initializer.firstAccessNames.contains(typeName)
return initializer.accessedNames.contains(type.trimmedDescription)
}
}

private extension ExprSyntax {
/// An expression can represent an access to an identifier in one or another way depending on the exact underlying
/// expression type. E.g. the expression `A` accesses `A` while `f()` accesses `f` and `a.b.c` accesses `a` in the
/// sense of this property. In the context of this rule, `Set<Int>()` accesses `Set` as well as `Set<Int>`.
var firstAccessNames: [String] {
var accessedNames: [String] {
if let declRef = `as`(DeclReferenceExprSyntax.self) {
return [declRef.trimmedDescription]
}
if let memberAccess = `as`(MemberAccessExprSyntax.self) {
return memberAccess.base?.firstAccessNames ?? []
}
if let genericSpecialization = `as`(GenericSpecializationExprSyntax.self) {
return [genericSpecialization.trimmedDescription] + genericSpecialization.expression.firstAccessNames
}
if let call = `as`(FunctionCallExprSyntax.self) {
return call.calledExpression.firstAccessNames
}
if let arrayExpr = `as`(ArrayExprSyntax.self) {
return [arrayExpr.trimmedDescription]
}
return []
}
}

private extension TypeSyntax {
var name: String? {
if let idType = `as`(IdentifierTypeSyntax.self) {
return idType.trimmedDescription
}
if let arrayType = `as`(ArrayTypeSyntax.self) {
return arrayType.trimmedDescription
[declRef.trimmedDescription]
} else if let memberAccess = `as`(MemberAccessExprSyntax.self) {
(memberAccess.base?.accessedNames ?? []) + [memberAccess.trimmedDescription]
} else if let genericSpecialization = `as`(GenericSpecializationExprSyntax.self) {
[genericSpecialization.trimmedDescription] + genericSpecialization.expression.accessedNames
} else if let call = `as`(FunctionCallExprSyntax.self) {
call.calledExpression.accessedNames
} else if let arrayExpr = `as`(ArrayExprSyntax.self) {
[arrayExpr.trimmedDescription]
} else {
[]
}
return nil
}
}

0 comments on commit 3276266

Please sign in to comment.