Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add consider_default_literal_types_redundant option to RedundantTypeAnnotationRule #4756

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
[Martin Redington](https://github.com/mildm8nnered)
[#4792](https://github.com/realm/SwiftLint/issues/4792)

* With the introduction of the `consider_default_literal_types_redundant`
option to the `redundant_type_annotation` rule, `Bool` literals will no
longer be considered redundant by default. Set this option to true to
preserve the previous behavior.
[Garric Nahapetian](https://github.com/garricn)

#### Experimental

* Add two new options to the `lint` and `analyze` commands: `--write-baseline`
Expand Down Expand Up @@ -188,6 +194,14 @@
[Martin Redington](https://github.com/mildm8nnered)
[#5470](https://github.com/realm/SwiftLint/issues/5470)

* Include `Double`, `Int` and `String` to the exiting redundant type validation
check of `Bool` in the `redundant_type_annotation` rule. Add
`consider_default_literal_types_redundant` option supporting `Bool`,
`Double`, `Int` and `String`. Setting this option to `true` lets the rule
consider said types in declarations like `let i: Int = 1` or
`let s: String = ""` as redundant.
[Garric Nahapetian](https://github.com/garricn)

#### Bug Fixes

* Silence `discarded_notification_center_observer` rule in closures. Furthermore,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule {
@IgnoreMe let i: Int = Int(1)
return i
}
""", configuration: ["ignore_attributes": ["IgnoreMe"]])
""", configuration: ["ignore_attributes": ["IgnoreMe"]]),
Example("var bol: Bool = true"),
Example("var dbl: Double = 0.0"),
Example("var int: Int = 0"),
Example("var str: String = \"str\"")
],
triggeringExamples: [
Example("var url↓:URL=URL()"),
Expand Down Expand Up @@ -84,7 +88,6 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule {
}
}
"""),
Example("var isEnabled↓: Bool = true"),
Example("let a↓: [Int] = [Int]()"),
Example("let a↓: A.B = A.B()"),
Example("""
Expand All @@ -102,7 +105,11 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule {
let i↓: Int = Int(1)
return i
}
""", configuration: ["ignore_attributes": ["IgnoreMe"]])
""", configuration: ["ignore_attributes": ["IgnoreMe"]]),
Example("var bol↓: Bool = true", configuration: ["consider_default_literal_types_redundant": true]),
Example("var dbl↓: Double = 0.0", configuration: ["consider_default_literal_types_redundant": true]),
Example("var int↓: Int = 0", configuration: ["consider_default_literal_types_redundant": true]),
Example("var str↓: String = \"str\"", configuration: ["consider_default_literal_types_redundant": true])
],
corrections: [
Example("var url↓: URL = URL()"): Example("var url = URL()"),
Expand Down Expand Up @@ -159,7 +166,15 @@ struct RedundantTypeAnnotationRule: OptInRule, SwiftSyntaxCorrectableRule {
let i = Int(1)
return i
}
""")
"""),
Example("var bol: Bool = true", configuration: ["consider_default_literal_types_redundant": true]):
Example("var bol = true"),
Example("var dbl: Double = 0.0", configuration: ["consider_default_literal_types_redundant": true]):
Example("var dbl = 0.0"),
Example("var int: Int = 0", configuration: ["consider_default_literal_types_redundant": true]):
Example("var int = 0"),
Example("var str: String = \"str\"", configuration: ["consider_default_literal_types_redundant": true]):
Example("var str = \"str\"")
]
)
}
Expand All @@ -168,52 +183,44 @@ private extension RedundantTypeAnnotationRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: PatternBindingSyntax) {
guard let varDecl = node.parent?.parent?.as(VariableDeclSyntax.self),
configuration.ignoreAttributes.allSatisfy({ !varDecl.attributes.contains(attributeNamed: $0) }) else {
configuration.ignoreAttributes.allSatisfy({ !varDecl.attributes.contains(attributeNamed: $0) }),
let typeAnnotation = node.typeAnnotation,
let initializer = node.initializer?.value else {
return
}
if let typeAnnotation = node.typeAnnotation,
let initializer = node.initializer?.value,
typeAnnotation.isRedundant(with: initializer) {
violations.append(typeAnnotation.positionAfterSkippingLeadingTrivia)
violationCorrections.append(ViolationCorrection(
start: typeAnnotation.position,
end: typeAnnotation.endPositionBeforeTrailingTrivia,
replacement: ""
))
let validateLiterals = configuration.considerDefaultLiteralTypesRedundant
let isLiteralRedundant = validateLiterals && initializer.hasRedundantLiteralType(typeAnnotation.type)
guard isLiteralRedundant || initializer.hasRedundantType(typeAnnotation.type) else {
return
}
violations.append(typeAnnotation.positionAfterSkippingLeadingTrivia)
violationCorrections.append(ViolationCorrection(
start: typeAnnotation.position,
end: typeAnnotation.endPositionBeforeTrailingTrivia,
replacement: ""
))
}

override func visitPost(_ node: OptionalBindingConditionSyntax) {
if let typeAnnotation = node.typeAnnotation,
let initializer = node.initializer?.value,
typeAnnotation.isRedundant(with: initializer) {
violations.append(typeAnnotation.positionAfterSkippingLeadingTrivia)
violationCorrections.append(ViolationCorrection(
start: typeAnnotation.position,
end: typeAnnotation.endPositionBeforeTrailingTrivia,
replacement: ""
))
guard let typeAnnotation = node.typeAnnotation,
let initializer = node.initializer?.value else {
return
}
let validateLiterals = configuration.considerDefaultLiteralTypesRedundant
let isLiteralRedundant = validateLiterals && initializer.hasRedundantLiteralType(typeAnnotation.type)
guard isLiteralRedundant || initializer.hasRedundantType(typeAnnotation.type) else {
return
}
violations.append(typeAnnotation.positionAfterSkippingLeadingTrivia)
violationCorrections.append(ViolationCorrection(
start: typeAnnotation.position,
end: typeAnnotation.endPositionBeforeTrailingTrivia,
replacement: ""
))
}
}
}

private extension TypeAnnotationSyntax {
func isRedundant(with initializerExpr: ExprSyntax) -> Bool {
var initializer = initializerExpr
if let forceUnwrap = initializer.as(ForceUnwrapExprSyntax.self) {
initializer = forceUnwrap.expression
}

// If the initializer is a boolean expression, we consider using the `Bool` type
// annotation as redundant.
if initializer.is(BooleanLiteralExprSyntax.self) {
return type.trimmedDescription == "Bool"
}
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
Expand All @@ -233,4 +240,33 @@ private extension ExprSyntax {
[]
}
}

func hasRedundantLiteralType(_ type: TypeSyntax) -> Bool {
type.trimmedDescription == kind.compilerInferredLiteralType
}

func hasRedundantType(_ type: TypeSyntax) -> Bool {
var expr = self
if let forceUnwrap = expr.as(ForceUnwrapExprSyntax.self) {
expr = forceUnwrap.expression
}
return expr.accessedNames.contains(type.trimmedDescription)
}
}

private extension SyntaxKind {
var compilerInferredLiteralType: String? {
switch self {
case .booleanLiteralExpr:
"Bool"
case .floatLiteralExpr:
"Double"
case .integerLiteralExpr:
"Int"
case .stringLiteralExpr:
"String"
default:
nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ struct RedundantTypeAnnotationConfiguration: SeverityBasedRuleConfiguration {
var severityConfiguration = SeverityConfiguration<Parent>(.warning)
@ConfigurationElement(key: "ignore_attributes")
var ignoreAttributes = Set<String>(["IBInspectable"])
@ConfigurationElement(key: "consider_default_literal_types_redundant")
private(set) var considerDefaultLiteralTypesRedundant = false
}
1 change: 1 addition & 0 deletions Tests/IntegrationTests/default_rule_configurations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ redundant_string_enum_value:
redundant_type_annotation:
severity: warning
ignore_attributes: ["IBInspectable"]
consider_default_literal_types_redundant: false
redundant_void_return:
severity: warning
include_closures: true
Expand Down