Skip to content

Commit

Permalink
Add own wrappers over syntax tokens and syntax map
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulTaykalo committed Nov 10, 2019
1 parent 944bea4 commit dd32040
Show file tree
Hide file tree
Showing 37 changed files with 170 additions and 161 deletions.
6 changes: 0 additions & 6 deletions Source/SwiftLintFramework/Extensions/Array+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ extension Array where Element: NSTextCheckingResult {
}
}

extension Array where Element == SyntaxToken {
var kinds: [SyntaxKind] {
return compactMap { SyntaxKind(rawValue: $0.type) }
}
}

extension Array where Element: Equatable {
var unique: [Element] {
var uniqueValues = [Element]()
Expand Down
10 changes: 6 additions & 4 deletions Source/SwiftLintFramework/Extensions/SwiftLintFile+Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ private var structureDictionaryCache = Cache({ file in
return structureCache.get(file).map { SourceKittenDictionary($0.dictionary) }
})

private var syntaxMapCache = Cache({ file in responseCache.get(file).map(SyntaxMap.init) })
private var syntaxMapCache = Cache({ file in
responseCache.get(file).map { SwiftLintSyntaxMap(value: SyntaxMap(sourceKitResponse: $0)) }
})
private var syntaxKindsByLinesCache = Cache({ file in file.syntaxKindsByLine() })
private var syntaxTokensByLinesCache = Cache({ file in file.syntaxTokensByLine() })

Expand Down Expand Up @@ -145,18 +147,18 @@ extension SwiftLintFile {
return structureDictionary
}

internal var syntaxMap: SyntaxMap {
internal var syntaxMap: SwiftLintSyntaxMap {
guard let syntaxMap = syntaxMapCache.get(self) else {
if let handler = assertHandler {
handler()
return SyntaxMap(data: [])
return SwiftLintSyntaxMap(value: SyntaxMap(data: []))
}
queuedFatalError("Never call this for file that sourcekitd fails.")
}
return syntaxMap
}

internal var syntaxTokensByLines: [[SyntaxToken]] {
internal var syntaxTokensByLines: [[SwiftLintSyntaxToken]] {
guard let syntaxTokensByLines = syntaxTokensByLinesCache.get(self) else {
if let handler = assertHandler {
handler()
Expand Down
19 changes: 9 additions & 10 deletions Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ extension SwiftLintFile {
}

internal func matchesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SyntaxToken])] {
range: NSRange? = nil) -> [(NSTextCheckingResult, [SwiftLintSyntaxToken])] {
let contents = self.contents.bridge()
let range = range ?? NSRange(location: 0, length: contents.length)
let syntax = syntaxMap
Expand All @@ -115,7 +115,7 @@ extension SwiftLintFile {
}

internal func rangesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSRange, [SyntaxToken])] {
range: NSRange? = nil) -> [(NSRange, [SwiftLintSyntaxToken])] {
return matchesAndTokens(matching: pattern, range: range).map { ($0.0.range, $0.1) }
}

Expand Down Expand Up @@ -149,17 +149,17 @@ extension SwiftLintFile {
return results
}

internal func syntaxTokensByLine() -> [[SyntaxToken]]? {
internal func syntaxTokensByLine() -> [[SwiftLintSyntaxToken]]? {
if sourcekitdFailed {
return nil
}
var results = [[SyntaxToken]](repeating: [], count: lines.count + 1)
var results = [[SwiftLintSyntaxToken]](repeating: [], count: lines.count + 1)
var tokenGenerator = syntaxMap.tokens.makeIterator()
var lineGenerator = lines.makeIterator()
var maybeLine = lineGenerator.next()
var maybeToken = tokenGenerator.next()
while let line = maybeLine, let token = maybeToken {
let tokenRange = NSRange(location: token.offset, length: token.length)
let tokenRange = token.range
if NSLocationInRange(token.offset, line.byteRange) ||
NSLocationInRange(line.byteRange.location, tokenRange) {
results[line.index].append(token)
Expand Down Expand Up @@ -317,17 +317,16 @@ extension SwiftLintFile {
return corrections
}

internal func isACL(token: SyntaxToken) -> Bool {
guard SyntaxKind(rawValue: token.type) == .attributeBuiltin else {
internal func isACL(token: SwiftLintSyntaxToken) -> Bool {
guard token.kind == .attributeBuiltin else {
return false
}

let aclString = contents.bridge().substringWithByteRange(start: token.offset,
length: token.length)
let aclString = contents(for: token)
return aclString.flatMap(AccessControlLevel.init(description:)) != nil
}

internal func contents(for token: SyntaxToken) -> String? {
internal func contents(for token: SwiftLintSyntaxToken) -> String? {
return contents.bridge().substringWithByteRange(start: token.offset,
length: token.length)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import Foundation
import SourceKittenFramework

extension SyntaxMap {
public struct SwiftLintSyntaxMap {
public let value: SyntaxMap
public let tokens: [SwiftLintSyntaxToken]

public init(value: SyntaxMap) {
self.value = value
self.tokens = value.tokens.map(SwiftLintSyntaxToken.init)
}

/// Returns array of SyntaxTokens intersecting with byte range
///
/// - Parameter byteRange: byte based NSRange
internal func tokens(inByteRange byteRange: NSRange) -> [SyntaxToken] {
func intersect(_ token: SyntaxToken) -> Bool {
return NSRange(location: token.offset, length: token.length)
.intersects(byteRange)
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
return token.range.intersects(byteRange)
}

func intersectsOrAfter(_ token: SyntaxToken) -> Bool {
func intersectsOrAfter(_ token: SwiftLintSyntaxToken) -> Bool {
return token.offset + token.length > byteRange.location
}

Expand All @@ -29,6 +36,6 @@ extension SyntaxMap {
}

internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
return tokens(inByteRange: byteRange).kinds
return tokens(inByteRange: byteRange).compactMap { $0.kind }
}
}
30 changes: 30 additions & 0 deletions Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation
import SourceKittenFramework

public struct SwiftLintSyntaxToken {
public let value: SyntaxToken
public let kind: SyntaxKind?

public init(value: SyntaxToken) {
self.value = value
kind = SyntaxKind(rawValue: value.type)
}

public var range: NSRange {
return NSRange(location: value.offset, length: value.length)
}

public var offset: Int {
return value.offset
}

public var length: Int {
return value.length
}
}

extension Array where Element == SwiftLintSyntaxToken {
var kinds: [SyntaxKind] {
return compactMap { $0.kind }
}
}
7 changes: 2 additions & 5 deletions Source/SwiftLintFramework/Models/SwiftVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public extension SwiftVersion {

if !Request.disableSourceKit,
case let dynamicCallableFile = SwiftLintFile(contents: "@dynamicCallable"),
dynamicCallableFile.syntaxMap.tokens.compactMap({ SyntaxKind(rawValue: $0.type) }) == [.attributeID] {
dynamicCallableFile.syntaxMap.tokens.compactMap({ $0.kind }) == [.attributeID] {
return .five
}

Expand Down Expand Up @@ -100,13 +100,10 @@ public extension SwiftVersion {
let version = "3.0.0"
#endif
""")
func isString(token: SyntaxToken) -> Bool {
return token.type == SyntaxKind.string.rawValue
}
if !Request.disableSourceKit,
let decl = file.structureDictionary.kinds()
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: isString ) {
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) {
return .init(rawValue: file.contents.substring(from: token.offset + 1, length: token.length - 2))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public struct DuplicateImportsRule: ConfigurationProviderRule, AutomaticTestable
let contents = file.contents.bridge()

let ranges = file.syntaxMap.tokens
.filter { SyntaxKind(rawValue: $0.type) == .buildconfigKeyword }
.map { NSRange(location: $0.offset, length: $0.length) }
.filter { $0.kind == .buildconfigKeyword }
.map { $0.range }
.filter { range in
let keyword = contents.substringWithByteRange(start: range.location, length: range.length)
return ["#if", "#endif"].contains(keyword)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
}
}

private func violationRange(match: NSTextCheckingResult, nsstring: NSString, syntaxMap: SyntaxMap,
private func violationRange(match: NSTextCheckingResult, nsstring: NSString, syntaxMap: SwiftLintSyntaxMap,
file: SwiftLintFile) -> NSRange? {
if match.numberOfRanges < 3 { return nil }

Expand Down Expand Up @@ -155,18 +155,16 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
}

// check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`.
private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SyntaxMap, file: SwiftLintFile) -> Bool {
private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SwiftLintSyntaxMap,
file: SwiftLintFile) -> Bool {
let tokens = syntaxMap.tokens(inByteRange: byteRange)
let nsString = file.contents.bridge()
return tokens.contains { token in
guard let kind = SyntaxKind(rawValue: token.type),
guard let kind = token.kind,
ForceUnwrappingRule.excludingSyntaxKindsForFirstCapture.contains(kind)
else { return false }
// check for `self
guard kind == .keyword,
let nsRange = nsString.byteRangeToNSRange(start: token.offset, length: token.length)
else { return true }
return nsString.substring(with: nsRange) != "self"
guard kind == .keyword else { return true }
return file.contents(for: token) != "self"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
private func isNextTokenUnknownAttribute(afterOffset offset: Int, file: SwiftLintFile) -> Bool {
let nextNonCommentToken = file.syntaxMap.tokens
.first { token in
guard let kind = SyntaxKind(rawValue: token.type), !kind.isCommentLike else {
guard let kind = token.kind, !kind.isCommentLike else {
return false
}

return token.offset > offset
}

return (nextNonCommentToken?.type).flatMap(SyntaxKind.init(rawValue:)) == .attributeID &&
return nextNonCommentToken?.kind == .attributeID &&
nextNonCommentToken.flatMap(file.contents(for:)) == "@unknown"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public struct PrivateOverFilePrivateRule: ConfigurationProviderRule, Substitutio

let parts = syntaxTokens.prefix { offset > $0.offset }
guard let lastKind = parts.last,
SyntaxKind(rawValue: lastKind.type) == .attributeBuiltin,
lastKind.kind == .attributeBuiltin,
let aclName = contents.substringWithByteRange(start: lastKind.offset, length: lastKind.length),
AccessControlLevel(description: aclName) == .fileprivate,
let range = contents.byteRangeToNSRange(start: lastKind.offset, length: lastKind.length) else {
Expand Down
8 changes: 3 additions & 5 deletions Source/SwiftLintFramework/Rules/Idiomatic/TypeNameRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule {
guard tokens.count == 2,
let keywordToken = tokens.first,
let nameToken = tokens.last,
SyntaxKind(rawValue: keywordToken.type) == .keyword,
SyntaxKind(rawValue: nameToken.type) == .identifier else {
keywordToken.kind == .keyword,
nameToken.kind == .identifier else {
return []
}

let contents = file.contents.bridge()
guard let name = contents.substringWithByteRange(start: nameToken.offset,
length: nameToken.length) else {
guard let name = file.contents(for: nameToken) else {
return []
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes

let caseRange = NSRange(location: caseOffset, length: caseLength)
let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in
guard let kind = SyntaxKind(rawValue: token.type),
guard let kind = token.kind,
token.offset > lastPatternEnd else {
return false
}
Expand All @@ -61,7 +61,7 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes

// is the `break` found the last (non-comment) token inside `case`?
guard let lastValidToken = tokens.last,
SyntaxKind(rawValue: lastValidToken.type) == .keyword,
lastValidToken.kind == .keyword,
lastValidToken.offset == byteRange.location,
lastValidToken.length == byteRange.length else {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
]
}

private func isTokenUnderscore(_ token: SyntaxToken, file: SwiftLintFile) -> Bool {
private func isTokenUnderscore(_ token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool {
return token.length == 1 &&
SyntaxKind(rawValue: token.type) == .keyword &&
token.kind == .keyword &&
isUnderscore(file: file, token: token)
}

Expand Down Expand Up @@ -97,8 +97,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
return nil
}

private func isUnderscore(file: SwiftLintFile, token: SyntaxToken) -> Bool {
let contents = file.contents.bridge()
return contents.substringWithByteRange(start: token.offset, length: token.length) == "_"
private func isUnderscore(file: SwiftLintFile, token: SwiftLintSyntaxToken) -> Bool {
return file.contents(for: token) == "_"
}
}
Loading

0 comments on commit dd32040

Please sign in to comment.