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

Cache Structure Dictionaries #2922

Merged
merged 5 commits into from
Nov 7, 2019
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

#### Breaking

* None.
* Use SourceKittenDictionary wrapper over dictionaries
returned from SourceKitten
[PaulTaykalo](https://github.com/PaulTaykalo)
[#2922](https://github.com/realm/SwiftLint/issues/2922)

#### Experimental

Expand Down
16 changes: 8 additions & 8 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -24981,13 +24981,13 @@ Function parameters should be aligned vertically if they're in multiple lines in

```swift
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) { }
dictionary: SourceKittenDictionary) { }

```

```swift
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation]
dictionary: SourceKittenDictionary) -> [StyleViolation]

```

Expand All @@ -25003,22 +25003,22 @@ func foo(bar: Int) -> String

```swift
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable])
dictionary: SourceKittenDictionary)
-> [StyleViolation]

```

```swift
func validateFunction(
_ file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation]
dictionary: SourceKittenDictionary) -> [StyleViolation]

```

```swift
func validateFunction(
_ file: File, kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]
dictionary: SourceKittenDictionary
) -> [StyleViolation]

```
Expand Down Expand Up @@ -25054,20 +25054,20 @@ func foo(data: Data,

```swift
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
↓dictionary: [String: SourceKitRepresentable]) { }
↓dictionary: SourceKittenDictionary) { }

```

```swift
func validateFunction(_ file: File, kind: SwiftDeclarationKind,
↓dictionary: [String: SourceKitRepresentable]) { }
↓dictionary: SourceKittenDictionary) { }

```

```swift
func validateFunction(_ file: File,
↓kind: SwiftDeclarationKind,
↓dictionary: [String: SourceKitRepresentable]) { }
↓dictionary: SourceKittenDictionary) { }

```

Expand Down
87 changes: 53 additions & 34 deletions Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift
Original file line number Diff line number Diff line change
@@ -1,87 +1,106 @@
import SourceKittenFramework

extension Dictionary where Key: ExpressibleByStringLiteral {
public struct SourceKittenDictionary {
public let value: [String: SourceKitRepresentable]
public let substructure: [SourceKittenDictionary]

init(_ value: [String: SourceKitRepresentable]) {
self.value = value

let substructure = value["key.substructure"] as? [SourceKitRepresentable] ?? []
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caching is mostly what this PR is about

self.substructure = substructure.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(SourceKittenDictionary.init)
}

/// Accessibility.
var accessibility: String? {
return self["key.accessibility"] as? String
return value["key.accessibility"] as? String
}
/// Body length.

/// Body length
var bodyLength: Int? {
return (self["key.bodylength"] as? Int64).flatMap({ Int($0) })
return (value["key.bodylength"] as? Int64).flatMap({ Int($0) })
}

/// Body offset.
var bodyOffset: Int? {
return (self["key.bodyoffset"] as? Int64).flatMap({ Int($0) })
return (value["key.bodyoffset"] as? Int64).flatMap({ Int($0) })
}

/// Kind.
var kind: String? {
return self["key.kind"] as? String
return value["key.kind"] as? String
}

/// Length.
var length: Int? {
return (self["key.length"] as? Int64).flatMap({ Int($0) })
return (value["key.length"] as? Int64).flatMap({ Int($0) })
}
/// Name.
var name: String? {
return self["key.name"] as? String
return value["key.name"] as? String
}

/// Name length.
var nameLength: Int? {
return (self["key.namelength"] as? Int64).flatMap({ Int($0) })
return (value["key.namelength"] as? Int64).flatMap({ Int($0) })
}

/// Name offset.
var nameOffset: Int? {
return (self["key.nameoffset"] as? Int64).flatMap({ Int($0) })
return (value["key.nameoffset"] as? Int64).flatMap({ Int($0) })
}

/// Offset.
var offset: Int? {
return (self["key.offset"] as? Int64).flatMap({ Int($0) })
return (value["key.offset"] as? Int64).flatMap({ Int($0) })
}

/// Setter accessibility.
var setterAccessibility: String? {
return self["key.setter_accessibility"] as? String
return value["key.setter_accessibility"] as? String
}

/// Type name.
var typeName: String? {
return self["key.typename"] as? String
return value["key.typename"] as? String
}

/// Documentation length.
var docLength: Int? {
return (self["key.doclength"] as? Int64).flatMap({ Int($0) })
return (value["key.doclength"] as? Int64).flatMap({ Int($0) })
}

var attribute: String? {
return self["key.attribute"] as? String
return value["key.attribute"] as? String
}

var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
return swiftAttributes.compactMap { $0.attribute }
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
}

var swiftAttributes: [[String: SourceKitRepresentable]] {
let array = self["key.attributes"] as? [SourceKitRepresentable] ?? []
let dictionaries = array.compactMap { ($0 as? [String: SourceKitRepresentable]) }
var swiftAttributes: [SourceKittenDictionary] {
let array = value["key.attributes"] as? [SourceKitRepresentable] ?? []
let dictionaries = array.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(SourceKittenDictionary.init)
return dictionaries
}

var substructure: [[String: SourceKitRepresentable]] {
let substructure = self["key.substructure"] as? [SourceKitRepresentable] ?? []
return substructure.compactMap { $0 as? [String: SourceKitRepresentable] }
}

var elements: [[String: SourceKitRepresentable]] {
let elements = self["key.elements"] as? [SourceKitRepresentable] ?? []
var elements: [SourceKittenDictionary] {
let elements = value["key.elements"] as? [SourceKitRepresentable] ?? []
return elements.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(SourceKittenDictionary.init)
}

var entities: [[String: SourceKitRepresentable]] {
let entities = self["key.entities"] as? [SourceKitRepresentable] ?? []
var entities: [SourceKittenDictionary] {
let entities = value["key.entities"] as? [SourceKitRepresentable] ?? []
return entities.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(SourceKittenDictionary.init)
}

var enclosedVarParameters: [[String: SourceKitRepresentable]] {
return substructure.flatMap { subDict -> [[String: SourceKitRepresentable]] in
var enclosedVarParameters: [SourceKittenDictionary] {
return substructure.flatMap { subDict -> [SourceKittenDictionary] in
guard let kindString = subDict.kind else {
return []
}
Expand All @@ -97,8 +116,8 @@ extension Dictionary where Key: ExpressibleByStringLiteral {
}
}

var enclosedArguments: [[String: SourceKitRepresentable]] {
return substructure.flatMap { subDict -> [[String: SourceKitRepresentable]] in
var enclosedArguments: [SourceKittenDictionary] {
return substructure.flatMap { subDict -> [SourceKittenDictionary] in
guard let kindString = subDict.kind,
SwiftExpressionKind(rawValue: kindString) == .argument else {
return []
Expand All @@ -109,8 +128,8 @@ extension Dictionary where Key: ExpressibleByStringLiteral {
}

var inheritedTypes: [String] {
let array = self["key.inheritedtypes"] as? [SourceKitRepresentable] ?? []
return array.compactMap { ($0 as? [String: String])?.name }
let array = value["key.inheritedtypes"] as? [SourceKitRepresentable] ?? []
return array.compactMap { ($0 as? [String: String]).flatMap { $0["key.name"] } }
}

internal func extractCallsToSuper(methodName: String) -> [String] {
Expand Down
18 changes: 18 additions & 0 deletions Source/SwiftLintFramework/Extensions/File+Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ private var structureCache = Cache({ file -> Structure? in
}
return nil
})

private var structureDictionaryCache = Cache({ file in
return structureCache.get(file).map { SourceKittenDictionary($0.dictionary) }
PaulTaykalo marked this conversation as resolved.
Show resolved Hide resolved
})

private var syntaxMapCache = Cache({ file in responseCache.get(file).map(SyntaxMap.init) })
private var syntaxKindsByLinesCache = Cache({ file in file.syntaxKindsByLine() })
private var syntaxTokensByLinesCache = Cache({ file in file.syntaxTokensByLine() })
Expand Down Expand Up @@ -128,6 +133,17 @@ extension File {
return structure
}

internal var structureDictionary: SourceKittenDictionary {
guard let structureDictionary = structureDictionaryCache.get(self) else {
if let handler = assertHandler {
handler()
return SourceKittenDictionary([:])
}
queuedFatalError("Never call this for file that sourcekitd fails.")
}
return structureDictionary
}

internal var syntaxMap: SyntaxMap {
guard let syntaxMap = syntaxMapCache.get(self) else {
if let handler = assertHandler {
Expand Down Expand Up @@ -165,6 +181,7 @@ extension File {
responseCache.invalidate(self)
assertHandlerCache.invalidate(self)
structureCache.invalidate(self)
structureDictionaryCache.invalidate(self)
syntaxMapCache.invalidate(self)
syntaxTokensByLinesCache.invalidate(self)
syntaxKindsByLinesCache.invalidate(self)
Expand All @@ -175,6 +192,7 @@ extension File {
responseCache.clear()
assertHandlerCache.clear()
structureCache.clear()
structureDictionaryCache.clear()
syntaxMapCache.clear()
syntaxTokensByLinesCache.clear()
syntaxKindsByLinesCache.clear()
Expand Down
2 changes: 1 addition & 1 deletion Source/SwiftLintFramework/Extensions/File+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ extension File {
}
var results = [[SwiftDeclarationKind]](repeating: [], count: lines.count + 1)
var lineIterator = lines.makeIterator()
var structureIterator = structure.kinds().makeIterator()
var structureIterator = structureDictionary.kinds().makeIterator()
var maybeLine = lineIterator.next()
var maybeStructure = structureIterator.next()
while let line = maybeLine, let structure = maybeStructure {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Foundation
import SourceKittenFramework

extension Structure {
extension SourceKittenDictionary {
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure
/// that contains the byte offset. Returns all kinds if no parameter specified.
///
/// - Parameter byteOffset: Int?
internal func kinds(forByteOffset byteOffset: Int? = nil) -> [(kind: String, byteRange: NSRange)] {
internal func kinds(forByteOffset byteOffset: Int? = nil)
-> [(kind: String, byteRange: NSRange)] {
var results = [(kind: String, byteRange: NSRange)]()

func parse(_ dictionary: [String: SourceKitRepresentable]) {
func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset,
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }) else {
return
Expand All @@ -22,14 +22,14 @@ extension Structure {
}
dictionary.substructure.forEach(parse)
}
parse(dictionary)
parse(self)
return results
}

internal func structures(forByteOffset byteOffset: Int) -> [[String: SourceKitRepresentable]] {
var results = [[String: SourceKitRepresentable]]()
internal func structures(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()

func parse(_ dictionary: [String: SourceKitRepresentable]) {
func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset,
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }),
NSLocationInRange(byteOffset, byteRange) else {
Expand All @@ -39,7 +39,7 @@ extension Structure {
results.append(dictionary)
dictionary.substructure.forEach(parse)
}
parse(dictionary)
parse(self)
return results
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extension String {
return self == lowercased()
}

internal func nameStrippingLeadingUnderscoreIfPrivate(_ dict: [String: SourceKitRepresentable]) -> String {
internal func nameStrippingLeadingUnderscoreIfPrivate(_ dict: SourceKittenDictionary) -> String {
if let aclString = dict.accessibility,
let acl = AccessControlLevel(identifier: aclString),
acl.isPrivate && first == "_" {
Expand Down
10 changes: 5 additions & 5 deletions Source/SwiftLintFramework/Helpers/NamespaceCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ struct NamespaceCollector {
let name: String
let kind: SwiftDeclarationKind
let offset: Int
let dictionary: [String: SourceKitRepresentable]
let dictionary: SourceKittenDictionary

init?(dictionary: [String: SourceKitRepresentable], namespace: [String]) {
init?(dictionary: SourceKittenDictionary, namespace: [String]) {
guard let name = dictionary.name,
let kind = dictionary.kind.flatMap(SwiftDeclarationKind.init),
let offset = dictionary.offset else {
Expand All @@ -21,9 +21,9 @@ struct NamespaceCollector {
}
}

private let dictionary: [String: SourceKitRepresentable]
private let dictionary: SourceKittenDictionary

init(dictionary: [String: SourceKitRepresentable]) {
init(dictionary: SourceKittenDictionary) {
self.dictionary = dictionary
}

Expand All @@ -32,7 +32,7 @@ struct NamespaceCollector {
return findAllElements(in: dictionary, of: types, namespace: namespace)
}

private func findAllElements(in dictionary: [String: SourceKitRepresentable],
private func findAllElements(in dictionary: SourceKittenDictionary,
of types: Set<SwiftDeclarationKind>,
namespace: [String] = []) -> [Element] {
return dictionary.substructure.flatMap { subDict -> [Element] in
Expand Down
3 changes: 2 additions & 1 deletion Source/SwiftLintFramework/Models/SwiftVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ public extension SwiftVersion {
return token.type == SyntaxKind.string.rawValue
}
if !Request.disableSourceKit,
let decl = file.structure.kinds().first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
let decl = file.structureDictionary.kinds()
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: isString ) {
return .init(rawValue: file.contents.substring(from: token.offset + 1, length: token.length - 2))
}
Expand Down
Loading