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

Migrate to use SourceKitten's new ByteCount/ByteRange types #3037

Merged
merged 15 commits into from
Jan 16, 2020
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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

#### Breaking

* None.
* Replace all uses of `Int`/`Int64`/`NSRange` representing byte offsets
to use newly introduced `ByteCount` and `ByteRange` values instead.
This will minimize the risk of accidentally using a byte-based offset
in character-based contexts.
[Paul Taykalo](https://github.com/PaulTaykalo)
[JP Simard](https://github.com/jpsim)

#### Experimental

Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github "jpsim/SourceKitten" ~> 0.28.0
github "jpsim/SourceKitten" ~> 0.29.0
github "scottrhoyt/SwiftyTextTable" ~> 0.9.0
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
github "Carthage/Commandant" "0.17.0"
github "drmohundro/SWXMLHash" "5.0.1"
github "jpsim/SourceKitten" "0.28.0"
github "jpsim/SourceKitten" "0.29.0"
github "jpsim/Yams" "2.0.0"
github "jspahrsummers/xcconfigs" "0.12"
github "scottrhoyt/SwiftyTextTable" "0.9.0"
2 changes: 1 addition & 1 deletion Carthage/Checkouts/SourceKitten
Submodule SourceKitten updated 44 files
+22 −0 CHANGELOG.md
+34 −33 Gemfile.lock
+65 −0 Source/SourceKittenFramework/ByteCount.swift
+59 −0 Source/SourceKittenFramework/ByteRange.swift
+1 −1 Source/SourceKittenFramework/Clang+SourceKitten.swift
+27 −23 Source/SourceKittenFramework/File.swift
+1 −1 Source/SourceKittenFramework/Info.plist
+1 −1 Source/SourceKittenFramework/Line.swift
+3 −4 Source/SourceKittenFramework/OffsetMap.swift
+18 −15 Source/SourceKittenFramework/Request.swift
+3 −3 Source/SourceKittenFramework/SourceDeclaration.swift
+20 −31 Source/SourceKittenFramework/SourceLocation.swift
+4 −8 Source/SourceKittenFramework/String+SourceKitten.swift
+8 −5 Source/SourceKittenFramework/StringView+SourceKitten.swift
+29 −27 Source/SourceKittenFramework/StringView.swift
+17 −13 Source/SourceKittenFramework/SwiftDocKey.swift
+39 −0 Source/SourceKittenFramework/SwiftVersion.swift
+12 −9 Source/SourceKittenFramework/SyntaxMap.swift
+9 −4 Source/SourceKittenFramework/SyntaxToken.swift
+1 −1 Source/SourceKittenFramework/Version.swift
+2 −2 Source/sourcekitten/CompleteCommand.swift
+1 −1 Source/sourcekitten/Info.plist
+1 −0 Tests/LinuxMain.swift
+73 −0 Tests/SourceKittenFrameworkTests/ByteRangeTests.swift
+1 −1 Tests/SourceKittenFrameworkTests/CursorInfoUSRTests.swift
+1 −1 Tests/SourceKittenFrameworkTests/FileTests.swift
+12 −1 Tests/SourceKittenFrameworkTests/Fixtures/Bicycle@swift-5.0.json
+374 −23 Tests/SourceKittenFrameworkTests/Fixtures/Bicycle@swift-5.1.json
+55 −55 Tests/SourceKittenFrameworkTests/Fixtures/Commandant@swift-5.1.json
+55 −55 Tests/SourceKittenFrameworkTests/Fixtures/CommandantSPM@swift-5.1.json
+10 −10 Tests/SourceKittenFrameworkTests/Fixtures/CursorInfoUSR@swift-5.0.json
+0 −3 Tests/SourceKittenFrameworkTests/Fixtures/CursorInfoUSR@swift-5.1.json
+0 −158 Tests/SourceKittenFrameworkTests/Fixtures/Extension@swift-5.1.json
+6 −6 Tests/SourceKittenFrameworkTests/Fixtures/LinuxBicycle@swift-5.1.json
+55 −55 Tests/SourceKittenFrameworkTests/Fixtures/LinuxCommandantSPM@swift-5.1.json
+0 −313 Tests/SourceKittenFrameworkTests/Fixtures/LinuxExtension@swift-5.1.json
+0 −65 Tests/SourceKittenFrameworkTests/Fixtures/LinuxSubscript@swift-5.1.json
+0 −41 Tests/SourceKittenFrameworkTests/Fixtures/Subscript@swift-5.1.json
+11 −1 Tests/SourceKittenFrameworkTests/SourceKitTests.swift
+42 −14 Tests/SourceKittenFrameworkTests/StringTests.swift
+2 −2 Tests/SourceKittenFrameworkTests/SwiftDocsTests.swift
+1 −1 Tests/SourceKittenFrameworkTests/SyntaxTests.swift
+7 −2 azure-pipelines.yml
+16 −0 sourcekitten.xcodeproj/project.pbxproj
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"state": {
"branch": null,
"revision": "97b5848e5692150d75b5cf0b81d7ebef5f4d5071",
"version": "0.28.0"
"revision": "77a4dbbb477a8110eb8765e3c44c70fb4929098f",
"version": "0.29.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")),
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.28.0"),
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.29.0")),
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"),
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),
] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []),
Expand Down
48 changes: 30 additions & 18 deletions Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,19 @@ public struct SourceKittenDictionary {
}

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

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

/// Body byte range.
var bodyByteRange: ByteRange? {
guard let offset = bodyOffset, let length = bodyLength else { return nil }
return ByteRange(location: offset, length: length)
}

/// Kind.
Expand All @@ -53,33 +59,39 @@ public struct SourceKittenDictionary {
}

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

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

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

/// Byte range of name.
var nameByteRange: ByteRange? {
guard let offset = nameOffset, let length = nameLength else { return nil }
return ByteRange(location: offset, length: length)
}

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

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

/// Setter accessibility.
Expand All @@ -93,13 +105,13 @@ public struct SourceKittenDictionary {
}

/// Documentation offset.
var docOffset: Int? {
return (value["key.docoffset"] as? Int64).flatMap({ Int($0) })
var docOffset: ByteCount? {
return (value["key.docoffset"] as? Int64).flatMap(ByteCount.init)
}

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

/// The attribute for this dictionary, as returned by SourceKit.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Foundation
import SourceKittenFramework

extension SourceKittenDictionary {
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure
Expand All @@ -7,35 +7,32 @@ extension SourceKittenDictionary {
/// - parameter byteOffset: Int?
///
/// - returns: The kinds and byte ranges.
internal func kinds(forByteOffset byteOffset: Int? = nil)
-> [(kind: String, byteRange: NSRange)] {
var results = [(kind: String, byteRange: NSRange)]()
internal func kinds(forByteOffset byteOffset: ByteCount? = nil)
-> [(kind: String, byteRange: ByteRange)] {
var results = [(kind: String, byteRange: ByteRange)]()

func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset,
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }) else {
return
guard let range = dictionary.byteRange else {
return
}
if let byteOffset = byteOffset, !NSLocationInRange(byteOffset, byteRange) {
if let byteOffset = byteOffset, !range.contains(byteOffset) {
return
}
if let kind = dictionary.kind {
results.append((kind: kind, byteRange: byteRange))
results.append((kind: kind, byteRange: range))
}
dictionary.substructure.forEach(parse)
}
parse(self)
return results
}

internal func structures(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
internal func structures(forByteOffset byteOffset: ByteCount) -> [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
guard let byteRange = dictionary.byteRange, byteRange.contains(byteOffset) else {
return
}

results.append(dictionary)
Expand Down
25 changes: 11 additions & 14 deletions Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,11 @@ extension SwiftLintFile {
internal func matchesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SwiftLintSyntaxToken])] {
let contents = stringView
let range = range ?? stringView.range
let range = range ?? contents.range
let syntax = syntaxMap
return regex(pattern).matches(in: contents, options: [], range: range).map { match in
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location,
length: match.range.length) ?? match.range
let tokensInRange = syntax.tokens(inByteRange: matchByteRange)
return (match, tokensInRange)
return regex(pattern).matches(in: contents, options: [], range: range).compactMap { match in
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location, length: match.range.length)
return matchByteRange.map { (match, syntax.tokens(inByteRange: $0)) }
}
}

Expand Down Expand Up @@ -143,12 +141,11 @@ extension SwiftLintFile {
var maybeLine = lineIterator.next()
var maybeStructure = structureIterator.next()
while let line = maybeLine, let structure = maybeStructure {
if NSLocationInRange(structure.byteRange.location, line.byteRange),
if line.byteRange.contains(structure.byteRange.location),
let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) {
results[line.index].append(swiftDeclarationKind)
}
let lineEnd = NSMaxRange(line.byteRange)
if structure.byteRange.location >= lineEnd {
if structure.byteRange.location >= line.byteRange.upperBound {
maybeLine = lineIterator.next()
} else {
maybeStructure = structureIterator.next()
Expand All @@ -168,12 +165,12 @@ extension SwiftLintFile {
var maybeToken = tokenGenerator.next()
while let line = maybeLine, let token = maybeToken {
let tokenRange = token.range
if NSLocationInRange(token.offset, line.byteRange) ||
NSLocationInRange(line.byteRange.location, tokenRange) {
if line.byteRange.contains(token.offset) ||
tokenRange.contains(line.byteRange.location) {
results[line.index].append(token)
}
let tokenEnd = NSMaxRange(tokenRange)
let lineEnd = NSMaxRange(line.byteRange)
let tokenEnd = tokenRange.upperBound
let lineEnd = line.byteRange.upperBound
if tokenEnd < lineEnd {
maybeToken = tokenGenerator.next()
} else if tokenEnd > lineEnd {
Expand Down Expand Up @@ -337,6 +334,6 @@ extension SwiftLintFile {
}

internal func contents(for token: SwiftLintSyntaxToken) -> String? {
return stringView.substringWithByteRange(start: token.offset, length: token.length)
return stringView.substringWithByteRange(token.range)
}
}
2 changes: 1 addition & 1 deletion Source/SwiftLintFramework/Helpers/NamespaceCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ struct NamespaceCollector {
struct Element {
let name: String
let kind: SwiftDeclarationKind
let offset: Int
let offset: ByteCount
let dictionary: SourceKittenDictionary

init?(dictionary: SourceKittenDictionary, namespace: [String]) {
Expand Down
4 changes: 2 additions & 2 deletions Source/SwiftLintFramework/Models/Location.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ public struct Location: CustomStringConvertible, Comparable, Codable {
}

/// Creates a `Location` based on a `SwiftLintFile` and a byte-offset into the file.
/// Fails if tthe specified offset was not a valid location in the file.
/// Fails if the specified offset was not a valid location in the file.
///
/// - parameter file: The file for this location.
/// - parameter offset: The offset in bytes into the file for this location.
public init(file: SwiftLintFile, byteOffset offset: Int) {
public init(file: SwiftLintFile, byteOffset offset: ByteCount) {
self.file = file.path
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
line = lineAndCharacter.line
Expand Down
7 changes: 3 additions & 4 deletions Source/SwiftLintFramework/Models/SwiftLintSyntaxMap.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Foundation
import SourceKittenFramework

/// Represents a Swift file's syntax information.
Expand All @@ -22,7 +21,7 @@ public struct SwiftLintSyntaxMap {
/// - parameter byteRange: Byte-based NSRange.
///
/// - returns: The array of syntax tokens intersecting with byte range.
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
internal func tokens(inByteRange byteRange: ByteRange) -> [SwiftLintSyntaxToken] {
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
return token.range.intersects(byteRange)
}
Expand All @@ -46,10 +45,10 @@ public struct SwiftLintSyntaxMap {

/// Returns the syntax kinds in the specified byte range.
///
/// - parameter byteRange: Byte-based NSRange.
/// - parameter byteRange: Byte range.
///
/// - returns: The syntax kinds in the specified byte range.
internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
internal func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] {
return tokens(inByteRange: byteRange).compactMap { $0.kind }
}
}
9 changes: 4 additions & 5 deletions Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Foundation
import SourceKittenFramework

/// A SwiftLint-aware Swift syntax token.
Expand All @@ -18,17 +17,17 @@ public struct SwiftLintSyntaxToken {
}

/// The byte range in a source file for this token.
public var range: NSRange {
return NSRange(location: value.offset, length: value.length)
public var range: ByteRange {
return value.range
}

/// The starting byte offset in a source file for this token.
public var offset: Int {
public var offset: ByteCount {
return value.offset
}

/// The length in bytes for this token.
public var length: Int {
public var length: ByteCount {
return value.length
}
}
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 @@ -111,7 +111,8 @@ public extension SwiftVersion {
let decl = file.structureDictionary.kinds()
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
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))
let offsetRange = ByteRange(location: token.offset + 1, length: token.length - 2)
return .init(rawValue: file.stringView.substringWithByteRange(offsetRange)!)
}

return .three
Expand Down
20 changes: 8 additions & 12 deletions Source/SwiftLintFramework/Protocols/CallPairRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension CallPairRule {
let stringView = file.stringView
let dictionary = file.structureDictionary

let violatingLocations: [Int] = firstRanges.compactMap { range in
let violatingLocations: [ByteCount] = firstRanges.compactMap { range in
guard let bodyByteRange = stringView.NSRangeToByteRange(start: range.location, length: range.length),
case let firstLocation = range.location + range.length - 1,
let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else {
Expand All @@ -63,18 +63,14 @@ extension CallPairRule {
}
}

private func methodCall(forByteOffset byteOffset: Int, excludingOffset: Int,
private func methodCall(forByteOffset byteOffset: ByteCount, excludingOffset: ByteCount,
dictionary: SourceKittenDictionary,
predicate: (SourceKittenDictionary) -> Bool) -> Int? {
if dictionary.expressionKind == .call,
let bodyOffset = dictionary.offset,
let bodyLength = dictionary.length,
let offset = dictionary.offset {
let byteRange = NSRange(location: bodyOffset, length: bodyLength)

if NSLocationInRange(byteOffset, byteRange) &&
!NSLocationInRange(excludingOffset, byteRange) && predicate(dictionary) {
return offset
predicate: (SourceKittenDictionary) -> Bool) -> ByteCount? {
if dictionary.expressionKind == .call, let byteRange = dictionary.byteRange {
if byteRange.contains(byteOffset) &&
!byteRange.contains(excludingOffset) &&
predicate(dictionary) {
return dictionary.offset
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ public struct ConvenienceTypeRule: ASTRule, OptInRule, ConfigurationProviderRule
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
return dictionary.swiftAttributes.contains { dict -> Bool in
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
let offset = dict.offset, let length = dict.length,
let contents = file.stringView.substringWithByteRange(start: offset, length: length) else {
return false
let contents = dict.byteRange.flatMap(file.stringView.substringWithByteRange)
else {
return false
}

return contents.contains("unavailable")
Expand Down
Loading