Skip to content

Commit

Permalink
Fix issues with explicit_type_interface on Swift 5.4
Browse files Browse the repository at this point in the history
Fixes #3615
  • Loading branch information
marcelofabri committed Apr 29, 2021
1 parent ff5ddf1 commit ed2c2d0
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 10 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
[Marcelo Fabri](https://github.com/marcelofabri)
[#3562](https://github.com/realm/SwiftLint/issues/3562)

* Fix `unused_capture_list`, `empty_enum_arguments` and `implicit_return` rules
when using Swift 5.4.
* Fix `unused_capture_list`, `empty_enum_arguments`, `implicit_return` and
`explicit_type_interface` rules when using Swift 5.4.
[Marcelo Fabri](https://github.com/marcelofabri)
[#3615](https://github.com/realm/SwiftLint/issues/3615)

## 0.43.1: Laundroformat

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ public struct ExplicitTypeInterfaceRule: OptInRule, ConfigurationProviderRule {
)

public func validate(file: SwiftLintFile) -> [StyleViolation] {
return file.structureDictionary.traverseWithParentDepthFirst { parent, subDict in
guard let kind = subDict.declarationKind else { return nil }
return file.structureDictionary.traverseWithParentsDepthFirst { parents, subDict in
guard let kind = subDict.declarationKind,
let parent = parents.lastIgnoringCallAndArgument() else {
return nil
}
return validate(file: file, kind: kind, dictionary: subDict, parentStructure: parent)
}
}
Expand Down Expand Up @@ -178,3 +181,44 @@ private extension Collection where Element == ByteRange {
return contains { $0.contains(index) }
}
}

private extension SourceKittenDictionary {
func traverseWithParentsDepthFirst<T>(traverseBlock: ([SourceKittenDictionary], SourceKittenDictionary) -> [T]?)
-> [T] {
var result: [T] = []
traverseWithParentDepthFirst(collectingValuesInto: &result,
parents: [],
traverseBlock: traverseBlock)
return result
}

private func traverseWithParentDepthFirst<T>(
collectingValuesInto array: inout [T],
parents: [SourceKittenDictionary],
traverseBlock: ([SourceKittenDictionary], SourceKittenDictionary) -> [T]?) {
var updatedParents = parents
updatedParents.append(self)

substructure.forEach { subDict in
subDict.traverseWithParentDepthFirst(collectingValuesInto: &array,
parents: updatedParents,
traverseBlock: traverseBlock)

if let collectedValues = traverseBlock(updatedParents, subDict) {
array += collectedValues
}
}
}
}

private extension Array where Element == SourceKittenDictionary {
func lastIgnoringCallAndArgument() -> Element? {
guard SwiftVersion.current >= .fiveDotFour else {
return last
}

return last { element in
element.expressionKind != .call && element.expressionKind != .argument
}
}
}
43 changes: 37 additions & 6 deletions Tests/SwiftLintFrameworkTests/ExplicitTypeInterfaceRuleTests.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
import SwiftLintFramework
import XCTest

// swiftlint:disable:next type_body_length
class ExplicitTypeInterfaceRuleTests: XCTestCase {
func testExplicitTypeInterface() {
verifyRule(ExplicitTypeInterfaceRule.description)
}

func testLocalVars() {
let nonTriggeringExamples = [
Example("func foo() {\nlet intVal: Int = 1\n}"),
Example("""
func foo() {
bar {
let x: Int = 1
}
}
""")
]
let triggeringExamples = [
Example("func foo() {\n↓let intVal = 1\n}"),
Example("""
func foo() {
bar {
↓let x = 1
}
}
""")
]
let description = ExplicitTypeInterfaceRule.description
.with(triggeringExamples: triggeringExamples)
.with(nonTriggeringExamples: nonTriggeringExamples)

verifyRule(description)
}

func testExcludeLocalVars() {
let nonTriggeringExamples = ExplicitTypeInterfaceRule.description.nonTriggeringExamples + [
Example("func foo() {\nlet intVal = 1\n}")
Expand Down Expand Up @@ -160,12 +189,14 @@ class ExplicitTypeInterfaceRuleTests: XCTestCase {
enum Foo {
case failure(Any)
case success(Any)
}
func bar() {
let foo: Foo = .success(1)
switch foo {
case .failure(let error): let bar: Int = 1
case .success(let result): let bar: Int = 2
}
func bar() {
let foo: Foo = .success(1)
switch foo {
case .failure(let error):
let bar: Int = 1
case .success(let result):
let bar: Int = 2
}
}
"""),
Expand Down

0 comments on commit ed2c2d0

Please sign in to comment.