Skip to content

Commit

Permalink
add byterange to the dictionary and breadthfirst algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulTaykalo committed Nov 10, 2019
1 parent 73802c2 commit 61a6a27
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 52 deletions.
1 change: 0 additions & 1 deletion Source/SwiftLintFramework/Extensions/Array+SwiftLint.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Dispatch
import Foundation
import SourceKittenFramework

extension Array where Element: NSTextCheckingResult {
func ranges() -> [NSRange] {
Expand Down
26 changes: 26 additions & 0 deletions Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Foundation
import SourceKittenFramework

public struct SourceKittenDictionary {
Expand Down Expand Up @@ -64,6 +65,12 @@ public struct SourceKittenDictionary {
return (value["key.offset"] as? Int64).flatMap({ Int($0) })
}

/// Returns byte range startif from `offset` with `length` bytes
var byteRange: NSRange? {
guard let offset = offset, let length = length else { return nil }
return NSRange(location: offset, length: length)
}

/// Setter accessibility.
var setterAccessibility: String? {
return value["key.setter_accessibility"] as? String
Expand Down Expand Up @@ -195,6 +202,25 @@ extension SourceKittenDictionary {
}
}
}

/// Traversing all substuctures of the dictionary hierarchically, calling `traverseBlock` on each node.
/// Traversing using breadth first strategy, so deepest substructures will be passed to `traverseBlock` last.
/// - parameter traverseBlock: block that will be called for each substructure in the dictionary.
func traverseBreadthFirst<T>(traverseBlock: (SourceKittenDictionary) -> [T]?) -> [T] {
var result: [T] = []
traverseBreadthFirst(collectingValuesInto: &result, traverseBlock: traverseBlock)
return result
}

private func traverseBreadthFirst<T>(collectingValuesInto array: inout [T],
traverseBlock: (SourceKittenDictionary) -> [T]?) {
substructure.forEach { subDict in
if let collectedValues = traverseBlock(subDict) {
array += collectedValues
}
subDict.traverseDepthFirst(collectingValuesInto: &array, traverseBlock: traverseBlock)
}
}
}

extension Dictionary where Key == String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ public struct SyntacticSugarRule: SubstitutionCorrectableRule, ConfigurationProv
return false
}

let kinds = file.structureDictionary.kinds(forByteOffset: byteOffset)
.compactMap { SwiftExpressionKind(rawValue: $0.kind) }
let kinds = file.structureDictionary.structures(forByteOffset: byteOffset).compactMap { $0.expressionKind }
guard kinds.contains(.call) else {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro

private extension SourceKittenDictionary {
func functions(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()

func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset,
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }),
NSLocationInRange(byteOffset, byteRange) else {
return
}

if let kind = dictionary.declarationKind,
SwiftDeclarationKind.functionKinds.contains(kind) {
results.append(dictionary)
}
dictionary.substructure.forEach(parse)
}
parse(self)
return results
return structures(forByteOffset: byteOffset)
.filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,13 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi

private func localVariableDeclarations(inByteRange byteRange: NSRange,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()

func parse(dictionary: SourceKittenDictionary) {
if dictionary.declarationKind == .varLocal,
let offset = dictionary.offset,
let length = dictionary.length {
let variableByteRange = NSRange(location: offset, length: length)

if byteRange.intersects(variableByteRange) {
results.append(dictionary)
}
return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .varLocal,
let variableByteRange = dictionary.byteRange,
byteRange.intersects(variableByteRange) else {
return nil
}
dictionary.substructure.forEach(parse)
return [dictionary]
}
parse(dictionary: structureDictionary)
return results
}
}
22 changes: 6 additions & 16 deletions Source/SwiftLintFramework/Rules/Lint/WeakDelegateRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,13 @@ public struct WeakDelegateRule: ASTRule, ConfigurationProviderRule, AutomaticTes

private func protocolDeclarations(forByteOffset byteOffset: Int,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()

func parse(dictionary: SourceKittenDictionary) {
// Only accepts protocols declarations which contains a body and contains the
// searched byteOffset
if dictionary.declarationKind == .protocol,
let offset = dictionary.bodyOffset,
let length = dictionary.bodyLength {
let byteRange = NSRange(location: offset, length: length)

if NSLocationInRange(byteOffset, byteRange) {
results.append(dictionary)
}
return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .protocol,
let byteRange = dictionary.byteRange,
NSLocationInRange(byteOffset, byteRange) else {
return nil
}
dictionary.substructure.forEach(parse)
return [dictionary]
}
parse(dictionary: structureDictionary)
return results
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ public struct ControlStatementRule: ConfigurationProviderRule, AutomaticTestable
.filter { match, _ -> Bool in
let contents = file.contents.bridge()
guard let byteOffset = contents.NSRangeToByteRange(start: match.location, length: 1)?.location,
let outerKind = file.structureDictionary.kinds(forByteOffset: byteOffset).last else {
let outerKind = file.structureDictionary.structures(forByteOffset: byteOffset).last else {
return true
}

return SwiftExpressionKind(rawValue: outerKind.kind) != .call
return outerKind.expressionKind != .call
}
.map { match, _ -> StyleViolation in
return StyleViolation(ruleDescription: type(of: self).description,
Expand Down

0 comments on commit 61a6a27

Please sign in to comment.