Compare commits

...

3 Commits

Author SHA1 Message Date
Paul Taykalo 6240b5a606 Added all Analyzer rules back 2020-01-16 07:57:23 +02:00
Paul Taykalo 4e01095004 Add ExplictiSelfRule 2020-01-16 07:41:52 +02:00
JP Simard 1a28275891
Migrate to use SourceKitten's new ByteCount/ByteRange types 2020-01-13 21:53:10 -08:00
123 changed files with 869 additions and 875 deletions

2
.gitmodules vendored
View File

@ -9,7 +9,7 @@
url = https://github.com/Carthage/Commandant.git url = https://github.com/Carthage/Commandant.git
[submodule "Carthage/Checkouts/SourceKitten"] [submodule "Carthage/Checkouts/SourceKitten"]
path = Carthage/Checkouts/SourceKitten path = Carthage/Checkouts/SourceKitten
url = https://github.com/jpsim/SourceKitten.git url = https://github.com/PaulTaykalo/SourceKitten.git
[submodule "Carthage/Checkouts/SwiftyTextTable"] [submodule "Carthage/Checkouts/SwiftyTextTable"]
path = Carthage/Checkouts/SwiftyTextTable path = Carthage/Checkouts/SwiftyTextTable
url = https://github.com/scottrhoyt/SwiftyTextTable.git url = https://github.com/scottrhoyt/SwiftyTextTable.git

View File

@ -1,2 +1,2 @@
github "jpsim/SourceKitten" ~> 0.28.0 github "PaulTaykalo/SourceKitten" "update/framework-api-visibility"
github "scottrhoyt/SwiftyTextTable" ~> 0.9.0 github "scottrhoyt/SwiftyTextTable" ~> 0.9.0

View File

@ -1,6 +1,6 @@
github "Carthage/Commandant" "0.17.0" github "Carthage/Commandant" "0.17.0"
github "PaulTaykalo/SourceKitten" "76350746e111778cc9e5893d35ddbcabe5f366dd"
github "drmohundro/SWXMLHash" "5.0.1" github "drmohundro/SWXMLHash" "5.0.1"
github "jpsim/SourceKitten" "0.28.0"
github "jpsim/Yams" "2.0.0" github "jpsim/Yams" "2.0.0"
github "jspahrsummers/xcconfigs" "0.12" github "jspahrsummers/xcconfigs" "0.12"
github "scottrhoyt/SwiftyTextTable" "0.9.0" github "scottrhoyt/SwiftyTextTable" "0.9.0"

@ -1 +1 @@
Subproject commit 97b5848e5692150d75b5cf0b81d7ebef5f4d5071 Subproject commit 003611a7f24b192ba86eba27e88b39718cd16eba

View File

@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/Quick/Nimble.git", "repositoryURL": "https://github.com/Quick/Nimble.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0", "revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6",
"version": "8.0.5" "version": "8.0.4"
} }
}, },
{ {
@ -48,11 +48,11 @@
}, },
{ {
"package": "SourceKitten", "package": "SourceKitten",
"repositoryURL": "https://github.com/jpsim/SourceKitten.git", "repositoryURL": "https://github.com/PaulTaykalo/SourceKitten.git",
"state": { "state": {
"branch": null, "branch": "update/framework-api-visibility",
"revision": "97b5848e5692150d75b5cf0b81d7ebef5f4d5071", "revision": "76350746e111778cc9e5893d35ddbcabe5f366dd",
"version": "0.28.0" "version": null
} }
}, },
{ {

View File

@ -15,7 +15,7 @@ let package = Package(
], ],
dependencies: [ dependencies: [
.package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")), .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/PaulTaykalo/SourceKitten.git", .branch("update/framework-api-visibility")),
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.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"), .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"))] : []), ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []),

View File

@ -38,13 +38,19 @@ public struct SourceKittenDictionary {
} }
/// Body length /// Body length
var bodyLength: Int? { var bodyLength: ByteCount? {
return (value["key.bodylength"] as? Int64).flatMap({ Int($0) }) return (value["key.bodylength"] as? Int64).map(ByteCount.init)
} }
/// Body offset. /// Body offset.
var bodyOffset: Int? { var bodyOffset: ByteCount? {
return (value["key.bodyoffset"] as? Int64).flatMap({ Int($0) }) 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. /// Kind.
@ -53,8 +59,8 @@ public struct SourceKittenDictionary {
} }
/// Length. /// Length.
var length: Int? { var length: ByteCount? {
return (value["key.length"] as? Int64).flatMap({ Int($0) }) return (value["key.length"] as? Int64).map(ByteCount.init)
} }
/// Name. /// Name.
var name: String? { var name: String? {
@ -62,24 +68,30 @@ public struct SourceKittenDictionary {
} }
/// Name length. /// Name length.
var nameLength: Int? { var nameLength: ByteCount? {
return (value["key.namelength"] as? Int64).flatMap({ Int($0) }) return (value["key.namelength"] as? Int64).map(ByteCount.init)
} }
/// Name offset. /// Name offset.
var nameOffset: Int? { var nameOffset: ByteCount? {
return (value["key.nameoffset"] as? Int64).flatMap({ Int($0) }) 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. /// Offset.
var offset: Int? { var offset: ByteCount? {
return (value["key.offset"] as? Int64).flatMap({ Int($0) }) return (value["key.offset"] as? Int64).map(ByteCount.init)
} }
/// Returns byte range starting from `offset` with `length` bytes /// 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 } guard let offset = offset, let length = length else { return nil }
return NSRange(location: offset, length: length) return ByteRange(location: offset, length: length)
} }
/// Setter accessibility. /// Setter accessibility.
@ -93,13 +105,13 @@ public struct SourceKittenDictionary {
} }
/// Documentation offset. /// Documentation offset.
var docOffset: Int? { var docOffset: ByteCount? {
return (value["key.docoffset"] as? Int64).flatMap({ Int($0) }) return (value["key.docoffset"] as? Int64).flatMap(ByteCount.init)
} }
/// Documentation length. /// Documentation length.
var docLength: Int? { var docLength: ByteCount? {
return (value["key.doclength"] as? Int64).flatMap({ Int($0) }) return (value["key.doclength"] as? Int64).flatMap(ByteCount.init)
} }
/// The attribute for this dictionary, as returned by SourceKit. /// The attribute for this dictionary, as returned by SourceKit.

View File

@ -1,4 +1,5 @@
import Foundation import Foundation
import SourceKittenFramework
extension SourceKittenDictionary { extension SourceKittenDictionary {
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure /// Returns array of tuples containing "key.kind" and "byteRange" from Structure
@ -7,20 +8,19 @@ extension SourceKittenDictionary {
/// - parameter byteOffset: Int? /// - parameter byteOffset: Int?
/// ///
/// - returns: The kinds and byte ranges. /// - returns: The kinds and byte ranges.
internal func kinds(forByteOffset byteOffset: Int? = nil) internal func kinds(forByteOffset byteOffset: ByteCount? = nil)
-> [(kind: String, byteRange: NSRange)] { -> [(kind: String, byteRange: ByteRange)] {
var results = [(kind: String, byteRange: NSRange)]() var results = [(kind: String, byteRange: ByteRange)]()
func parse(_ dictionary: SourceKittenDictionary) { func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset, guard let range = dictionary.byteRange else {
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }) else { return
return
} }
if let byteOffset = byteOffset, !NSLocationInRange(byteOffset, byteRange) { if let byteOffset = byteOffset, !range.contains(byteOffset) {
return return
} }
if let kind = dictionary.kind { if let kind = dictionary.kind {
results.append((kind: kind, byteRange: byteRange)) results.append((kind: kind, byteRange: range))
} }
dictionary.substructure.forEach(parse) dictionary.substructure.forEach(parse)
} }
@ -28,14 +28,12 @@ extension SourceKittenDictionary {
return results return results
} }
internal func structures(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] { internal func structures(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]() var results = [SourceKittenDictionary]()
func parse(_ dictionary: SourceKittenDictionary) { func parse(_ dictionary: SourceKittenDictionary) {
guard let offset = dictionary.offset, guard let byteRange = dictionary.byteRange, byteRange.contains(byteOffset) else {
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }), return
NSLocationInRange(byteOffset, byteRange) else {
return
} }
results.append(dictionary) results.append(dictionary)

View File

@ -109,8 +109,10 @@ extension SwiftLintFile {
let syntax = syntaxMap let syntax = syntaxMap
return regex(pattern).matches(in: contents, options: [], range: range).map { match in return regex(pattern).matches(in: contents, options: [], range: range).map { match in
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location, let matchByteRange = contents.NSRangeToByteRange(start: match.range.location,
length: match.range.length) ?? match.range length: match.range.length)
let tokensInRange = syntax.tokens(inByteRange: matchByteRange) let fallbackRange = ByteRange(location: ByteCount(match.range.location),
length: ByteCount(match.range.length))
let tokensInRange = syntax.tokens(inByteRange: matchByteRange ?? fallbackRange)
return (match, tokensInRange) return (match, tokensInRange)
} }
} }
@ -143,12 +145,11 @@ extension SwiftLintFile {
var maybeLine = lineIterator.next() var maybeLine = lineIterator.next()
var maybeStructure = structureIterator.next() var maybeStructure = structureIterator.next()
while let line = maybeLine, let structure = maybeStructure { 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) { let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) {
results[line.index].append(swiftDeclarationKind) results[line.index].append(swiftDeclarationKind)
} }
let lineEnd = NSMaxRange(line.byteRange) if structure.byteRange.location >= line.byteRange.upperBound {
if structure.byteRange.location >= lineEnd {
maybeLine = lineIterator.next() maybeLine = lineIterator.next()
} else { } else {
maybeStructure = structureIterator.next() maybeStructure = structureIterator.next()
@ -168,12 +169,12 @@ extension SwiftLintFile {
var maybeToken = tokenGenerator.next() var maybeToken = tokenGenerator.next()
while let line = maybeLine, let token = maybeToken { while let line = maybeLine, let token = maybeToken {
let tokenRange = token.range let tokenRange = token.range
if NSLocationInRange(token.offset, line.byteRange) || if line.byteRange.contains(token.offset) ||
NSLocationInRange(line.byteRange.location, tokenRange) { tokenRange.contains(line.byteRange.location) {
results[line.index].append(token) results[line.index].append(token)
} }
let tokenEnd = NSMaxRange(tokenRange) let tokenEnd = tokenRange.upperBound
let lineEnd = NSMaxRange(line.byteRange) let lineEnd = line.byteRange.upperBound
if tokenEnd < lineEnd { if tokenEnd < lineEnd {
maybeToken = tokenGenerator.next() maybeToken = tokenGenerator.next()
} else if tokenEnd > lineEnd { } else if tokenEnd > lineEnd {
@ -337,6 +338,6 @@ extension SwiftLintFile {
} }
internal func contents(for token: SwiftLintSyntaxToken) -> String? { internal func contents(for token: SwiftLintSyntaxToken) -> String? {
return stringView.substringWithByteRange(start: token.offset, length: token.length) return stringView.substringWithByteRange(token.range)
} }
} }

View File

@ -4,7 +4,7 @@ struct NamespaceCollector {
struct Element { struct Element {
let name: String let name: String
let kind: SwiftDeclarationKind let kind: SwiftDeclarationKind
let offset: Int let offset: ByteCount
let dictionary: SourceKittenDictionary let dictionary: SourceKittenDictionary
init?(dictionary: SourceKittenDictionary, namespace: [String]) { init?(dictionary: SourceKittenDictionary, namespace: [String]) {

View File

@ -37,11 +37,11 @@ public struct Location: CustomStringConvertible, Comparable, Codable {
} }
/// Creates a `Location` based on a `SwiftLintFile` and a byte-offset into the file. /// 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 file: The file for this location.
/// - parameter offset: The offset in bytes into 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 self.file = file.path
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) { if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
line = lineAndCharacter.line line = lineAndCharacter.line

View File

@ -22,7 +22,7 @@ public struct SwiftLintSyntaxMap {
/// - parameter byteRange: Byte-based NSRange. /// - parameter byteRange: Byte-based NSRange.
/// ///
/// - returns: The array of syntax tokens intersecting with byte range. /// - 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 { func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
return token.range.intersects(byteRange) return token.range.intersects(byteRange)
} }
@ -46,10 +46,39 @@ public struct SwiftLintSyntaxMap {
/// Returns the syntax kinds in the specified byte range. /// 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. /// - 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 } return tokens(inByteRange: byteRange).compactMap { $0.kind }
} }
} }
// TODO: Move to SourceKitten
extension ByteRange {
func contains(_ value: ByteCount) -> Bool {
return location <= value && upperBound >= value
}
func intersects(_ otherRange: ByteRange) -> Bool {
return contains(otherRange.location) ||
contains(otherRange.location + otherRange.length) ||
otherRange.contains(location) ||
otherRange.contains(location + length)
}
func intersects(_ ranges: [ByteRange]) -> Bool {
return ranges.contains { intersects($0) }
}
func union(with otherRange: ByteRange) -> ByteRange {
let maxUpperBound = max(upperBound, otherRange.upperBound)
let minLocation = min(location, otherRange.location)
return ByteRange(location: minLocation, length: maxUpperBound - minLocation)
}
var upperBound: ByteCount {
return location + length
}
}

View File

@ -18,17 +18,17 @@ public struct SwiftLintSyntaxToken {
} }
/// The byte range in a source file for this token. /// The byte range in a source file for this token.
public var range: NSRange { public var range: ByteRange {
return NSRange(location: value.offset, length: value.length) return value.range
} }
/// The starting byte offset in a source file for this token. /// The starting byte offset in a source file for this token.
public var offset: Int { public var offset: ByteCount {
return value.offset return value.offset
} }
/// The length in bytes for this token. /// The length in bytes for this token.
public var length: Int { public var length: ByteCount {
return value.length return value.length
} }
} }

View File

@ -111,7 +111,8 @@ public extension SwiftVersion {
let decl = file.structureDictionary.kinds() let decl = file.structureDictionary.kinds()
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }), .first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) { 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 return .three

View File

@ -36,7 +36,7 @@ extension CallPairRule {
let stringView = file.stringView let stringView = file.stringView
let dictionary = file.structureDictionary 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), guard let bodyByteRange = stringView.NSRangeToByteRange(start: range.location, length: range.length),
case let firstLocation = range.location + range.length - 1, case let firstLocation = range.location + range.length - 1,
let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else { let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else {
@ -63,18 +63,14 @@ extension CallPairRule {
} }
} }
private func methodCall(forByteOffset byteOffset: Int, excludingOffset: Int, private func methodCall(forByteOffset byteOffset: ByteCount, excludingOffset: ByteCount,
dictionary: SourceKittenDictionary, dictionary: SourceKittenDictionary,
predicate: (SourceKittenDictionary) -> Bool) -> Int? { predicate: (SourceKittenDictionary) -> Bool) -> ByteCount? {
if dictionary.expressionKind == .call, if dictionary.expressionKind == .call, let byteRange = dictionary.byteRange {
let bodyOffset = dictionary.offset, if byteRange.contains(byteOffset) &&
let bodyLength = dictionary.length, !byteRange.contains(excludingOffset) &&
let offset = dictionary.offset { predicate(dictionary) {
let byteRange = NSRange(location: bodyOffset, length: bodyLength) return dictionary.offset
if NSLocationInRange(byteOffset, byteRange) &&
!NSLocationInRange(excludingOffset, byteRange) && predicate(dictionary) {
return offset
} }
} }

View File

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

View File

@ -31,7 +31,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
private func variableViolations(file: SwiftLintFile, private func variableViolations(file: SwiftLintFile,
kind: SwiftDeclarationKind, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [Int] { dictionary: SourceKittenDictionary) -> [ByteCount] {
guard guard
SwiftDeclarationKind.variableKinds.contains(kind), SwiftDeclarationKind.variableKinds.contains(kind),
let offset = dictionary.offset, let offset = dictionary.offset,
@ -42,7 +42,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
private func functionViolations(file: SwiftLintFile, private func functionViolations(file: SwiftLintFile,
kind: SwiftDeclarationKind, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [Int] { dictionary: SourceKittenDictionary) -> [ByteCount] {
guard guard
SwiftDeclarationKind.functionKinds.contains(kind), SwiftDeclarationKind.functionKinds.contains(kind),
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
@ -51,8 +51,9 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
let offset = dictionary.offset, let offset = dictionary.offset,
case let start = nameOffset + nameLength, case let start = nameOffset + nameLength,
case let end = dictionary.bodyOffset ?? offset + length, case let end = dictionary.bodyOffset ?? offset + length,
case let byteRange = ByteRange(location: start, length: end - start),
case let contents = file.stringView, case let contents = file.stringView,
let range = file.stringView.byteRangeToNSRange(start: start, length: end - start), let range = file.stringView.byteRangeToNSRange(byteRange),
let match = file.match(pattern: "->\\s*(.*?)\\{", excludingSyntaxKinds: excludingKinds, range: range).first let match = file.match(pattern: "->\\s*(.*?)\\{", excludingSyntaxKinds: excludingKinds, range: range).first
else { return [] } else { return [] }

View File

@ -22,19 +22,18 @@ public struct DuplicateImportsRule: ConfigurationProviderRule, AutomaticTestable
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples
) )
private func rangesInConditionalCompilation(file: SwiftLintFile) -> [NSRange] { private func rangesInConditionalCompilation(file: SwiftLintFile) -> [ByteRange] {
let contents = file.stringView let contents = file.stringView
let ranges = file.syntaxMap.tokens let ranges = file.syntaxMap.tokens
.filter { $0.kind == .buildconfigKeyword } .filter { $0.kind == .buildconfigKeyword }
.map { $0.range } .map { $0.range }
.filter { range in .filter { range in
let keyword = contents.substringWithByteRange(start: range.location, length: range.length) return ["#if", "#endif"].contains(contents.substringWithByteRange(range))
return ["#if", "#endif"].contains(keyword)
} }
return stride(from: 0, to: ranges.count, by: 2).reduce(into: []) { result, rangeIndex in return stride(from: 0, to: ranges.count, by: 2).reduce(into: []) { result, rangeIndex in
result.append(NSUnionRange(ranges[rangeIndex], ranges[rangeIndex + 1])) result.append(ranges[rangeIndex].union(with: ranges[rangeIndex + 1]))
} }
} }

View File

@ -53,7 +53,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
] ]
) )
private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [NSRange] { private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [ByteRange] {
let contents = file.stringView let contents = file.stringView
return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap { return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap {
contents.NSRangeToByteRange(start: $0.location, length: $0.length) contents.NSRangeToByteRange(start: $0.location, length: $0.length)
@ -61,7 +61,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
} }
private func offsetOfElements(from elements: [SourceKittenElement], in file: SwiftLintFile, private func offsetOfElements(from elements: [SourceKittenElement], in file: SwiftLintFile,
thatAreNotInRanges ranges: [NSRange]) -> [Int] { thatAreNotInRanges ranges: [ByteRange]) -> [ByteCount] {
let extensionKinds: Set<SwiftDeclarationKind> = [.extension, .extensionClass, .extensionEnum, let extensionKinds: Set<SwiftDeclarationKind> = [.extension, .extensionClass, .extensionEnum,
.extensionProtocol, .extensionStruct] .extensionProtocol, .extensionStruct]
@ -83,7 +83,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
// the "internal" token correspond to the type if there're only // the "internal" token correspond to the type if there're only
// attributeBuiltin (`final` for example) tokens between them // attributeBuiltin (`final` for example) tokens between them
let length = typeOffset - previousInternalByteRange.location let length = typeOffset - previousInternalByteRange.location
let range = NSRange(location: previousInternalByteRange.location, length: length) let range = ByteRange(location: previousInternalByteRange.location, length: length)
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin] let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
return internalDoesntBelongToType ? typeOffset : nil return internalDoesntBelongToType ? typeOffset : nil
@ -109,7 +109,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
} }
} }
private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
let firstPartition = ranges.prefix(while: { typeOffset > $0.location }) let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
return firstPartition.last return firstPartition.last
} }

View File

@ -100,7 +100,7 @@ public struct ExplicitEnumRawValueRule: ASTRule, OptInRule, ConfigurationProvide
} }
} }
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] { private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] {
let locs = substructureElements(of: dictionary, matching: .enumcase) let locs = substructureElements(of: dictionary, matching: .enumcase)
.compactMap { substructureElements(of: $0, matching: .enumelement) } .compactMap { substructureElements(of: $0, matching: .enumelement) }
.flatMap(enumElementsMissingInitExpr) .flatMap(enumElementsMissingInitExpr)

View File

@ -71,14 +71,14 @@ public struct ExplicitInitRule: SubstitutionCorrectableASTRule, ConfigurationPro
&& initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0 && initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0
} }
let length = ".init".utf8.count let length = ByteCount(".init".utf8.count)
guard kind == .call, guard kind == .call,
let name = dictionary.name, isExpected(name), let name = dictionary.name, isExpected(name),
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let range = file.stringView let range = file.stringView
.byteRangeToNSRange(start: nameOffset + nameLength - length, length: length) .byteRangeToNSRange(ByteRange(location: nameOffset + nameLength - length, length: length))
else { return [] } else { return [] }
return [range] return [range]
} }

View File

@ -37,7 +37,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
// find all top-level types marked as internal (either explictly or implictly) // find all top-level types marked as internal (either explictly or implictly)
let dictionary = file.structureDictionary let dictionary = file.structureDictionary
let internalTypesOffsets = dictionary.substructure.compactMap { element -> Int? in let internalTypesOffsets = dictionary.substructure.compactMap { element -> ByteCount? in
// ignore extensions // ignore extensions
guard let kind = element.declarationKind, guard let kind = element.declarationKind,
!extensionKinds.contains(kind) else { !extensionKinds.contains(kind) else {
@ -72,7 +72,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
// the "internal" token correspond to the type if there're only // the "internal" token correspond to the type if there're only
// attributeBuiltin (`final` for example) tokens between them // attributeBuiltin (`final` for example) tokens between them
let length = typeOffset - previousInternalByteRange.location let length = typeOffset - previousInternalByteRange.location
let range = NSRange(location: previousInternalByteRange.location, length: length) let range = ByteRange(location: previousInternalByteRange.location, length: length)
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin] let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
return internalDoesntBelongToType return internalDoesntBelongToType
@ -85,7 +85,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
} }
} }
private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
let firstPartition = ranges.prefix(while: { typeOffset > $0.location }) let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
return firstPartition.last return firstPartition.last
} }

View File

@ -108,7 +108,8 @@ private extension SourceKittenDictionary {
guard guard
let nameOffset = nameOffset, let nameOffset = nameOffset,
let nameLength = nameLength, let nameLength = nameLength,
let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0) case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0),
let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange)
else { else {
return false return false
} }
@ -125,7 +126,8 @@ private extension SourceKittenDictionary {
guard guard
let nameOffset = nameOffset, let nameOffset = nameOffset,
let nameLength = nameLength, let nameLength = nameLength,
let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0) case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0),
let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange)
else { else {
return false return false
} }
@ -137,11 +139,11 @@ private extension SourceKittenDictionary {
return typeAssignment.firstMatch(in: contentAfterName, options: [], range: contentAfterName.fullNSRange) != nil return typeAssignment.firstMatch(in: contentAfterName, options: [], range: contentAfterName.fullNSRange) != nil
} }
var caseStatementPatternRanges: [NSRange] { var caseStatementPatternRanges: [ByteRange] {
return ranges(with: StatementKind.case.rawValue, for: "source.lang.swift.structure.elem.pattern") return ranges(with: StatementKind.case.rawValue, for: "source.lang.swift.structure.elem.pattern")
} }
var caseExpressionRanges: [NSRange] { var caseExpressionRanges: [ByteRange] {
return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr") return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr")
} }
@ -152,30 +154,27 @@ private extension SourceKittenDictionary {
return statements.contains(statement) return statements.contains(statement)
} }
func ranges(with parentKind: String, for elementKind: String) -> [NSRange] { func ranges(with parentKind: String, for elementKind: String) -> [ByteRange] {
guard parentKind == kind else { guard parentKind == kind else {
return [] return []
} }
return elements return elements
.filter { elementKind == $0.kind } .filter { elementKind == $0.kind }
.compactMap { .compactMap { $0.byteRange }
guard let location = $0.offset, let length = $0.length else { return nil }
return NSRange(location: location, length: length)
}
} }
} }
private extension SwiftLintFile { private extension SwiftLintFile {
var captureGroupByteRanges: [NSRange] { var captureGroupByteRanges: [ByteRange] {
return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]", return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]",
excludingSyntaxKinds: SyntaxKind.commentKinds) excludingSyntaxKinds: SyntaxKind.commentKinds)
.compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) } .compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) }
} }
} }
private extension Collection where Element == NSRange { private extension Collection where Element == ByteRange {
func contains(_ index: Int) -> Bool { func contains(_ index: ByteCount) -> Bool {
return contains { $0.contains(index) } return contains { $0.contains(index) }
} }
} }

View File

@ -79,20 +79,23 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind, public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] { dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .extension, let offset = dictionary.offset, guard kind == .extension, let offset = dictionary.offset,
dictionary.inheritedTypes.isEmpty else { dictionary.inheritedTypes.isEmpty
return [] else {
return []
} }
let declarations = dictionary.substructure.compactMap { entry -> (acl: AccessControlLevel, offset: Int)? in let declarations = dictionary.substructure
guard entry.declarationKind != nil, .compactMap { entry -> (acl: AccessControlLevel, offset: ByteCount)? in
let acl = entry.accessibility, guard entry.declarationKind != nil,
let offset = entry.offset else { let acl = entry.accessibility,
return nil let offset = entry.offset
else {
return nil
}
return (acl: acl, offset: offset)
} }
return (acl: acl, offset: offset)
}
let declarationsACLs = declarations.map { $0.acl }.unique let declarationsACLs = declarations.map { $0.acl }.unique
let allowedACLs: Set<AccessControlLevel> = [.internal, .private, .open] let allowedACLs: Set<AccessControlLevel> = [.internal, .private, .open]
guard declarationsACLs.count == 1, !allowedACLs.contains(declarationsACLs[0]) else { guard declarationsACLs.count == 1, !allowedACLs.contains(declarationsACLs[0]) else {
@ -115,11 +118,11 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
} }
private func declarationsViolations(file: SwiftLintFile, acl: AccessControlLevel, private func declarationsViolations(file: SwiftLintFile, acl: AccessControlLevel,
declarationOffsets: [Int], declarationOffsets: [ByteCount],
dictionary: SourceKittenDictionary) -> [StyleViolation] { dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard let offset = dictionary.offset, let length = dictionary.length, guard let byteRange = dictionary.byteRange,
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: offset, length: length) else { let range = contents.byteRangeToNSRange(byteRange) else {
return [] return []
} }
@ -138,7 +141,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
// the ACL token correspond to the type if there're only // the ACL token correspond to the type if there're only
// attributeBuiltin (`final` for example) tokens between them // attributeBuiltin (`final` for example) tokens between them
let length = typeOffset - previousInternalByteRange.location let length = typeOffset - previousInternalByteRange.location
let range = NSRange(location: previousInternalByteRange.location, length: length) let range = ByteRange(location: previousInternalByteRange.location, length: length)
let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin] let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin]
return internalBelongsToType return internalBelongsToType
@ -151,7 +154,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
} }
} }
private func lastACLByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { private func lastACLByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
let firstPartition = ranges.partitioned(by: { $0.location > typeOffset }).first let firstPartition = ranges.partitioned(by: { $0.location > typeOffset }).first
return firstPartition.last return firstPartition.last
} }

View File

@ -53,16 +53,15 @@ public struct FatalErrorMessageRule: ASTRule, ConfigurationProviderRule, OptInRu
} }
private func hasEmptyBody(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { private func hasEmptyBody(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
guard let bodyOffset = dictionary.bodyOffset, guard let bodyRange = dictionary.bodyByteRange else {
let bodyLength = dictionary.bodyLength else { return false
return false
} }
if bodyLength == 0 { if bodyRange.length == 0 {
return true return true
} }
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) let body = file.stringView.substringWithByteRange(bodyRange)
return body == "\"\"" return body == "\"\""
} }
} }

View File

@ -133,9 +133,9 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
return false return false
} }
let beforeIfRange = NSRange(location: offset, length: ifOffset - offset) let beforeIfRange = ByteRange(location: offset, length: ifOffset - offset)
let ifFinalPosition = ifOffset + ifLength let ifFinalPosition = ifOffset + ifLength
let afterIfRange = NSRange(location: ifFinalPosition, length: offset + length - ifFinalPosition) let afterIfRange = ByteRange(location: ifFinalPosition, length: offset + length - ifFinalPosition)
let allKinds = file.syntaxMap.kinds(inByteRange: beforeIfRange) + let allKinds = file.syntaxMap.kinds(inByteRange: beforeIfRange) +
file.syntaxMap.kinds(inByteRange: afterIfRange) file.syntaxMap.kinds(inByteRange: afterIfRange)
@ -148,13 +148,11 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
let kind = "source.lang.swift.structure.elem.condition_expr" let kind = "source.lang.swift.structure.elem.condition_expr"
let contents = file.stringView
return dictionary.elements.contains { element in return dictionary.elements.contains { element in
guard element.kind == kind, guard element.kind == kind,
let offset = element.offset, let range = element.byteRange.flatMap(file.stringView.byteRangeToNSRange)
let length = element.length, else {
let range = contents.byteRangeToNSRange(start: offset, length: length) else { return false
return false
} }
let containsKeyword = !file.match(pattern: "\\blet|var|case\\b", with: [.keyword], range: range).isEmpty let containsKeyword = !file.match(pattern: "\\blet|var|case\\b", with: [.keyword], range: range).isEmpty

View File

@ -152,7 +152,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
} }
// check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`. // check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`.
private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SwiftLintSyntaxMap, private func isFirstRangeExcludedToken(byteRange: ByteRange, syntaxMap: SwiftLintSyntaxMap,
file: SwiftLintFile) -> Bool { file: SwiftLintFile) -> Bool {
let tokens = syntaxMap.tokens(inByteRange: byteRange) let tokens = syntaxMap.tokens(inByteRange: byteRange)
return tokens.contains { token in return tokens.contains { token in
@ -166,7 +166,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
} }
// check deepest kind matching range in structure is a typeAnnotation // check deepest kind matching range in structure is a typeAnnotation
private func isTypeAnnotation(in file: SwiftLintFile, byteRange: NSRange) -> Bool { private func isTypeAnnotation(in file: SwiftLintFile, byteRange: ByteRange) -> Bool {
let kinds = file.structureDictionary.kinds(forByteOffset: byteRange.location) let kinds = file.structureDictionary.kinds(forByteOffset: byteRange.location)
guard let lastItem = kinds.last, guard let lastItem = kinds.last,
let lastKind = SwiftDeclarationKind(rawValue: lastItem.kind), let lastKind = SwiftDeclarationKind(rawValue: lastItem.kind),
@ -175,9 +175,9 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
} }
// range is in some "source.lang.swift.decl.var.*" // range is in some "source.lang.swift.decl.var.*"
let byteOffset = lastItem.byteRange.location let varRange = ByteRange(location: lastItem.byteRange.location,
let byteLength = byteRange.location - byteOffset length: byteRange.location - lastItem.byteRange.location)
if let varDeclarationString = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength), if let varDeclarationString = file.stringView.substringWithByteRange(varRange),
varDeclarationString.contains("=") { varDeclarationString.contains("=") {
// if declarations contains "=", range is not type annotation // if declarations contains "=", range is not type annotation
return false return false

View File

@ -97,10 +97,8 @@ public struct FunctionDefaultParameterAtEndRule: ASTRule, ConfigurationProviderR
} }
private func isDefaultParameter(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { private func isDefaultParameter(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
let contents = file.stringView guard let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange) else {
guard let offset = dictionary.offset, let length = dictionary.length, return false
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
return false
} }
return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil

View File

@ -71,15 +71,16 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule {
} else { } else {
guard kind == .genericTypeParam, guard kind == .genericTypeParam,
let name = dictionary.name, let name = dictionary.name,
let offset = dictionary.offset else { let offset = dictionary.offset
return [] else {
return []
} }
return validate(name: name, file: file, offset: offset) return validate(name: name, file: file, offset: offset)
} }
} }
private func validate(name: String, file: SwiftLintFile, offset: Int) -> [StyleViolation] { private func validate(name: String, file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] {
guard !configuration.excluded.contains(name) else { guard !configuration.excluded.contains(name) else {
return [] return []
} }
@ -122,7 +123,7 @@ extension GenericTypeNameRule {
private func validateGenericTypeAliases(in file: SwiftLintFile) -> [StyleViolation] { private func validateGenericTypeAliases(in file: SwiftLintFile) -> [StyleViolation] {
let pattern = "typealias\\s+\\w+?\\s*" + type(of: self).genericTypePattern + "\\s*=" let pattern = "typealias\\s+\\w+?\\s*" + type(of: self).genericTypePattern + "\\s*="
return file.match(pattern: pattern).flatMap { range, tokens -> [(String, Int)] in return file.match(pattern: pattern).flatMap { range, tokens -> [(String, ByteCount)] in
guard tokens.first == .keyword, guard tokens.first == .keyword,
Set(tokens.dropFirst()) == [.identifier], Set(tokens.dropFirst()) == [.identifier],
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [], let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
@ -136,7 +137,7 @@ extension GenericTypeNameRule {
} }
private func genericTypesForType(in file: SwiftLintFile, kind: SwiftDeclarationKind, private func genericTypesForType(in file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [(String, Int)] { dictionary: SourceKittenDictionary) -> [(String, ByteCount)] {
guard SwiftDeclarationKind.typeKinds.contains(kind), guard SwiftDeclarationKind.typeKinds.contains(kind),
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
@ -144,7 +145,8 @@ extension GenericTypeNameRule {
case let contents = file.stringView, case let contents = file.stringView,
case let start = nameOffset + nameLength, case let start = nameOffset + nameLength,
case let length = bodyOffset - start, case let length = bodyOffset - start,
let range = file.stringView.byteRangeToNSRange(start: start, length: length), case let byteRange = ByteRange(location: start, length: length),
let range = file.stringView.byteRangeToNSRange(byteRange),
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [], let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
range: range)?.range(at: 1) else { range: range)?.range(at: 1) else {
return [] return []
@ -155,16 +157,18 @@ extension GenericTypeNameRule {
} }
private func genericTypesForFunction(in file: SwiftLintFile, kind: SwiftDeclarationKind, private func genericTypesForFunction(in file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [(String, Int)] { dictionary: SourceKittenDictionary) -> [(String, ByteCount)] {
guard SwiftDeclarationKind.functionKinds.contains(kind), guard SwiftDeclarationKind.functionKinds.contains(kind),
let offset = dictionary.nameOffset, let offset = dictionary.nameOffset,
let length = dictionary.nameLength, let length = dictionary.nameLength,
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: offset, length: length), case let byteRange = ByteRange(location: offset, length: length),
let range = contents.byteRangeToNSRange(byteRange),
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents,
options: [], range: range)?.range(at: 1), options: [], range: range)?.range(at: 1),
match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file) else { match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file)
return [] else {
return []
} }
let genericConstraint = contents.substring(with: match) let genericConstraint = contents.substring(with: match)
@ -174,7 +178,7 @@ extension GenericTypeNameRule {
private func minParameterOffset(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> Int { private func minParameterOffset(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> Int {
let offsets = parameters.compactMap { param -> Int? in let offsets = parameters.compactMap { param -> Int? in
return param.offset.flatMap { return param.offset.flatMap {
file.stringView.byteRangeToNSRange(start: $0, length: 0)?.location file.stringView.byteRangeToNSRange(ByteRange(location: $0, length: 0))?.location
} }
} }
@ -182,7 +186,7 @@ extension GenericTypeNameRule {
} }
private func extractTypes(fromGenericConstraint constraint: String, offset: Int, private func extractTypes(fromGenericConstraint constraint: String, offset: Int,
file: SwiftLintFile) -> [(String, Int)] { file: SwiftLintFile) -> [(String, ByteCount)] {
guard let beforeWhere = constraint.components(separatedBy: "where").first else { guard let beforeWhere = constraint.components(separatedBy: "where").first else {
return [] return []
} }
@ -196,7 +200,7 @@ extension GenericTypeNameRule {
} }
let contents = file.stringView let contents = file.stringView
return namesAndRanges.compactMap { name, range -> (String, Int)? in return namesAndRanges.compactMap { name, range -> (String, ByteCount)? in
guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset, guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset,
length: range.length), length: range.length),
file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else { file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else {
@ -228,8 +232,9 @@ private extension String {
let bridged = bridge() let bridged = bridge()
let range = NSRange(location: 0, length: bridged.length) let range = NSRange(location: 0, length: bridged.length)
guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range), guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range),
NSEqualRanges(range, match.range) else { NSEqualRanges(range, match.range)
return (self, range) else {
return (self, range)
} }
let trimmedRange = match.range(at: 1) let trimmedRange = match.range(at: 1)

View File

@ -70,23 +70,18 @@ public struct JoinedDefaultParameterRule: SubstitutionCorrectableASTRule, Config
guard guard
// is this single argument called 'separator'? // is this single argument called 'separator'?
let argument = dictionary.enclosedArguments.first, let argument = dictionary.enclosedArguments.first,
let offset = argument.offset, let argumentByteRange = argument.byteRange,
let length = argument.length, argument.name == "separator",
argument.name == "separator" let argumentNSRange = file.stringView.byteRangeToNSRange(argumentByteRange)
else { return [] } else { return [] }
guard guard
// is this single argument the default parameter? // is this single argument the default parameter?
let bodyOffset = argument.bodyOffset, let bodyRange = argument.bodyByteRange,
let bodyLength = argument.bodyLength, let body = file.stringView.substringWithByteRange(bodyRange),
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength),
body == "\"\"" body == "\"\""
else { return [] } else { return [] }
guard return [argumentNSRange]
let range = file.stringView.byteRangeToNSRange(start: offset, length: length)
else { return [] }
return [range]
} }
} }

View File

@ -181,8 +181,8 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv
var adjustedLocations = [Int]() var adjustedLocations = [Int]()
for dictionary in violatingDictionaries.reversed() { for dictionary in violatingDictionaries.reversed() {
guard let offset = dictionary.offset, let length = dictionary.length, guard let byteRange = dictionary.byteRange,
let range = file.stringView.byteRangeToNSRange(start: offset, length: length), let range = file.stringView.byteRangeToNSRange(byteRange),
let name = dictionary.name, let name = dictionary.name,
let correctedName = type(of: self).constructorsToCorrectedNames[name], let correctedName = type(of: self).constructorsToCorrectedNames[name],
file.ruleEnabled(violatingRanges: [range], for: self) == [range], file.ruleEnabled(violatingRanges: [range], for: self) == [range],
@ -213,13 +213,11 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv
private func argumentsContents(file: SwiftLintFile, arguments: [SourceKittenDictionary]) -> [String] { private func argumentsContents(file: SwiftLintFile, arguments: [SourceKittenDictionary]) -> [String] {
let contents = file.stringView let contents = file.stringView
return arguments.compactMap { argument -> String? in return arguments.compactMap { argument -> String? in
guard argument.name == nil, guard argument.name == nil, let byteRange = argument.byteRange else {
let offset = argument.offset, return nil
let length = argument.length else {
return nil
} }
return contents.substringWithByteRange(start: offset, length: length) return contents.substringWithByteRange(byteRange)
} }
} }
} }

View File

@ -19,10 +19,9 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
kind: StatementKind, kind: StatementKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] { dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .case, guard kind == .case,
let length = dictionary.length, let byteRange = dictionary.byteRange,
let offset = dictionary.offset,
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: offset, length: length), let range = contents.byteRangeToNSRange(byteRange),
let colonLocation = findCaseColon(text: file.stringView.nsString, range: range) let colonLocation = findCaseColon(text: file.stringView.nsString, range: range)
else { else {
return [] return []
@ -40,7 +39,7 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
let nsRange = nonCommentCaseBody[0].0 let nsRange = nonCommentCaseBody[0].0
if contents.substring(with: nsRange) == "fallthrough" && nonCommentCaseBody[0].1 == [.keyword] && if contents.substring(with: nsRange) == "fallthrough" && nonCommentCaseBody[0].1 == [.keyword] &&
!isNextTokenUnknownAttribute(afterOffset: offset + length, file: file) { !isNextTokenUnknownAttribute(afterOffset: byteRange.upperBound, file: file) {
return [StyleViolation(ruleDescription: type(of: self).description, return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity, severity: configuration.severity,
location: Location(file: file, characterOffset: nsRange.location))] location: Location(file: file, characterOffset: nsRange.location))]
@ -49,13 +48,12 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
return [] return []
} }
private func isNextTokenUnknownAttribute(afterOffset offset: Int, file: SwiftLintFile) -> Bool { private func isNextTokenUnknownAttribute(afterOffset offset: ByteCount, file: SwiftLintFile) -> Bool {
let nextNonCommentToken = file.syntaxMap.tokens let nextNonCommentToken = file.syntaxMap.tokens
.first { token in .first { token in
guard let kind = token.kind, !kind.isCommentLike else { guard let kind = token.kind, !kind.isCommentLike else {
return false return false
} }
return token.offset > offset return token.offset > offset
} }

View File

@ -45,18 +45,14 @@ public struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule, Aut
} }
private func hasWhereClause(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { private func hasWhereClause(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
let contents = file.stringView
guard let nameOffset = dictionary.nameOffset, guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let bodyOffset = dictionary.bodyOffset else { let bodyOffset = dictionary.bodyOffset,
return false case let contents = file.stringView,
} case let rangeStart = nameOffset + nameLength,
case let rangeLength = bodyOffset - rangeStart,
let rangeStart = nameOffset + nameLength let range = contents.byteRangeToNSRange(ByteRange(location: rangeStart, length: rangeLength))
let rangeLength = bodyOffset - rangeStart else {
guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) else {
return false return false
} }

View File

@ -94,11 +94,6 @@ public struct ObjectLiteralRule: ASTRule, ConfigurationProviderRule, OptInRule {
} }
private func kinds(forArgument argument: SourceKittenDictionary, file: SwiftLintFile) -> Set<SyntaxKind> { private func kinds(forArgument argument: SourceKittenDictionary, file: SwiftLintFile) -> Set<SyntaxKind> {
guard let offset = argument.bodyOffset, let length = argument.bodyLength else { return argument.bodyByteRange.map { Set(file.syntaxMap.kinds(inByteRange: $0)) } ?? []
return []
}
let range = NSRange(location: offset, length: length)
return Set(file.syntaxMap.kinds(inByteRange: range))
} }
} }

View File

@ -43,10 +43,10 @@ public struct PatternMatchingKeywordsRule: ASTRule, ConfigurationProviderRule, O
let contents = file.stringView let contents = file.stringView
return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
let offset = subDictionary.offset, let caseByteRange = subDictionary.byteRange,
let length = subDictionary.length, let caseRange = contents.byteRangeToNSRange(caseByteRange)
let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else { else {
return [] return []
} }
let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange) let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange)

View File

@ -81,10 +81,11 @@ public struct PrivateOverFilePrivateRule: ConfigurationProviderRule, Substitutio
let parts = syntaxTokens.prefix { offset > $0.offset } let parts = syntaxTokens.prefix { offset > $0.offset }
guard let lastKind = parts.last, guard let lastKind = parts.last,
lastKind.kind == .attributeBuiltin, lastKind.kind == .attributeBuiltin,
let aclName = contents.substringWithByteRange(start: lastKind.offset, length: lastKind.length), let aclName = contents.substringWithByteRange(lastKind.range),
AccessControlLevel(description: aclName) == .fileprivate, AccessControlLevel(description: aclName) == .fileprivate,
let range = contents.byteRangeToNSRange(start: lastKind.offset, length: lastKind.length) else { let range = contents.byteRangeToNSRange(lastKind.range)
return nil else {
return nil
} }
return range return range

View File

@ -41,10 +41,10 @@ public struct RedundantObjcAttributeRule: SubstitutionCorrectableRule, Configura
parentStructure: SourceKittenDictionary?) -> [NSRange] { parentStructure: SourceKittenDictionary?) -> [NSRange] {
let objcAttribute = dictionary.swiftAttributes let objcAttribute = dictionary.swiftAttributes
.first(where: { $0.attribute == SwiftDeclarationAttributeKind.objc.rawValue }) .first(where: { $0.attribute == SwiftDeclarationAttributeKind.objc.rawValue })
guard let objcOffset = objcAttribute?.offset, guard let objcByteRange = objcAttribute?.byteRange,
let objcLength = objcAttribute?.length, let range = file.stringView.byteRangeToNSRange(objcByteRange),
let range = file.stringView.byteRangeToNSRange(start: objcOffset, length: objcLength), !dictionary.isObjcAndIBDesignableDeclaredExtension
!dictionary.isObjcAndIBDesignableDeclaredExtension else { else {
return [] return []
} }

View File

@ -127,9 +127,9 @@ public struct RedundantOptionalInitializationRule: SubstitutionCorrectableASTRul
let contents = file.stringView let contents = file.stringView
if let bodyOffset = dictionary.bodyOffset { if let bodyOffset = dictionary.bodyOffset {
return contents.byteRangeToNSRange(start: offset, length: bodyOffset - offset) return contents.byteRangeToNSRange(ByteRange(location: offset, length: bodyOffset - offset))
} else { } else {
return contents.byteRangeToNSRange(start: offset, length: length) return contents.byteRangeToNSRange(ByteRange(location: offset, length: length))
} }
} }
@ -148,9 +148,9 @@ extension SourceKittenDictionary {
} }
private func isVariable(file: SwiftLintFile) -> Bool { private func isVariable(file: SwiftLintFile) -> Bool {
guard let start = offset, let length = length, guard let byteRange = byteRange,
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: start, length: length), let range = contents.byteRangeToNSRange(byteRange),
!file.match(pattern: "\\Avar\\b", with: [.keyword], range: range).isEmpty else { !file.match(pattern: "\\Avar\\b", with: [.keyword], range: range).isEmpty else {
return false return false
} }

View File

@ -90,9 +90,9 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
} }
} }
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] { private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] {
var caseCount = 0 var caseCount = 0
var violations = [Int]() var violations = [ByteCount]()
for enumCase in children(of: dictionary, matching: .enumcase) { for enumCase in children(of: dictionary, matching: .enumcase) {
caseCount += enumElementsCount(dictionary: enumCase) caseCount += enumElementsCount(dictionary: enumCase)
@ -112,8 +112,8 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
}).count }).count
} }
private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] { private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] {
return children(of: dictionary, matching: .enumelement).flatMap { element -> [Int] in return children(of: dictionary, matching: .enumelement).flatMap { element -> [ByteCount] in
guard let name = element.name else { guard let name = element.name else {
return [] return []
} }
@ -122,18 +122,18 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
} }
private func violatingOffsetsForEnumElement(dictionary: SourceKittenDictionary, name: String, private func violatingOffsetsForEnumElement(dictionary: SourceKittenDictionary, name: String,
file: SwiftLintFile) -> [Int] { file: SwiftLintFile) -> [ByteCount] {
let enumInits = filterEnumInits(dictionary: dictionary) let enumInits = filterEnumInits(dictionary: dictionary)
return enumInits.compactMap { dictionary -> Int? in return enumInits.compactMap { dictionary -> ByteCount? in
guard let offset = dictionary.offset, guard let offset = dictionary.offset,
let length = dictionary.length else { let length = dictionary.length else {
return nil return nil
} }
// the string would be quoted if offset and length were used directly // the string would be quoted if offset and length were used directly
let enumCaseName = file.stringView let rangeWithoutQuotes = ByteRange(location: offset + 1, length: length - 2)
.substringWithByteRange(start: offset + 1, length: length - 2) ?? "" let enumCaseName = file.stringView.substringWithByteRange(rangeWithoutQuotes) ?? ""
guard enumCaseName == name else { guard enumCaseName == name else {
return nil return nil
} }

View File

@ -81,7 +81,8 @@ public struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCo
case let start = nameOffset + nameLength, case let start = nameOffset + nameLength,
case let end = dictionary.bodyOffset ?? offset + length, case let end = dictionary.bodyOffset ?? offset + length,
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: start, length: end - start), case let byteRange = ByteRange(location: start, length: end - start),
let range = contents.byteRangeToNSRange(byteRange),
file.match(pattern: "->", excludingSyntaxKinds: excludingKinds, range: range).count == 1, file.match(pattern: "->", excludingSyntaxKinds: excludingKinds, range: range).count == 1,
let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else { let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else {
return [] return []

View File

@ -61,7 +61,7 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule {
} }
private func validate(name: String, dictionary: SourceKittenDictionary = SourceKittenDictionary([:]), private func validate(name: String, dictionary: SourceKittenDictionary = SourceKittenDictionary([:]),
file: SwiftLintFile, offset: Int) -> [StyleViolation] { file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] {
guard !configuration.excluded.contains(name) else { guard !configuration.excluded.contains(name) else {
return [] return []
} }

View File

@ -65,8 +65,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn
guard let offset = dictionary.offset, containsFatalError, guard let offset = dictionary.offset, containsFatalError,
!isFunctionUnavailable(file: file, dictionary: dictionary), !isFunctionUnavailable(file: file, dictionary: dictionary),
let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, let bodyRange = dictionary.bodyByteRange,
let range = file.stringView.byteRangeToNSRange(start: bodyOffset, length: bodyLength), let range = file.stringView.byteRangeToNSRange(bodyRange),
file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else { file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else {
return [] return []
} }
@ -81,8 +81,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
return dictionary.swiftAttributes.contains { dict -> Bool in return dictionary.swiftAttributes.contains { dict -> Bool in
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available, guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
let offset = dict.offset, let length = dict.length, let byteRange = dict.byteRange,
let contents = file.stringView.substringWithByteRange(start: offset, length: length) else { let contents = file.stringView.substringWithByteRange(byteRange) else {
return false return false
} }

View File

@ -38,13 +38,11 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes
guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length), guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
let innerStructure = file.structureDictionary.structures(forByteOffset: byteRange.location).last, let innerStructure = file.structureDictionary.structures(forByteOffset: byteRange.location).last,
innerStructure.statementKind == .case, innerStructure.statementKind == .case,
let caseOffset = innerStructure.offset, let caseRange = innerStructure.byteRange,
let caseLength = innerStructure.length,
let lastPatternEnd = patternEnd(dictionary: innerStructure) else { let lastPatternEnd = patternEnd(dictionary: innerStructure) else {
return nil return nil
} }
let caseRange = NSRange(location: caseOffset, length: caseLength)
let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in
guard let kind = token.kind, guard let kind = token.kind,
token.offset > lastPatternEnd else { token.offset > lastPatternEnd else {
@ -73,8 +71,8 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes
} }
} }
private func patternEnd(dictionary: SourceKittenDictionary) -> Int? { private func patternEnd(dictionary: SourceKittenDictionary) -> ByteCount? {
let patternEnds = dictionary.elements.compactMap { subDictionary -> Int? in let patternEnds = dictionary.elements.compactMap { subDictionary -> ByteCount? in
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
let offset = subDictionary.offset, let offset = subDictionary.offset,
let length = subDictionary.length else { let length = subDictionary.length else {

View File

@ -44,7 +44,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
return [] return []
} }
let offset: Int let offset: ByteCount
let reason: String let reason: String
if firstTokenIsUnderscore { if firstTokenIsUnderscore {
offset = tokens[0].offset offset = tokens[0].offset
@ -83,7 +83,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
return false return false
} }
private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> NSRange? { private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> ByteRange? {
let expectedKind = "source.lang.swift.structure.elem.id" let expectedKind = "source.lang.swift.structure.elem.id"
for subDict in dictionary.elements where subDict.kind == expectedKind { for subDict in dictionary.elements where subDict.kind == expectedKind {
guard let offset = subDict.offset, guard let offset = subDict.offset,
@ -91,7 +91,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
continue continue
} }
return NSRange(location: offset, length: length) return ByteRange(location: offset, length: length)
} }
return nil return nil

View File

@ -54,13 +54,13 @@ public struct XCTFailMessageRule: ASTRule, ConfigurationProviderRule, AutomaticT
} }
private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
guard guard let bodyRange = dictionary.bodyByteRange else {
let bodyOffset = dictionary.bodyOffset, return false
let bodyLength = dictionary.bodyLength else { return false } }
guard bodyLength > 0 else { return true } guard bodyRange.length > 0 else { return true }
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) let body = file.stringView.substringWithByteRange(bodyRange)
return body == "\"\"" return body == "\"\""
} }
} }

View File

@ -46,15 +46,7 @@ public struct XCTSpecificMatcherRule: ASTRule, OptInRule, ConfigurationProviderR
return firstOffset < secondOffset return firstOffset < secondOffset
} }
.prefix(2) .prefix(2)
.compactMap { argument -> String? in .compactMap { $0.byteRange.flatMap(file.stringView.substringWithByteRange) }
guard
let argOffset = argument.offset,
let argLength = argument.length,
let body = file.stringView.substringWithByteRange(start: argOffset, length: argLength)
else { return nil }
return body
}
.sorted { arg1, _ -> Bool in .sorted { arg1, _ -> Bool in
return protectedArguments.contains(arg1) return protectedArguments.contains(arg1)
} }

View File

@ -61,15 +61,14 @@ public struct AnyObjectProtocolRule: SubstitutionCorrectableASTRule, OptInRule,
return dictionary.elements.compactMap { subDict -> NSRange? in return dictionary.elements.compactMap { subDict -> NSRange? in
guard guard
let offset = subDict.offset, let byteRange = subDict.byteRange,
let length = subDict.length, let content = file.stringView.substringWithByteRange(byteRange),
let content = file.stringView.substringWithByteRange(start: offset, length: length),
content == "class" content == "class"
else { else {
return nil return nil
} }
return file.stringView.byteRangeToNSRange(start: offset, length: length) return file.stringView.byteRangeToNSRange(byteRange)
} }
} }
} }

View File

@ -49,14 +49,14 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
guard kind == .call, let name = dictionary.name, name.hasSuffix(".map"), guard kind == .call, let name = dictionary.name, name.hasSuffix(".map"),
let bodyOffset = dictionary.bodyOffset, let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength, let bodyLength = dictionary.bodyLength,
let bodyRange = dictionary.bodyByteRange,
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let offset = dictionary.offset else { let offset = dictionary.offset else {
return [] return []
} }
let range = NSRange(location: bodyOffset, length: bodyLength) let tokens = file.syntaxMap.tokens(inByteRange: bodyRange).filter { token in
let tokens = file.syntaxMap.tokens(inByteRange: range).filter { token in
guard let kind = token.kind else { guard let kind = token.kind else {
return false return false
} }
@ -84,37 +84,39 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
} }
private func isClosureParameter(firstToken: SwiftLintSyntaxToken, private func isClosureParameter(firstToken: SwiftLintSyntaxToken,
nameEndPosition: Int, nameEndPosition: ByteCount,
file: SwiftLintFile) -> Bool { file: SwiftLintFile) -> Bool {
let length = firstToken.offset - nameEndPosition let length = firstToken.offset - nameEndPosition
guard length > 0, guard length > 0,
case let contents = file.stringView, case let contents = file.stringView,
let byteRange = contents.byteRangeToNSRange(start: nameEndPosition, length: length) else { case let byteRange = ByteRange(location: nameEndPosition, length: length),
return false let nsRange = contents.byteRangeToNSRange(byteRange)
else {
return false
} }
let pattern = regex("\\A\\s*\\(?\\s*\\{") let pattern = regex("\\A\\s*\\(?\\s*\\{")
return pattern.firstMatch(in: file.contents, options: .anchored, range: byteRange) != nil return pattern.firstMatch(in: file.contents, options: .anchored, range: nsRange) != nil
} }
private func containsTrailingContent(lastToken: SwiftLintSyntaxToken, private func containsTrailingContent(lastToken: SwiftLintSyntaxToken,
bodyEndPosition: Int, bodyEndPosition: ByteCount,
file: SwiftLintFile) -> Bool { file: SwiftLintFile) -> Bool {
let lastTokenEnd = lastToken.offset + lastToken.length let lastTokenEnd = lastToken.offset + lastToken.length
let remainingLength = bodyEndPosition - lastTokenEnd let remainingLength = bodyEndPosition - lastTokenEnd
let remainingRange = NSRange(location: lastTokenEnd, length: remainingLength) let remainingRange = ByteRange(location: lastTokenEnd, length: remainingLength)
return containsContent(inByteRange: remainingRange, file: file) return containsContent(inByteRange: remainingRange, file: file)
} }
private func containsLeadingContent(tokens: [SwiftLintSyntaxToken], private func containsLeadingContent(tokens: [SwiftLintSyntaxToken],
bodyStartPosition: Int, bodyStartPosition: ByteCount,
file: SwiftLintFile) -> Bool { file: SwiftLintFile) -> Bool {
let inTokenPosition = tokens.firstIndex(where: { token in let inTokenPosition = tokens.firstIndex(where: { token in
token.kind == .keyword && file.contents(for: token) == "in" token.kind == .keyword && file.contents(for: token) == "in"
}) })
let firstToken: SwiftLintSyntaxToken let firstToken: SwiftLintSyntaxToken
let start: Int let start: ByteCount
if let position = inTokenPosition { if let position = inTokenPosition {
let index = tokens.index(after: position) let index = tokens.index(after: position)
firstToken = tokens[index] firstToken = tokens[index]
@ -126,25 +128,26 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
} }
let length = firstToken.offset - start let length = firstToken.offset - start
let remainingRange = NSRange(location: start, length: length) let remainingRange = ByteRange(location: start, length: length)
return containsContent(inByteRange: remainingRange, file: file) return containsContent(inByteRange: remainingRange, file: file)
} }
private func containsContent(inByteRange byteRange: NSRange, file: SwiftLintFile) -> Bool { private func containsContent(inByteRange byteRange: ByteRange, file: SwiftLintFile) -> Bool {
let nsstring = file.stringView let stringView = file.stringView
let remainingTokens = file.syntaxMap.tokens(inByteRange: byteRange) let remainingTokens = file.syntaxMap.tokens(inByteRange: byteRange)
let ranges = NSMutableIndexSet(indexesIn: byteRange) guard let nsRange = stringView.byteRangeToNSRange(byteRange) else {
return false
}
for token in remainingTokens { let ranges = NSMutableIndexSet(indexesIn: nsRange)
ranges.remove(in: token.range)
for tokenNSRange in remainingTokens.compactMap({ stringView.byteRangeToNSRange($0.range) }) {
ranges.remove(in: tokenNSRange)
} }
var containsContent = false var containsContent = false
ranges.enumerateRanges(options: []) { range, stop in ranges.enumerateRanges(options: []) { range, stop in
guard let substring = nsstring.substringWithByteRange(start: range.location, length: range.length) else { let substring = stringView.substring(with: range)
return
}
let processedSubstring = substring let processedSubstring = substring
.trimmingCharacters(in: CharacterSet(charactersIn: "{}")) .trimmingCharacters(in: CharacterSet(charactersIn: "{}"))
.trimmingCharacters(in: .whitespacesAndNewlines) .trimmingCharacters(in: .whitespacesAndNewlines)

View File

@ -65,8 +65,10 @@ public struct ClassDelegateProtocolRule: ASTRule, ConfigurationProviderRule, Aut
let bodyOffset = dictionary.bodyOffset, let bodyOffset = dictionary.bodyOffset,
case let contents = file.stringView, case let contents = file.stringView,
case let start = nameOffset + nameLength, case let start = nameOffset + nameLength,
let range = contents.byteRangeToNSRange(start: start, length: bodyOffset - start), case let byteRange = ByteRange(location: start, length: bodyOffset - start),
!isClassProtocol(file: file, range: range) else { let range = contents.byteRangeToNSRange(byteRange),
!isClassProtocol(file: file, range: range)
else {
return [] return []
} }

View File

@ -52,10 +52,9 @@ public struct CompilerProtocolInitRule: ASTRule, ConfigurationProviderRule {
guard compilerProtocol.initCallNames.contains(name), guard compilerProtocol.initCallNames.contains(name),
case let arguments = dictionary.enclosedArguments.compactMap({ $0.name }), case let arguments = dictionary.enclosedArguments.compactMap({ $0.name }),
compilerProtocol.match(arguments: arguments), compilerProtocol.match(arguments: arguments),
let offset = dictionary.offset, let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange)
let length = dictionary.length, else {
let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else { continue
continue
} }
return [(compilerProtocol, range)] return [(compilerProtocol, range)]

View File

@ -53,9 +53,9 @@ public struct DeploymentTargetRule: ConfigurationProviderRule {
return file.rangesAndTokens(matching: pattern).flatMap { range, tokens -> [StyleViolation] in return file.rangesAndTokens(matching: pattern).flatMap { range, tokens -> [StyleViolation] in
guard let availabilityToken = tokens.first, guard let availabilityToken = tokens.first,
availabilityToken.kind == .keyword, availabilityToken.kind == .keyword,
let tokenRange = file.stringView.byteRangeToNSRange(start: availabilityToken.offset, let tokenRange = file.stringView.byteRangeToNSRange(availabilityToken.range)
length: availabilityToken.length) else { else {
return [] return []
} }
let rangeToSearch = NSRange(location: tokenRange.upperBound, length: range.length - tokenRange.length) let rangeToSearch = NSRange(location: tokenRange.upperBound, length: range.length - tokenRange.length)
@ -83,17 +83,19 @@ public struct DeploymentTargetRule: ConfigurationProviderRule {
let contents = file.stringView let contents = file.stringView
return attributes.flatMap { dictionary -> [StyleViolation] in return attributes.flatMap { dictionary -> [StyleViolation] in
guard let offset = dictionary.offset, let length = dictionary.length, guard let byteRange = dictionary.byteRange,
let range = contents.byteRangeToNSRange(start: offset, length: length) else { let range = contents.byteRangeToNSRange(byteRange)
return [] else {
return []
} }
return validate(range: range, file: file, violationType: "attribute", byteOffsetToReport: offset) return validate(range: range, file: file, violationType: "attribute",
byteOffsetToReport: byteRange.location)
}.unique }.unique
} }
private func validate(range: NSRange, file: SwiftLintFile, violationType: String, private func validate(range: NSRange, file: SwiftLintFile, violationType: String,
byteOffsetToReport: Int) -> [StyleViolation] { byteOffsetToReport: ByteCount) -> [StyleViolation] {
let platformToConfiguredMinVersion = self.platformToConfiguredMinVersion let platformToConfiguredMinVersion = self.platformToConfiguredMinVersion
let allPlatforms = "(?:" + platformToConfiguredMinVersion.keys.joined(separator: "|") + ")" let allPlatforms = "(?:" + platformToConfiguredMinVersion.keys.joined(separator: "|") + ")"
let pattern = "\(allPlatforms) [\\d\\.]+" let pattern = "\(allPlatforms) [\\d\\.]+"

View File

@ -48,7 +48,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
} }
private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary, private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
kind: SwiftExpressionKind) -> [Int] { kind: SwiftExpressionKind) -> [ByteCount] {
guard kind == .call, guard kind == .call,
let name = dictionary.name, let name = dictionary.name,
name.hasSuffix(".addObserver"), name.hasSuffix(".addObserver"),
@ -57,7 +57,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
argumentsNames == ["forName", "object", "queue"] || argumentsNames == ["forName", "object", "queue"] ||
argumentsNames == ["forName", "object", "queue", "using"], argumentsNames == ["forName", "object", "queue", "using"],
let offset = dictionary.offset, let offset = dictionary.offset,
let range = file.stringView.byteRangeToNSRange(start: 0, length: offset) else { let range = file.stringView.byteRangeToNSRange(ByteRange(location: 0, length: offset)) else {
return [] return []
} }
@ -83,7 +83,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
} }
private extension SourceKittenDictionary { private extension SourceKittenDictionary {
func functions(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] { func functions(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
return structures(forByteOffset: byteOffset) return structures(forByteOffset: byteOffset)
.filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true } .filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true }
} }

View File

@ -47,11 +47,12 @@ public struct DuplicateEnumCasesRule: ConfigurationProviderRule, ASTRule, Automa
.compactMap { substructureElements(of: $0, matching: .enumelement) } .compactMap { substructureElements(of: $0, matching: .enumelement) }
.flatMap { $0 } .flatMap { $0 }
var elementsByName: [String: [Int]] = [:] var elementsByName: [String: [ByteCount]] = [:]
for element in enumElements { for element in enumElements {
guard let name = element.name, guard let name = element.name,
let nameWithoutParameters = name.split(separator: "(").first, let nameWithoutParameters = name.split(separator: "(").first,
let offset = element.offset else { let offset = element.offset
else {
continue continue
} }

View File

@ -34,9 +34,7 @@ public struct DynamicInlineRule: ASTRule, ConfigurationProviderRule, AutomaticTe
case let attributes = dictionary.enclosedSwiftAttributes, case let attributes = dictionary.enclosedSwiftAttributes,
attributes.contains(.dynamic), attributes.contains(.dynamic),
attributes.contains(.inline), attributes.contains(.inline),
let funcByteOffset = dictionary.offset, let funcOffset = dictionary.offset.flatMap(file.stringView.location),
let funcOffset = file.stringView
.byteRangeToNSRange(start: funcByteOffset, length: 0)?.location,
case let inlinePattern = regex("@inline"), case let inlinePattern = regex("@inline"),
case let range = NSRange(location: 0, length: funcOffset), case let range = NSRange(location: 0, length: funcOffset),
let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range) let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range)

View File

@ -131,8 +131,7 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom
} }
} }
let violationRange = file.stringView.byteRangeToNSRange(start: leftmostToken.offset, let violationRange = file.stringView.byteRangeToNSRange(leftmostToken.range)
length: leftmostToken.length)
return violationRange return violationRange
} }
@ -175,12 +174,13 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom
private extension StringView { private extension StringView {
func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? { func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? {
return substringWithByteRange(start: syntaxToken.offset, length: syntaxToken.length) return substringWithByteRange(syntaxToken.range)
} }
func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? { func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? {
return substringWithByteRange(start: startToken.offset + startToken.length, let byteRange = ByteRange(location: startToken.range.upperBound,
length: endToken.offset - startToken.offset - startToken.length) length: endToken.offset - startToken.range.upperBound)
return substringWithByteRange(byteRange)
} }
func isDotOrOptionalChainingBetweenTokens(_ startToken: SwiftLintSyntaxToken, func isDotOrOptionalChainingBetweenTokens(_ startToken: SwiftLintSyntaxToken,

View File

@ -63,7 +63,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule {
case let outerKindIndex = kinds.index(before: brace.offset), case let outerKindIndex = kinds.index(before: brace.offset),
case let outerKind = kinds[outerKindIndex], case let outerKind = kinds[outerKindIndex],
case let braceEnd = brace.element.byteRange.upperBound, case let braceEnd = brace.element.byteRange.upperBound,
case let tokensRange = NSRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd), case let tokensRange = ByteRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd),
case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange), case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange),
!tokens.contains(where: isNotComment) else { !tokens.contains(where: isNotComment) else {
return nil return nil
@ -76,7 +76,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule {
} }
} }
private func isBrace(offset: Int, element: (kind: String, byteRange: NSRange)) -> Bool { private func isBrace(offset: Int, element: (kind: String, byteRange: ByteRange)) -> Bool {
return StatementKind(rawValue: element.kind) == .brace return StatementKind(rawValue: element.kind) == .brace
} }

View File

@ -41,14 +41,14 @@ public struct LowerACLThanParentRule: OptInRule, ConfigurationProviderRule, Auto
} }
private func validateACL(isHigherThan parentAccessibility: AccessControlLevel, private func validateACL(isHigherThan parentAccessibility: AccessControlLevel,
in substructure: SourceKittenDictionary) -> [Int] { in substructure: SourceKittenDictionary) -> [ByteCount] {
return substructure.substructure.flatMap { element -> [Int] in return substructure.substructure.flatMap { element -> [ByteCount] in
guard let elementKind = element.declarationKind, guard let elementKind = element.declarationKind,
elementKind.isRelevantDeclaration else { elementKind.isRelevantDeclaration else {
return [] return []
} }
var violationOffset: Int? var violationOffset: ByteCount?
let accessibility = element.accessibility ?? .internal let accessibility = element.accessibility ?? .internal
// Swift 5 infers members of private types with no explicit ACL attribute to be `internal`. // Swift 5 infers members of private types with no explicit ACL attribute to be `internal`.
let isInferredACL = accessibility == .internal && !element.enclosedSwiftAttributes.contains(.internal) let isInferredACL = accessibility == .internal && !element.enclosedSwiftAttributes.contains(.internal)

View File

@ -189,8 +189,8 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule {
} }
return !syntaxTokens.isEmpty && SyntaxKind.commentKinds.contains(syntaxKind) return !syntaxTokens.isEmpty && SyntaxKind.commentKinds.contains(syntaxKind)
}.compactMap { range, syntaxTokens in }.compactMap { range, syntaxTokens in
let identifierRange = file.stringView let byteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0) let identifierRange = file.stringView.byteRangeToNSRange(byteRange)
return identifierRange.map { NSUnionRange($0, range) } return identifierRange.map { NSUnionRange($0, range) }
} }
} }

View File

@ -2,7 +2,7 @@ import SourceKittenFramework
private extension SwiftLintFile { private extension SwiftLintFile {
func missingDocOffsets(in dictionary: SourceKittenDictionary, func missingDocOffsets(in dictionary: SourceKittenDictionary,
acls: [AccessControlLevel]) -> [(Int, AccessControlLevel)] { acls: [AccessControlLevel]) -> [(ByteCount, AccessControlLevel)] {
if dictionary.enclosedSwiftAttributes.contains(.override) || if dictionary.enclosedSwiftAttributes.contains(.override) ||
!dictionary.inheritedTypes.isEmpty { !dictionary.inheritedTypes.isEmpty {
return [] return []
@ -77,8 +77,7 @@ public struct MissingDocsRule: OptInRule, ConfigurationProviderRule, AutomaticTe
public func validate(file: SwiftLintFile) -> [StyleViolation] { public func validate(file: SwiftLintFile) -> [StyleViolation] {
let acls = configuration.parameters.map { $0.value } let acls = configuration.parameters.map { $0.value }
let dict = file.structureDictionary let dict = file.structureDictionary
return file.missingDocOffsets(in: dict, return file.missingDocOffsets(in: dict, acls: acls).map { offset, acl in
acls: acls).map { (offset: Int, acl: AccessControlLevel) in
StyleViolation(ruleDescription: type(of: self).description, StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.parameters.first { $0.value == acl }?.severity ?? .warning, severity: configuration.parameters.first { $0.value == acl }?.severity ?? .warning,
location: Location(file: file, byteOffset: offset), location: Location(file: file, byteOffset: offset),

View File

@ -28,9 +28,8 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
dictionary.name == "NSLocalizedString", dictionary.name == "NSLocalizedString",
let firstArgument = dictionary.enclosedArguments.first, let firstArgument = dictionary.enclosedArguments.first,
firstArgument.name == nil, firstArgument.name == nil,
let offset = firstArgument.offset, let byteRange = firstArgument.byteRange,
let length = firstArgument.length, case let kinds = file.syntaxMap.kinds(inByteRange: byteRange),
case let kinds = file.syntaxMap.kinds(inByteRange: NSRange(location: offset, length: length)),
!kinds.allSatisfy({ $0 == .string }) else { !kinds.allSatisfy({ $0 == .string }) else {
return [] return []
} }
@ -38,7 +37,7 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
return [ return [
StyleViolation(ruleDescription: type(of: self).description, StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity, severity: configuration.severity,
location: Location(file: file, byteOffset: offset)) location: Location(file: file, byteOffset: byteRange.location))
] ]
} }
} }

View File

@ -29,8 +29,8 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu
} }
private func violationOffsets(file: SwiftLintFile, private func violationOffsets(file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> [Int] { dictionary: SourceKittenDictionary) -> [ByteCount] {
return dictionary.substructure.flatMap { subDict -> [Int] in return dictionary.substructure.flatMap { subDict -> [ByteCount] in
// complete detachment is allowed on `deinit` // complete detachment is allowed on `deinit`
if subDict.declarationKind == .functionMethodInstance, if subDict.declarationKind == .functionMethodInstance,
subDict.name == "deinit" { subDict.name == "deinit" {
@ -51,16 +51,11 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu
private var methodName = "NotificationCenter.default.removeObserver" private var methodName = "NotificationCenter.default.removeObserver"
private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
guard let bodyOffset = dictionary.bodyOffset, guard let bodyRange = dictionary.bodyByteRange,
let bodyLength = dictionary.bodyLength else { case let tokens = file.syntaxMap.tokens(inByteRange: bodyRange),
return false tokens.kinds == [.keyword],
} let token = tokens.first
else {
let range = NSRange(location: bodyOffset, length: bodyLength)
let tokens = file.syntaxMap.tokens(inByteRange: range)
let types = tokens.kinds
guard types == [.keyword], let token = tokens.first else {
return false return false
} }

View File

@ -55,12 +55,12 @@ public struct OrphanedDocCommentRule: ConfigurationProviderRule {
return token.kind == .docComment || token.kind == .docCommentField return token.kind == .docComment || token.kind == .docCommentField
} }
let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [NSRange]? in let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [ByteRange]? in
guard let docOffset = dictionary.docOffset, let docLength = dictionary.docLength else { guard let docOffset = dictionary.docOffset, let docLength = dictionary.docLength else {
return nil return nil
} }
return [NSRange(location: docOffset, length: docLength)] return [ByteRange(location: docOffset, length: docLength)]
}.sorted { $0.location < $1.location } }.sorted { $0.location < $1.location }
return docStringsTokens return docStringsTokens

View File

@ -40,11 +40,12 @@ public struct OverrideInExtensionRule: ConfigurationProviderRule, OptInRule, Aut
return elements return elements
.filter { $0.kind == .extension && !susceptibleNames.contains($0.name) } .filter { $0.kind == .extension && !susceptibleNames.contains($0.name) }
.flatMap { element in .flatMap { element in
return element.dictionary.substructure.compactMap { element -> Int? in return element.dictionary.substructure.compactMap { element -> ByteCount? in
guard element.declarationKind != nil, guard element.declarationKind != nil,
element.enclosedSwiftAttributes.contains(.override), element.enclosedSwiftAttributes.contains(.override),
let offset = element.offset else { let offset = element.offset
return nil else {
return nil
} }
return offset return offset

View File

@ -60,8 +60,8 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
} }
} }
private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [Int] { private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [ByteCount] {
return substructure.flatMap { dictionary -> [Int] in return substructure.flatMap { dictionary -> [ByteCount] in
let substructure = dictionary.substructure.flatMap { dict -> [SourceKittenDictionary] in let substructure = dictionary.substructure.flatMap { dict -> [SourceKittenDictionary] in
if dict.expressionKind == .closure { if dict.expressionKind == .closure {
return dict.substructure return dict.substructure
@ -74,7 +74,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
} }
} }
private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [Int] { private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [ByteCount] {
guard guard
dictionary.kind != nil, dictionary.kind != nil,
let offset = dictionary.offset let offset = dictionary.offset
@ -90,7 +90,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
return dictionary.substructure.compactMap(toViolationOffset) return dictionary.substructure.compactMap(toViolationOffset)
} }
private func toViolationOffset(dictionary: SourceKittenDictionary) -> Int? { private func toViolationOffset(dictionary: SourceKittenDictionary) -> ByteCount? {
guard guard
let name = dictionary.name, let name = dictionary.name,
let offset = dictionary.offset, let offset = dictionary.offset,

View File

@ -1,7 +1,7 @@
import SourceKittenFramework import SourceKittenFramework
public struct RawValueForCamelCasedCodableEnumRule: ASTRule, OptInRule, ConfigurationProviderRule, public struct RawValueForCamelCasedCodableEnumRule: ASTRule, OptInRule, ConfigurationProviderRule,
AutomaticTestableRule { AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning) public var configuration = SeverityConfiguration(.warning)
public init() {} public init() {}
@ -104,13 +104,11 @@ AutomaticTestableRule {
} }
} }
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] { private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] {
let locs = substructureElements(of: dictionary, matching: .enumcase) return substructureElements(of: dictionary, matching: .enumcase)
.compactMap { substructureElements(of: $0, matching: .enumelement) } .compactMap { substructureElements(of: $0, matching: .enumelement) }
.flatMap(camelCasedEnumCasesMissingRawValue) .flatMap(camelCasedEnumCasesMissingRawValue)
.compactMap { $0.offset } .compactMap { $0.offset }
return locs
} }
private func substructureElements(of dict: SourceKittenDictionary, private func substructureElements(of dict: SourceKittenDictionary,

View File

@ -89,7 +89,7 @@ public struct RequiredEnumCaseRule: ASTRule, OptInRule, ConfigurationProviderRul
/// ///
/// - returns: Location of where the enum declaration starts. /// - returns: Location of where the enum declaration starts.
static func location(from dictionary: SourceKittenDictionary, in file: SwiftLintFile) -> Location { static func location(from dictionary: SourceKittenDictionary, in file: SwiftLintFile) -> Location {
return Location(file: file, characterOffset: dictionary.offset ?? 0) return Location(file: file, byteOffset: dictionary.offset ?? 0)
} }
/// Determines the names of cases found in the enum. /// Determines the names of cases found in the enum.

View File

@ -27,8 +27,9 @@ public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, OptInRule,
case let attributes = dictionary.enclosedSwiftAttributes, case let attributes = dictionary.enclosedSwiftAttributes,
attributes.contains(.iboutlet), attributes.contains(.iboutlet),
attributes.contains(.weak), attributes.contains(.weak),
let offset = dictionary.offset else { let offset = dictionary.offset
return [] else {
return []
} }
return [ return [

View File

@ -29,26 +29,28 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi
public func validate(file: SwiftLintFile, kind: SwiftExpressionKind, public func validate(file: SwiftLintFile, kind: SwiftExpressionKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] { dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .closure, let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, guard kind == .closure, let bodyRange = dictionary.bodyByteRange,
case let contents = file.stringView, case let contents = file.stringView,
let closureRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength), let closureRange = contents.byteRangeToNSRange(bodyRange),
let inTokenRange = file.match(pattern: "\\bin\\b", with: [.keyword], range: closureRange).first, let inTokenRange = file.match(pattern: "\\bin\\b", with: [.keyword], range: closureRange).first,
let inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location, let inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location,
length: inTokenRange.length) else { length: inTokenRange.length)
return [] else {
return []
} }
let length = inTokenByteRange.location - bodyOffset let length = inTokenByteRange.location - bodyRange.location
let variables = localVariableDeclarations(inByteRange: NSRange(location: bodyOffset, length: length), let variables = localVariableDeclarations(inByteRange: ByteRange(location: bodyRange.location, length: length),
structureDictionary: file.structureDictionary) structureDictionary: file.structureDictionary)
let unownedVariableOffsets = variables.compactMap { dictionary in let unownedVariableOffsets = variables.compactMap { dictionary in
return dictionary.swiftAttributes.first { attributeDict in return dictionary.swiftAttributes.first { attributeDict in
guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak, guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak,
let offset = attributeDict.offset, let length = attributeDict.length else { let attributeByteRange = attributeDict.byteRange
return false else {
return false
} }
return contents.substringWithByteRange(start: offset, length: length) == "unowned" return contents.substringWithByteRange(attributeByteRange) == "unowned"
}?.offset }?.offset
} }
@ -59,13 +61,14 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi
} }
} }
private func localVariableDeclarations(inByteRange byteRange: NSRange, private func localVariableDeclarations(inByteRange byteRange: ByteRange,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
return structureDictionary.traverseBreadthFirst { dictionary in return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .varLocal, guard dictionary.declarationKind == .varLocal,
let variableByteRange = dictionary.byteRange, let variableByteRange = dictionary.byteRange,
byteRange.intersects(variableByteRange) else { byteRange.intersects(variableByteRange)
return nil else {
return nil
} }
return [dictionary] return [dictionary]
} }

View File

@ -80,12 +80,14 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat
guard kind == .closure, guard kind == .closure,
let offset = dictionary.offset, let offset = dictionary.offset,
let length = dictionary.length, let length = dictionary.length,
let closureRange = contents.byteRangeToNSRange(start: offset, length: length) let closureByteRange = dictionary.byteRange,
let closureRange = contents.byteRangeToNSRange(closureByteRange)
else { return [] } else { return [] }
let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length) let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length)
let captureListSearchLength = firstSubstructureOffset - offset let captureListSearchLength = firstSubstructureOffset - offset
guard let captureListSearchRange = contents.byteRangeToNSRange(start: offset, length: captureListSearchLength), let captureListSearchByteRange = ByteRange(location: offset, length: captureListSearchLength)
guard let captureListSearchRange = contents.byteRangeToNSRange(captureListSearchByteRange),
let match = captureListRegex.firstMatch(in: file.contents, options: [], range: captureListSearchRange) let match = captureListRegex.firstMatch(in: file.contents, options: [], range: captureListSearchRange)
else { return [] } else { return [] }
@ -131,7 +133,7 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat
} }
} }
private func identifierStrings(in file: SwiftLintFile, byteRange: NSRange) -> Set<String> { private func identifierStrings(in file: SwiftLintFile, byteRange: ByteRange) -> Set<String> {
let identifiers = file.syntaxMap let identifiers = file.syntaxMap
.tokens(inByteRange: byteRange) .tokens(inByteRange: byteRange)
.compactMap { token -> String? in .compactMap { token -> String? in

View File

@ -127,54 +127,62 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let bodyLength = dictionary.bodyLength, let bodyLength = dictionary.bodyLength,
bodyLength > 0 else { bodyLength > 0
return [] else {
return []
} }
let rangeStart = nameOffset + nameLength let rangeStart = nameOffset + nameLength
let rangeLength = (offset + length) - (nameOffset + nameLength) let rangeLength = (offset + length) - (nameOffset + nameLength)
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
let parameters = dictionary.enclosedVarParameters let parameters = dictionary.enclosedVarParameters
let contents = file.stringView let contents = file.stringView
return parameters.compactMap { param -> (NSRange, String)? in return parameters.compactMap { param -> (NSRange, String)? in
guard let paramOffset = param.offset, self.rangeAndName(parameter: param, contents: contents, byteRange: byteRange, file: file)
let name = param.name, }
name != "_", }
let regex = try? NSRegularExpression(pattern: name,
options: [.ignoreMetacharacters]), private func rangeAndName(parameter: SourceKittenDictionary, contents: StringView, byteRange: ByteRange,
let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) file: SwiftLintFile) -> (range: NSRange, name: String)? {
guard let paramOffset = parameter.offset,
let name = parameter.name,
name != "_",
let regex = try? NSRegularExpression(pattern: name,
options: [.ignoreMetacharacters]),
let range = contents.byteRangeToNSRange(byteRange)
else {
return nil
}
let paramLength = ByteCount(name.lengthOfBytes(using: .utf8))
let matches = regex.matches(in: file.contents, options: [], range: range).ranges()
for range in matches {
guard let byteRange = contents.NSRangeToByteRange(start: range.location,
length: range.length),
// if it's the parameter declaration itself, we should skip
byteRange.location > paramOffset,
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange)
else { else {
continue
}
let token = tokens.first(where: { token -> Bool in
return (token.kind == .identifier
|| (token.kind == .keyword && name == "self")) &&
token.offset == byteRange.location &&
token.length == byteRange.length
})
// found a usage, there's no violation!
guard token == nil else {
return nil return nil
} }
}
let paramLength = name.lengthOfBytes(using: .utf8) let violationByteRange = ByteRange(location: paramOffset, length: paramLength)
return contents.byteRangeToNSRange(violationByteRange).map { range in
let matches = regex.matches(in: file.contents, options: [], range: range).ranges() return (range, name)
for range in matches {
guard let byteRange = contents.NSRangeToByteRange(start: range.location,
length: range.length),
// if it's the parameter declaration itself, we should skip
byteRange.location > paramOffset,
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange) else {
continue
}
let token = tokens.first(where: { token -> Bool in
return (token.kind == .identifier
|| (token.kind == .keyword && name == "self")) &&
token.offset == byteRange.location &&
token.length == byteRange.length
})
// found a usage, there's no violation!
guard token == nil else {
return nil
}
}
if let range = contents.byteRangeToNSRange(start: paramOffset, length: paramLength) {
return (range, name)
}
return nil
} }
} }

View File

@ -103,9 +103,9 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config
let contentsNSString = file.stringView let contentsNSString = file.stringView
if let byteRange = contentsNSString.NSRangeToByteRange(start: violationRange.location, if let byteRange = contentsNSString.NSRangeToByteRange(start: violationRange.location,
length: violationRange.length), length: violationRange.length),
let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }), let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }) {
let nextTokenLocation = contentsNSString.byteRangeToNSRange(start: nextToken.offset, length: 0) { let nextTokenLocation = contentsNSString.location(fromByteOffset: nextToken.offset)
rangeToRemove.length = nextTokenLocation.location - violationRange.location rangeToRemove.length = nextTokenLocation - violationRange.location
} }
return (rangeToRemove, "") return (rangeToRemove, "")
@ -114,22 +114,18 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config
public func violationRanges(in file: SwiftLintFile, kind: StatementKind, public func violationRanges(in file: SwiftLintFile, kind: StatementKind,
dictionary: SourceKittenDictionary) -> [NSRange] { dictionary: SourceKittenDictionary) -> [NSRange] {
guard type(of: self).kinds.contains(kind), guard type(of: self).kinds.contains(kind),
let offset = dictionary.offset, let length = dictionary.length, let byteRange = dictionary.byteRange,
case let byteRange = NSRange(location: offset, length: length),
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange), case let tokens = file.syntaxMap.tokens(inByteRange: byteRange),
let firstToken = tokens.first, let firstToken = tokens.first,
firstToken.kind == .identifier, firstToken.kind == .identifier,
let tokenContent = file.contents(for: firstToken), let tokenContent = file.contents(for: firstToken),
case let contents = file.stringView, case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: offset, length: length) else { let range = contents.byteRangeToNSRange(byteRange),
return [] case let pattern = "(?:break|continue)\\s+\(tokenContent)\\b",
} file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty,
let violationRange = contents.byteRangeToNSRange(firstToken.range)
let pattern = "(?:break|continue)\\s+\(tokenContent)\\b" else {
guard file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty, return []
let violationRange = contents.byteRangeToNSRange(start: firstToken.offset,
length: firstToken.length) else {
return []
} }
return [violationRange] return [violationRange]

View File

@ -4,7 +4,7 @@ import SourceKittenFramework
public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProviderRule, AnalyzerRule, CollectingRule { public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProviderRule, AnalyzerRule, CollectingRule {
public struct FileUSRs { public struct FileUSRs {
var referenced: Set<String> var referenced: Set<String>
var declared: [(usr: String, nameOffset: Int)] var declared: [(usr: String, nameOffset: ByteCount)]
var testCaseUSRs: Set<String> var testCaseUSRs: Set<String>
} }
@ -121,9 +121,9 @@ public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProvide
} }
private func violationOffsets(in file: SwiftLintFile, compilerArguments: [String], private func violationOffsets(in file: SwiftLintFile, compilerArguments: [String],
declaredUSRs: [(usr: String, nameOffset: Int)], declaredUSRs: [(usr: String, nameOffset: ByteCount)],
allReferencedUSRs: Set<String>, allReferencedUSRs: Set<String>,
allTestCaseUSRs: Set<String>) -> [Int] { allTestCaseUSRs: Set<String>) -> [ByteCount] {
// Unused declarations are: // Unused declarations are:
// 1. all declarations // 1. all declarations
// 2. minus all references // 2. minus all references
@ -160,7 +160,7 @@ private extension SwiftLintFile {
return nil return nil
} }
let offset = Int64(token.offset) let offset = token.offset
let request = Request.cursorInfo(file: path, offset: offset, arguments: compilerArguments) let request = Request.cursorInfo(file: path, offset: offset, arguments: compilerArguments)
guard var cursorInfo = try? request.sendIfNotDisabled() else { guard var cursorInfo = try? request.sendIfNotDisabled() else {
return nil return nil
@ -169,14 +169,14 @@ private extension SwiftLintFile {
if let acl = editorOpen.aclAtOffset(offset) { if let acl = editorOpen.aclAtOffset(offset) {
cursorInfo["key.accessibility"] = acl.rawValue cursorInfo["key.accessibility"] = acl.rawValue
} }
cursorInfo["swiftlint.offset"] = offset cursorInfo["swiftlint.offset"] = Int64(offset.value)
return cursorInfo return cursorInfo
} }
.map(SourceKittenDictionary.init) .map(SourceKittenDictionary.init)
} }
static func declaredUSRs(allCursorInfo: [SourceKittenDictionary], includePublicAndOpen: Bool) static func declaredUSRs(allCursorInfo: [SourceKittenDictionary], includePublicAndOpen: Bool)
-> [(usr: String, nameOffset: Int)] { -> [(usr: String, nameOffset: ByteCount)] {
return allCursorInfo.compactMap { cursorInfo in return allCursorInfo.compactMap { cursorInfo in
return declaredUSRAndOffset(cursorInfo: cursorInfo, includePublicAndOpen: includePublicAndOpen) return declaredUSRAndOffset(cursorInfo: cursorInfo, includePublicAndOpen: includePublicAndOpen)
} }
@ -191,7 +191,7 @@ private extension SwiftLintFile {
} }
private static func declaredUSRAndOffset(cursorInfo: SourceKittenDictionary, includePublicAndOpen: Bool) private static func declaredUSRAndOffset(cursorInfo: SourceKittenDictionary, includePublicAndOpen: Bool)
-> (usr: String, nameOffset: Int)? { -> (usr: String, nameOffset: ByteCount)? {
if let offset = cursorInfo.swiftlintOffset, if let offset = cursorInfo.swiftlintOffset,
let usr = cursorInfo.usr, let usr = cursorInfo.usr,
let kind = cursorInfo.declarationKind, let kind = cursorInfo.declarationKind,
@ -231,7 +231,7 @@ private extension SwiftLintFile {
return nil return nil
} }
return (usr, Int(offset)) return (usr, ByteCount(offset))
} }
return nil return nil
@ -276,7 +276,7 @@ private extension SourceKittenDictionary {
return value["key.annotated_decl"] as? String return value["key.annotated_decl"] as? String
} }
func aclAtOffset(_ offset: Int64) -> AccessControlLevel? { func aclAtOffset(_ offset: ByteCount) -> AccessControlLevel? {
if let nameOffset = nameOffset, if let nameOffset = nameOffset,
nameOffset == offset, nameOffset == offset,
let acl = accessibility { let acl = accessibility {

View File

@ -203,7 +203,7 @@ private extension SwiftLintFile {
if syntaxKindsToSkip.contains(tokenKind) { if syntaxKindsToSkip.contains(tokenKind) {
continue continue
} }
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: Int64(token.offset), let cursorInfoRequest = Request.cursorInfo(file: path!, offset: token.offset,
arguments: compilerArguments) arguments: compilerArguments)
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else { guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else {
queuedPrintError("Could not get cursor info") queuedPrintError("Could not get cursor info")
@ -252,7 +252,7 @@ private extension SwiftLintFile {
} }
// Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request // Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request
func operatorImports(arguments: [String], processedTokenOffsets: Set<Int>) -> Set<String> { func operatorImports(arguments: [String], processedTokenOffsets: Set<ByteCount>) -> Set<String> {
guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled()) guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled())
.map(SourceKittenDictionary.init) else { .map(SourceKittenDictionary.init) else {
queuedPrintError("Could not get index") queuedPrintError("Could not get index")
@ -271,9 +271,9 @@ private extension SwiftLintFile {
let offset = lineOffset + column - 1 let offset = lineOffset + column - 1
// Filter already processed tokens such as static methods that are not operators // Filter already processed tokens such as static methods that are not operators
guard !processedTokenOffsets.contains(Int(offset)) else { continue } guard !processedTokenOffsets.contains(ByteCount(offset)) else { continue }
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: offset, arguments: arguments) let cursorInfoRequest = Request.cursorInfo(file: path!, offset: ByteCount(offset), arguments: arguments)
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()) guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled())
.map(SourceKittenDictionary.init) else { .map(SourceKittenDictionary.init) else {
queuedPrintError("Could not get cursor info") queuedPrintError("Could not get cursor info")

View File

@ -101,43 +101,43 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
public func validate(file: SwiftLintFile) -> [StyleViolation] { public func validate(file: SwiftLintFile) -> [StyleViolation] {
let setTokens = file.rangesAndTokens(matching: "\\bset\\b").keywordTokens() let setTokens = file.rangesAndTokens(matching: "\\bset\\b").keywordTokens()
let violatingLocations = setTokens.compactMap { setToken -> Int? in let violatingLocations = setTokens.compactMap { setToken -> ByteCount? in
// the last element is the deepest structure // the last element is the deepest structure
guard let dict = declarations(forByteOffset: setToken.offset, guard let dict = declarations(forByteOffset: setToken.offset,
structureDictionary: file.structureDictionary).last, structureDictionary: file.structureDictionary).last,
let bodyOffset = dict.bodyOffset, let bodyLength = dict.bodyLength, let bodyByteRange = dict.bodyByteRange,
case let contents = file.stringView, case let contents = file.stringView,
let propertyRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength), let propertyRange = contents.byteRangeToNSRange(bodyByteRange),
let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict) else { let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict)
return nil else {
return nil
} }
let argument = findNamedArgument(after: setToken, file: file) let argument = findNamedArgument(after: setToken, file: file)
let propertyEndOffset = bodyOffset + bodyLength let propertyEndOffset = bodyByteRange.upperBound
let setterByteRange: NSRange let setterByteRange: ByteRange
if setToken.offset > getToken.offset { // get {} set {} if setToken.offset > getToken.offset { // get {} set {}
let startOfBody: Int let startOfBody: ByteCount
if let argumentToken = argument?.token { if let argumentToken = argument?.token {
startOfBody = argumentToken.offset + argumentToken.length startOfBody = argumentToken.offset + argumentToken.length
} else { } else {
startOfBody = setToken.offset startOfBody = setToken.offset
} }
setterByteRange = NSRange(location: startOfBody, setterByteRange = ByteRange(location: startOfBody,
length: propertyEndOffset - startOfBody) length: propertyEndOffset - startOfBody)
} else { // set {} get {} } else { // set {} get {}
let startOfBody: Int let startOfBody: ByteCount
if let argumentToken = argument?.token { if let argumentToken = argument?.token {
startOfBody = argumentToken.offset + argumentToken.length startOfBody = argumentToken.offset + argumentToken.length
} else { } else {
startOfBody = setToken.offset startOfBody = setToken.offset
} }
setterByteRange = NSRange(location: startOfBody, setterByteRange = ByteRange(location: startOfBody,
length: getToken.offset - startOfBody) length: getToken.offset - startOfBody)
} }
guard let setterRange = contents.byteRangeToNSRange(start: setterByteRange.location, guard let setterRange = contents.byteRangeToNSRange(setterByteRange) else {
length: setterByteRange.length) else {
return nil return nil
} }
@ -188,7 +188,7 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
}) })
} }
private func declarations(forByteOffset byteOffset: Int, private func declarations(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]() var results = [SourceKittenDictionary]()
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter]) let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
@ -197,11 +197,10 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
// Only accepts declarations which contains a body and contains the // Only accepts declarations which contains a body and contains the
// searched byteOffset // searched byteOffset
guard let kind = dictionary.declarationKind, guard let kind = dictionary.declarationKind,
let bodyOffset = dictionary.bodyOffset, let byteRange = dictionary.bodyByteRange,
let bodyLength = dictionary.bodyLength, byteRange.contains(byteOffset)
case let byteRange = NSRange(location: bodyOffset, length: bodyLength), else {
NSLocationInRange(byteOffset, byteRange) else { return
return
} }
if parentKind != .protocol && allowedKinds.contains(kind) { if parentKind != .protocol && allowedKinds.contains(kind) {

View File

@ -74,7 +74,10 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
guard !isComputed else { return [] } guard !isComputed else { return [] }
guard let offset = dictionary.offset, guard let offset = dictionary.offset,
let range = file.stringView.byteRangeToNSRange(start: offset, length: 3) else { return [] } let range = file.stringView.byteRangeToNSRange(ByteRange(location: offset, length: 3))
else {
return []
}
return [range] return [range]
} }
@ -83,13 +86,14 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
return (violationRange, "weak var") return (violationRange, "weak var")
} }
private func protocolDeclarations(forByteOffset byteOffset: Int, private func protocolDeclarations(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
return structureDictionary.traverseBreadthFirst { dictionary in return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .protocol, guard dictionary.declarationKind == .protocol,
let byteRange = dictionary.byteRange, let byteRange = dictionary.byteRange,
NSLocationInRange(byteOffset, byteRange) else { byteRange.contains(byteOffset)
return nil else {
return nil
} }
return [dictionary] return [dictionary]
} }

View File

@ -61,11 +61,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule,
public func validate(file: SwiftLintFile, public func validate(file: SwiftLintFile,
kind: StatementKind, kind: StatementKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] { dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard observedStatements.contains(kind), guard observedStatements.contains(kind), let offset = dictionary.offset else {
let offset = dictionary.offset, return []
let length = dictionary.length
else {
return []
} }
let matches = file.lines.filter({ $0.byteRange.contains(offset) }).reduce(into: []) { matches, line in let matches = file.lines.filter({ $0.byteRange.contains(offset) }).reduce(into: []) { matches, line in
@ -75,15 +72,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule,
} }
return matches.map { _ -> StyleViolation in return matches.map { _ -> StyleViolation in
let characterOffset = startOffset(of: offset, with: length, in: file)
let location = Location(file: file, characterOffset: characterOffset)
return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity,
location: location) location: Location(file: file, byteOffset: offset))
} }
} }
private func startOffset(of offset: Int, with length: Int, in file: SwiftLintFile) -> Int {
let range = file.stringView.byteRangeToNSRange(start: offset, length: length)
return range?.location ?? offset
}
} }

View File

@ -89,10 +89,9 @@ public struct CyclomaticComplexityRule: ASTRule, ConfigurationProviderRule {
private func reduceSwitchComplexity(initialComplexity complexity: Int, file: SwiftLintFile, private func reduceSwitchComplexity(initialComplexity complexity: Int, file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> Int { dictionary: SourceKittenDictionary) -> Int {
let bodyOffset = dictionary.bodyOffset ?? 0 let bodyRange = dictionary.bodyByteRange ?? ByteRange(location: 0, length: 0)
let bodyLength = dictionary.bodyLength ?? 0
let contents = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) ?? "" let contents = file.stringView.substringWithByteRange(bodyRange) ?? ""
let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1 let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1
return complexity - fallthroughCount return complexity - fallthroughCount

View File

@ -40,10 +40,8 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
return [] return []
} }
let nameOffset = dictionary.nameOffset ?? 0 let nameRange = ByteRange(location: dictionary.nameOffset ?? 0, length: dictionary.nameLength ?? 0)
let length = dictionary.nameLength ?? 0 if functionIsInitializer(file: file, byteRange: nameRange) {
if functionIsInitializer(file: file, byteOffset: nameOffset, byteLength: length) {
return [] return []
} }
@ -53,8 +51,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <) let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <)
let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, offset: nameOffset, let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, range: nameRange)
length: length)
if allParameterCount < minThreshold! { if allParameterCount < minThreshold! {
return [] return []
} }
@ -62,7 +59,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
var parameterCount = allParameterCount var parameterCount = allParameterCount
if configuration.ignoresDefaultParameters { if configuration.ignoresDefaultParameters {
parameterCount -= defaultFunctionParameterCount(file: file, byteOffset: nameOffset, byteLength: length) parameterCount -= defaultFunctionParameterCount(file: file, byteRange: nameRange)
} }
for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value { for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value {
@ -78,16 +75,14 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
return [] return []
} }
private func allFunctionParameterCount(structure: [SourceKittenDictionary], private func allFunctionParameterCount(structure: [SourceKittenDictionary], range: ByteRange) -> Int {
offset: Int, length: Int) -> Int {
var parameterCount = 0 var parameterCount = 0
for subDict in structure { for subDict in structure {
guard subDict.kind != nil, guard subDict.kind != nil, let parameterOffset = subDict.offset else {
let parameterOffset = subDict.offset else { continue
continue
} }
guard offset..<(offset + length) ~= parameterOffset else { guard range.contains(parameterOffset) else {
return parameterCount return parameterCount
} }
@ -98,15 +93,15 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
return parameterCount return parameterCount
} }
private func defaultFunctionParameterCount(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Int { private func defaultFunctionParameterCount(file: SwiftLintFile, byteRange: ByteRange) -> Int {
let substring = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength)! let substring = file.stringView.substringWithByteRange(byteRange)!
let equals = substring.filter { $0 == "=" } let equals = substring.filter { $0 == "=" }
return equals.count return equals.count
} }
private func functionIsInitializer(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Bool { private func functionIsInitializer(file: SwiftLintFile, byteRange: ByteRange) -> Bool {
guard let name = file.stringView guard let name = file.stringView
.substringWithByteRange(start: byteOffset, length: byteLength), .substringWithByteRange(byteRange),
name.hasPrefix("init"), name.hasPrefix("init"),
let funcName = name.components(separatedBy: CharacterSet(charactersIn: "<(")).first else { let funcName = name.components(separatedBy: CharacterSet(charactersIn: "<(")).first else {
return false return false

View File

@ -73,7 +73,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
} }
private func violationOffsetsForTypes(in file: SwiftLintFile, dictionary: SourceKittenDictionary, private func violationOffsetsForTypes(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] { kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] {
let kinds = SwiftDeclarationKind.variableKinds.subtracting([.varLocal]) let kinds = SwiftDeclarationKind.variableKinds.subtracting([.varLocal])
guard kinds.contains(kind), guard kinds.contains(kind),
let type = dictionary.typeName, let type = dictionary.typeName,
@ -86,12 +86,11 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
} }
private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary, private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] { kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] {
let contents = file.stringView let contents = file.stringView
guard SwiftDeclarationKind.functionKinds.contains(kind), guard SwiftDeclarationKind.functionKinds.contains(kind),
let returnRange = returnRangeForFunction(dictionary: dictionary), let returnRange = returnRangeForFunction(dictionary: dictionary),
let returnSubstring = contents.substringWithByteRange(start: returnRange.location, let returnSubstring = contents.substringWithByteRange(returnRange) else {
length: returnRange.length) else {
return [] return []
} }
@ -99,13 +98,13 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
return offsets.sorted { $0.offset < $1.offset } return offsets.sorted { $0.offset < $1.offset }
} }
private func violationOffsets(for text: String, initialOffset: Int = 0) -> [(offset: Int, size: Int)] { private func violationOffsets(for text: String, initialOffset: ByteCount = 0) -> [(offset: ByteCount, size: Int)] {
guard let ranges = try? parenthesesRanges(in: text) else { guard let ranges = try? parenthesesRanges(in: text) else {
return [] return []
} }
var text = text.bridge() var text = text.bridge()
var offsets = [(offset: Int, size: Int)]() var offsets = [(offset: ByteCount, size: Int)]()
for (range, kind) in ranges { for (range, kind) in ranges {
let substring = text.substring(with: range) let substring = text.substring(with: range)
@ -124,7 +123,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
return offsets return offsets
} }
private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> NSRange? { private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> ByteRange? {
guard let nameOffset = dictionary.nameOffset, guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let length = dictionary.length, let length = dictionary.length,
@ -139,7 +138,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
return nil return nil
} }
return NSRange(location: start, length: end - start) return ByteRange(location: start, length: end - start)
} }
private func parenthesesRanges(in text: String) throws -> [(NSRange, RangeKind)] { private func parenthesesRanges(in text: String) throws -> [(NSRange, RangeKind)] {

View File

@ -47,11 +47,11 @@ public struct FirstWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule
return true // has a substructure, like a closure return true // has a substructure, like a closure
} }
guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else { guard let bodyRange = dictionary.bodyByteRange else {
return true return true
} }
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength)) let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
return !syntaxKinds.contains(.string) return !syntaxKinds.contains(.string)
} }
} }

View File

@ -40,11 +40,11 @@ public struct LastWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule,
return true // has a substructure, like a closure return true // has a substructure, like a closure
} }
guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else { guard let bodyRange = dictionary.bodyByteRange else {
return true return true
} }
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength)) let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
return !syntaxKinds.contains(.string) return !syntaxKinds.contains(.string)
} }
} }

View File

@ -103,7 +103,8 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut
kind == .call, kind == .call,
let nameOffset = dictionary.nameOffset, let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let nameRange = file.stringView.byteRangeToNSRange(start: nameOffset, length: nameLength), case let nameByteRange = ByteRange(location: nameOffset, length: nameLength),
let nameRange = file.stringView.byteRangeToNSRange(nameByteRange),
let match = reduceExpression.firstMatch(in: file.contents, options: [], range: nameRange), let match = reduceExpression.firstMatch(in: file.contents, options: [], range: nameRange),
dictionary.enclosedArguments.count == 2, dictionary.enclosedArguments.count == 2,
// would otherwise equal "into" // would otherwise equal "into"
@ -132,13 +133,11 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut
} }
let contents = file.stringView let contents = file.stringView
guard let offset = argument.offset, guard let byteRange = argument.byteRange,
let length = argument.length, let range = contents.byteRangeToNSRange(byteRange)
let range = contents.byteRangeToNSRange(start: offset, length: length)
else { return false } else { return false }
// Check for string literal // Check for string literal
let byteRange = NSRange(location: offset, length: length)
let kinds = file.syntaxMap.kinds(inByteRange: byteRange) let kinds = file.syntaxMap.kinds(inByteRange: byteRange)
if kinds == [.string] { if kinds == [.string] {
return true return true

View File

@ -138,7 +138,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
} }
private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)], private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)],
attributesTokens: [(String, NSRange)], attributesTokens: [(String, ByteRange)],
line: Line, file: SwiftLintFile) -> Set<String> { line: Line, file: SwiftLintFile) -> Set<String> {
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map { let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
let hasParameter = attributeContainsParameter(attributeRange: $1, let hasParameter = attributeContainsParameter(attributeRange: $1,
@ -241,16 +241,17 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
return allTokens return allTokens
} }
private func attributeContainsParameter(attributeRange: NSRange, private func attributeContainsParameter(attributeRange: ByteRange,
line: Line, file: SwiftLintFile) -> Bool { line: Line, file: SwiftLintFile) -> Bool {
let restOfLineOffset = attributeRange.location + attributeRange.length let restOfLineOffset = attributeRange.upperBound
let restOfLineLength = line.byteRange.location + line.byteRange.length - restOfLineOffset let restOfLineLength = line.byteRange.upperBound - restOfLineOffset
let regex = AttributesRule.regularExpression let regex = AttributesRule.regularExpression
let contents = file.stringView let contents = file.stringView
// check if after the token is a `(` with only spaces allowed between the token and `(` // check if after the token is a `(` with only spaces allowed between the token and `(`
guard let restOfLine = contents.substringWithByteRange(start: restOfLineOffset, length: restOfLineLength), let restOfLineByteRange = ByteRange(location: restOfLineOffset, length: restOfLineLength)
guard let restOfLine = contents.substringWithByteRange(restOfLineByteRange),
case let range = restOfLine.fullNSRange, case let range = restOfLine.fullNSRange,
regex.firstMatch(in: restOfLine, options: [], range: range) != nil else { regex.firstMatch(in: restOfLine, options: [], range: range) != nil else {
return false return false
@ -259,7 +260,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
return true return true
} }
private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, NSRange)? { private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, ByteRange)? {
guard token.kind == .attributeBuiltin else { guard token.kind == .attributeBuiltin else {
return nil return nil
} }

View File

@ -38,8 +38,12 @@ public struct ClosureEndIndentationRule: Rule, OptInRule, ConfigurationProviderR
extension ClosureEndIndentationRule: CorrectableRule { extension ClosureEndIndentationRule: CorrectableRule {
public func correct(file: SwiftLintFile) -> [Correction] { public func correct(file: SwiftLintFile) -> [Correction] {
let allViolations = violations(in: file).reversed().filter { let allViolations = violations(in: file).reversed().filter { violation in
!file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else {
return false
}
return !file.ruleEnabled(violatingRanges: [nsRange], for: self).isEmpty
} }
guard !allViolations.isEmpty else { guard !allViolations.isEmpty else {
@ -107,8 +111,8 @@ extension ClosureEndIndentationRule: CorrectableRule {
extension ClosureEndIndentationRule { extension ClosureEndIndentationRule {
fileprivate struct Violation { fileprivate struct Violation {
var indentationRanges: (expected: NSRange, actual: NSRange) var indentationRanges: (expected: NSRange, actual: NSRange)
var endOffset: Int var endOffset: ByteCount
var range: NSRange var range: ByteRange
} }
fileprivate func violations(in file: SwiftLintFile) -> [Violation] { fileprivate func violations(in file: SwiftLintFile) -> [Violation] {
@ -136,11 +140,10 @@ extension ClosureEndIndentationRule {
private func hasTrailingClosure(in file: SwiftLintFile, private func hasTrailingClosure(in file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> Bool { dictionary: SourceKittenDictionary) -> Bool {
guard guard
let offset = dictionary.offset, let byteRange = dictionary.byteRange,
let length = dictionary.length, let text = file.stringView.substringWithByteRange(byteRange)
let text = file.stringView.substringWithByteRange(start: offset, length: length) else {
else { return false
return false
} }
return !text.hasSuffix(")") return !text.hasSuffix(")")
@ -156,15 +159,17 @@ extension ClosureEndIndentationRule {
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
bodyLength > 0, bodyLength > 0,
case let endOffset = offset + length - 1, case let endOffset = offset + length - 1,
contents.substringWithByteRange(start: endOffset, length: 1) == "}", case let closingBraceByteRange = ByteRange(location: endOffset, length: 1),
contents.substringWithByteRange(closingBraceByteRange) == "}",
let startOffset = startOffset(forDictionary: dictionary, file: file), let startOffset = startOffset(forDictionary: dictionary, file: file),
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset), let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
case let nameEndPosition = nameOffset + nameLength, case let nameEndPosition = nameOffset + nameLength,
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
startLine != endLine, bodyOffsetLine != endLine, startLine != endLine, bodyOffsetLine != endLine,
!containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { !containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file)
return nil else {
return nil
} }
let range = file.lines[startLine - 1].range let range = file.lines[startLine - 1].range
@ -172,8 +177,9 @@ extension ClosureEndIndentationRule {
let actual = endPosition - 1 let actual = endPosition - 1
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
case let expected = match.location - range.location, case let expected = match.location - range.location,
expected != actual else { expected != actual
return nil else {
return nil
} }
var expectedRange = range var expectedRange = range
@ -184,7 +190,7 @@ extension ClosureEndIndentationRule {
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange), return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
endOffset: endOffset, endOffset: endOffset,
range: NSRange(location: offset, length: length)) range: ByteRange(location: offset, length: length))
} }
private func validateArguments(in file: SwiftLintFile, private func validateArguments(in file: SwiftLintFile,
@ -216,15 +222,17 @@ extension ClosureEndIndentationRule {
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
bodyLength > 0, bodyLength > 0,
case let endOffset = offset + length - 1, case let endOffset = offset + length - 1,
contents.substringWithByteRange(start: endOffset, length: 1) == "}", case let closingBraceByteRange = ByteRange(location: endOffset, length: 1),
contents.substringWithByteRange(closingBraceByteRange) == "}",
let startOffset = dictionary.offset, let startOffset = dictionary.offset,
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset), let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
case let nameEndPosition = nameOffset + nameLength, case let nameEndPosition = nameOffset + nameLength,
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
startLine != endLine, bodyOffsetLine != endLine, startLine != endLine, bodyOffsetLine != endLine,
!isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { !isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file)
return nil else {
return nil
} }
let range = file.lines[startLine - 1].range let range = file.lines[startLine - 1].range
@ -232,8 +240,9 @@ extension ClosureEndIndentationRule {
let actual = endPosition - 1 let actual = endPosition - 1
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
case let expected = match.location - range.location, case let expected = match.location - range.location,
expected != actual else { expected != actual
return nil else {
return nil
} }
var expectedRange = range var expectedRange = range
@ -244,30 +253,28 @@ extension ClosureEndIndentationRule {
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange), return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
endOffset: endOffset, endOffset: endOffset,
range: NSRange(location: offset, length: length)) range: ByteRange(location: offset, length: length))
} }
private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Int? { private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> ByteCount? {
guard let nameOffset = dictionary.nameOffset, guard let nameByteRange = dictionary.nameByteRange else {
let nameLength = dictionary.nameLength else {
return nil return nil
} }
let newLineRegex = regex("\n(\\s*\\}?\\.)") let newLineRegex = regex("\n(\\s*\\}?\\.)")
let contents = file.stringView let contents = file.stringView
guard let range = contents.byteRangeToNSRange(start: nameOffset, length: nameLength), guard let range = contents.byteRangeToNSRange(nameByteRange),
let match = newLineRegex.matches(in: file.contents, options: [], let match = newLineRegex.matches(in: file.contents, options: [], range: range).last?.range(at: 1),
range: range).last?.range(at: 1), let methodByteRange = contents.NSRangeToByteRange(start: match.location, length: match.length)
let methodByteRange = contents.NSRangeToByteRange(start: match.location, else {
length: match.length) else { return nameByteRange.location
return nameOffset
} }
return methodByteRange.location return methodByteRange.location
} }
private func isSingleLineClosure(dictionary: SourceKittenDictionary, private func isSingleLineClosure(dictionary: SourceKittenDictionary,
endPosition: Int, file: SwiftLintFile) -> Bool { endPosition: ByteCount, file: SwiftLintFile) -> Bool {
let contents = file.stringView let contents = file.stringView
guard let start = dictionary.bodyOffset, guard let start = dictionary.bodyOffset,
@ -280,7 +287,7 @@ extension ClosureEndIndentationRule {
} }
private func containsSingleLineClosure(dictionary: SourceKittenDictionary, private func containsSingleLineClosure(dictionary: SourceKittenDictionary,
endPosition: Int, file: SwiftLintFile) -> Bool { endPosition: ByteCount, file: SwiftLintFile) -> Bool {
let contents = file.stringView let contents = file.stringView
guard let closure = trailingClosure(dictionary: dictionary, file: file), guard let closure = trailingClosure(dictionary: dictionary, file: file),
@ -309,12 +316,12 @@ extension ClosureEndIndentationRule {
private func filterClosureArguments(_ arguments: [SourceKittenDictionary], private func filterClosureArguments(_ arguments: [SourceKittenDictionary],
file: SwiftLintFile) -> [SourceKittenDictionary] { file: SwiftLintFile) -> [SourceKittenDictionary] {
return arguments.filter { argument in return arguments.filter { argument in
guard let offset = argument.bodyOffset, guard let bodyByteRange = argument.bodyByteRange,
let length = argument.bodyLength, let range = file.stringView.byteRangeToNSRange(bodyByteRange),
let range = file.stringView.byteRangeToNSRange(start: offset, length: length),
let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range, let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range,
match.location == range.location else { match.location == range.location
return false else {
return false
} }
return true return true
@ -330,10 +337,12 @@ extension ClosureEndIndentationRule {
let firstArgumentOffset = firstArgument.offset, let firstArgumentOffset = firstArgument.offset,
case let offset = nameOffset + nameLength, case let offset = nameOffset + nameLength,
case let length = firstArgumentOffset - offset, case let length = firstArgumentOffset - offset,
let range = file.stringView.byteRangeToNSRange(start: offset, length: length), case let byteRange = ByteRange(location: offset, length: length),
let range = file.stringView.byteRangeToNSRange(byteRange),
let match = regex("\\(\\s*\\n\\s*").firstMatch(in: file.contents, options: [], range: range)?.range, let match = regex("\\(\\s*\\n\\s*").firstMatch(in: file.contents, options: [], range: range)?.range,
match.location == range.location else { match.location == range.location
return false else {
return false
} }
return true return true

View File

@ -52,8 +52,9 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
guard let nameOffset = dictionary.nameOffset, guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, let nameLength = dictionary.nameLength,
let bodyLength = dictionary.bodyLength, let bodyLength = dictionary.bodyLength,
bodyLength > 0 else { bodyLength > 0
return [] else {
return []
} }
let parameters = dictionary.enclosedVarParameters let parameters = dictionary.enclosedVarParameters
@ -70,14 +71,16 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
let rangeLength = paramOffset - rangeStart let rangeLength = paramOffset - rangeStart
let contents = file.stringView let contents = file.stringView
guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength), let byteRange = ByteRange(location: rangeStart, length: rangeLength)
guard let range = contents.byteRangeToNSRange(byteRange),
let match = regex.matches(in: file.contents, options: [], range: range).last?.range, let match = regex.matches(in: file.contents, options: [], range: range).last?.range,
match.location != NSNotFound, match.location != NSNotFound,
let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location, let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location,
let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset), let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset),
let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset), let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset),
braceLine != paramLine else { braceLine != paramLine
return nil else {
return nil
} }
return StyleViolation(ruleDescription: type(of: self).description, return StyleViolation(ruleDescription: type(of: self).description,

View File

@ -90,7 +90,7 @@ public struct ClosureSpacingRule: CorrectableRule, ConfigurationProviderRule, Op
return kindsToExclude.contains(tokenKind) return kindsToExclude.contains(tokenKind)
} }
let tokenRanges = tokens.compactMap { let tokenRanges = tokens.compactMap {
file.stringView.byteRangeToNSRange(start: $0.offset, length: $0.length) file.stringView.byteRangeToNSRange($0.range)
} }
linesWithBraces.append(braces.filter({ !$0.intersects(tokenRanges) })) linesWithBraces.append(braces.filter({ !$0.intersects(tokenRanges) }))
} }

View File

@ -86,12 +86,13 @@ public struct CollectionAlignmentRule: ASTRule, ConfigurationProviderRule, OptIn
} }
} }
private func colonLocation(with file: SwiftLintFile, keyOffset: Int, keyLength: Int, private func colonLocation(with file: SwiftLintFile, keyOffset: ByteCount, keyLength: ByteCount,
valueOffset: Int) -> Location? { valueOffset: ByteCount) -> Location? {
let contents = file.stringView let contents = file.stringView
let matchStart = keyOffset + keyLength let matchStart = keyOffset + keyLength
let matchLength = valueOffset - matchStart let matchLength = valueOffset - matchStart
let range = contents.byteRangeToNSRange(start: matchStart, length: matchLength) let byteRange = ByteRange(location: matchStart, length: matchLength)
let range = contents.byteRangeToNSRange(byteRange)
let matches = file.match(pattern: ":", excludingSyntaxKinds: [.comment], range: range) let matches = file.match(pattern: ":", excludingSyntaxKinds: [.comment], range: range)
return matches.first.map { Location(file: file, characterOffset: $0.location) } return matches.first.map { Location(file: file, characterOffset: $0.location) }

View File

@ -3,12 +3,12 @@ import SourceKittenFramework
extension ColonRule { extension ColonRule {
internal func dictionaryColonViolationRanges(in file: SwiftLintFile, internal func dictionaryColonViolationRanges(in file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> [NSRange] { dictionary: SourceKittenDictionary) -> [ByteRange] {
guard configuration.applyToDictionaries else { guard configuration.applyToDictionaries else {
return [] return []
} }
let ranges: [NSRange] = dictionary.traverseDepthFirst { subDict in let ranges: [ByteRange] = dictionary.traverseDepthFirst { subDict in
guard let kind = subDict.expressionKind else { return nil } guard let kind = subDict.expressionKind else { return nil }
return dictionaryColonViolationRanges(in: file, kind: kind, dictionary: subDict) return dictionaryColonViolationRanges(in: file, kind: kind, dictionary: subDict)
} }
@ -17,7 +17,7 @@ extension ColonRule {
} }
internal func dictionaryColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind, internal func dictionaryColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
dictionary: SourceKittenDictionary) -> [NSRange] { dictionary: SourceKittenDictionary) -> [ByteRange] {
guard kind == .dictionary, guard kind == .dictionary,
let ranges = dictionaryColonRanges(dictionary: dictionary) else { let ranges = dictionaryColonRanges(dictionary: dictionary) else {
return [] return []
@ -25,7 +25,7 @@ extension ColonRule {
let contents = file.stringView let contents = file.stringView
return ranges.filter { return ranges.filter {
guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else { guard let colon = contents.substringWithByteRange($0) else {
return false return false
} }
@ -38,31 +38,28 @@ extension ColonRule {
} }
} }
private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? { private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? {
let elements = dictionary.elements let elements = dictionary.elements
guard elements.count % 2 == 0 else { guard elements.count % 2 == 0 else {
return nil return nil
} }
let expectedKind = "source.lang.swift.structure.elem.expr" let expectedKind = "source.lang.swift.structure.elem.expr"
let ranges: [NSRange] = elements.compactMap { subDict in let ranges: [ByteRange] = elements.compactMap { subDict in
guard subDict.kind == expectedKind, guard subDict.kind == expectedKind else {
let offset = subDict.offset, return nil
let length = subDict.length else {
return nil
} }
return NSRange(location: offset, length: length) return subDict.byteRange
} }
let even = ranges.enumerated().compactMap { $0 % 2 == 0 ? $1 : nil } let even = ranges.enumerated().compactMap { $0 % 2 == 0 ? $1 : nil }
let odd = ranges.enumerated().compactMap { $0 % 2 != 0 ? $1 : nil } let odd = ranges.enumerated().compactMap { $0 % 2 != 0 ? $1 : nil }
return zip(even, odd).map { evenRange, oddRange -> NSRange in return zip(even, odd).map { evenRange, oddRange -> ByteRange in
let location = NSMaxRange(evenRange) let location = evenRange.upperBound
let length = oddRange.location - location let length = oddRange.location - location
return ByteRange(location: location, length: length)
return NSRange(location: location, length: length)
} }
} }
} }

View File

@ -3,7 +3,7 @@ import SourceKittenFramework
extension ColonRule { extension ColonRule {
internal func functionCallColonViolationRanges(in file: SwiftLintFile, internal func functionCallColonViolationRanges(in file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> [NSRange] { dictionary: SourceKittenDictionary) -> [ByteRange] {
return dictionary.traverseDepthFirst { subDict in return dictionary.traverseDepthFirst { subDict in
guard let kind = subDict.expressionKind else { return nil } guard let kind = subDict.expressionKind else { return nil }
return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict) return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict)
@ -11,15 +11,16 @@ extension ColonRule {
} }
internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind, internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
dictionary: SourceKittenDictionary) -> [NSRange] { dictionary: SourceKittenDictionary) -> [ByteRange] {
guard kind == .argument, guard kind == .argument,
let ranges = functionCallColonRanges(dictionary: dictionary) else { let ranges = functionCallColonRanges(dictionary: dictionary)
return [] else {
return []
} }
let contents = file.stringView let contents = file.stringView
return ranges.filter { return ranges.filter {
guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else { guard let colon = contents.substringWithByteRange($0) else {
return false return false
} }
@ -32,15 +33,16 @@ extension ColonRule {
} }
} }
private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? { private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? {
guard let nameOffset = dictionary.nameOffset, guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength, nameLength > 0, let nameLength = dictionary.nameLength, nameLength > 0,
let bodyOffset = dictionary.bodyOffset, let bodyOffset = dictionary.bodyOffset,
case let location = nameOffset + nameLength, case let location = nameOffset + nameLength,
bodyOffset > location else { bodyOffset > location
return nil else {
return nil
} }
return [NSRange(location: location, length: bodyOffset - location)] return [ByteRange(location: location, length: bodyOffset - location)]
} }
} }

View File

@ -32,8 +32,8 @@ internal extension ColonRule {
return isValidMatch(syntaxTokens: syntaxTokens, file: file) return isValidMatch(syntaxTokens: syntaxTokens, file: file)
}.compactMap { match, syntaxTokens in }.compactMap { match, syntaxTokens in
let identifierRange = contents let firstSyntaxTokenByteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0) let identifierRange = contents.byteRangeToNSRange(firstSyntaxTokenByteRange)
return identifierRange.map { NSUnionRange($0, match.range) } return identifierRange.map { NSUnionRange($0, match.range) }
} }
} }
@ -53,7 +53,7 @@ internal extension ColonRule {
case (.identifier, .keyword), case (.identifier, .keyword),
(.typeidentifier, .keyword): (.typeidentifier, .keyword):
validKinds = file.isTypeLike(token: syntaxTokens[1]) validKinds = file.isTypeLike(token: syntaxTokens[1])
//Exclude explicit "Self" type because of static variables // Exclude explicit "Self" type because of static variables
if syntaxKinds[0] == .identifier, if syntaxKinds[0] == .identifier,
file.contents(for: syntaxTokens[1]) == "Self" { file.contents(for: syntaxTokens[1]) == "Self" {
validKinds = false validKinds = false

View File

@ -79,17 +79,11 @@ public struct ColonRule: CorrectableRule, ConfigurationProviderRule {
let contents = file.stringView let contents = file.stringView
let dictViolations: [RangeWithKind] = dictionaryColonViolationRanges(in: file, let dictViolations: [RangeWithKind] = dictionaryColonViolationRanges(in: file,
dictionary: dictionary).compactMap { dictionary: dictionary).compactMap {
guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else { return contents.byteRangeToNSRange($0).map { (range: $0, kind: .dictionary) }
return nil
}
return (range: range, kind: .dictionary)
} }
let functionViolations: [RangeWithKind] = functionCallColonViolationRanges(in: file, let functionViolations: [RangeWithKind] = functionCallColonViolationRanges(in: file,
dictionary: dictionary).compactMap { dictionary: dictionary).compactMap {
guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else { return contents.byteRangeToNSRange($0).map { (range: $0, kind: .functionCall) }
return nil
}
return (range: range, kind: .functionCall)
} }
return (violations + dictViolations + functionViolations).sorted { return (violations + dictViolations + functionViolations).sorted {

View File

@ -77,10 +77,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
let callsRanges = dictionary.substructure.compactMap { dict -> NSRange? in let callsRanges = dictionary.substructure.compactMap { dict -> NSRange? in
guard dict.expressionKind == .call, guard dict.expressionKind == .call,
let offset = dict.offset, let byteRange = dict.byteRange,
let length = dict.length, let range = contents.byteRangeToNSRange(byteRange)
let range = contents.byteRangeToNSRange(start: offset, length: length) else { else {
return nil return nil
} }
return range return range
@ -88,10 +88,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
return dictionary.elements.flatMap { subDictionary -> [NSRange] in return dictionary.elements.flatMap { subDictionary -> [NSRange] in
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
let offset = subDictionary.offset, let byteRange = subDictionary.byteRange,
let length = subDictionary.length, let caseRange = contents.byteRangeToNSRange(byteRange)
let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else { else {
return [] return []
} }
let emptyArgumentRegex = regex("\\.\\S+\\s*(\\([,\\s_]*\\))") let emptyArgumentRegex = regex("\\.\\S+\\s*(\\([,\\s_]*\\))")
@ -107,8 +107,8 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
// avoid matches in "(_, _) where" // avoid matches in "(_, _) where"
if let whereByteRange = contents.NSRangeToByteRange(start: whereRange.location, if let whereByteRange = contents.NSRangeToByteRange(start: whereRange.location,
length: whereRange.length), length: whereRange.length),
case let length = whereByteRange.location - offset, case let length = whereByteRange.location - byteRange.location,
case let byteRange = NSRange(location: offset, length: length), case let byteRange = ByteRange(location: byteRange.location, length: length),
Set(file.syntaxMap.kinds(inByteRange: byteRange)) == [.keyword] { Set(file.syntaxMap.kinds(inByteRange: byteRange)) == [.keyword] {
return nil return nil
} }

View File

@ -82,12 +82,14 @@ public struct EmptyParenthesesWithTrailingClosureRule: SubstitutionCorrectableAS
let rangeStart = nameOffset + nameLength let rangeStart = nameOffset + nameLength
let rangeLength = (offset + length) - (nameOffset + nameLength) let rangeLength = (offset + length) - (nameOffset + nameLength)
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex
guard let range = file.stringView.byteRangeToNSRange(start: rangeStart, length: rangeLength), guard let range = file.stringView.byteRangeToNSRange(byteRange),
let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
match.location == range.location else { match.location == range.location
return [] else {
return []
} }
return [match] return [match]

View File

@ -140,12 +140,12 @@ public struct ExplicitSelfRule: CorrectableRule, ConfigurationProviderRule, Anal
let contents = file.stringView let contents = file.stringView
return cursorsMissingExplicitSelf.compactMap { cursorInfo in return cursorsMissingExplicitSelf.compactMap { cursorInfo in
guard let byteOffset = cursorInfo["swiftlint.offset"] as? Int64 else { guard let byteOffset = (cursorInfo["swiftlint.offset"] as? Int64).flatMap(ByteCount.init) else {
queuedPrintError("couldn't convert offsets") queuedPrintError("couldn't convert offsets")
return nil return nil
} }
return contents.byteRangeToNSRange(start: Int(byteOffset), length: 0) return contents.byteRangeToNSRange(ByteRange(location: byteOffset, length: 0))
} }
} }
} }
@ -156,29 +156,29 @@ private let kindsToFind: Set = [
] ]
private extension SwiftLintFile { private extension SwiftLintFile {
func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [Int]) throws func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [ByteCount]) throws
-> [[String: SourceKitRepresentable]] { -> [[String: SourceKitRepresentable]] {
return try byteOffsets.compactMap { offset in return try byteOffsets.compactMap { offset in
if stringView.substringWithByteRange(start: offset - 1, length: 1)! == "." { return nil } if stringView.substringWithByteRange(ByteRange(location: offset - 1, length: 1))! == "." { return nil }
var cursorInfo = try Request.cursorInfo(file: self.path!, offset: Int64(offset), var cursorInfo = try Request.cursorInfo(file: self.path!, offset: offset,
arguments: compilerArguments).sendIfNotDisabled() arguments: compilerArguments).sendIfNotDisabled()
cursorInfo["swiftlint.offset"] = Int64(offset) cursorInfo["swiftlint.offset"] = Int64(offset.value)
return cursorInfo return cursorInfo
} }
} }
} }
private extension StringView { private extension StringView {
func byteOffset(forLine line: Int, column: Int) -> Int { func byteOffset(forLine line: Int, column: Int) -> ByteCount {
var byteOffset = 0 var byteOffset = ByteCount(0)
for line in lines[..<(line - 1)] { for line in lines[..<(line - 1)] {
byteOffset += line.byteRange.length byteOffset += line.byteRange.length
} }
return byteOffset + column - 1 return byteOffset + ByteCount(column - 1)
} }
func recursiveByteOffsets(_ dict: [String: Any]) -> [Int] { func recursiveByteOffsets(_ dict: [String: Any]) -> [ByteCount] {
let cur: [Int] let cur: [ByteCount]
if let line = dict["key.line"] as? Int64, if let line = dict["key.line"] as? Int64,
let column = dict["key.column"] as? Int64, let column = dict["key.column"] as? Int64,
let kindString = dict["key.kind"] as? String, let kindString = dict["key.kind"] as? String,
@ -194,7 +194,7 @@ private extension StringView {
} }
} }
private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [Int] { private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [ByteCount] {
let absoluteFile = file.path!.bridge().absolutePathRepresentation() let absoluteFile = file.path!.bridge().absolutePathRepresentation()
let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled() let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled()
let binaryOffsets = file.stringView.recursiveByteOffsets(index) let binaryOffsets = file.stringView.recursiveByteOffsets(index)

View File

@ -63,7 +63,8 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule {
if let firstToken = firstToken, let lastToken = lastToken { if let firstToken = firstToken, let lastToken = lastToken {
let start = firstToken.offset let start = firstToken.offset
let length = lastToken.offset + lastToken.length - firstToken.offset let length = lastToken.offset + lastToken.length - firstToken.offset
guard let range = file.stringView.byteRangeToNSRange(start: start, length: length) else { let byteRange = ByteRange(location: start, length: length)
guard let range = file.stringView.byteRangeToNSRange(byteRange) else {
return [] return []
} }
@ -75,36 +76,32 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule {
if let regex = requiredRegex, if let regex = requiredRegex,
case let matches = regex.matches(in: file.contents, options: [], range: range), case let matches = regex.matches(in: file.contents, options: [], range: range),
matches.isEmpty { matches.isEmpty {
violationsOffsets.append(start) violationsOffsets.append(file.stringView.location(fromByteOffset: start))
} }
} else if requiredRegex != nil { } else if requiredRegex != nil {
let location = firstNonCommentToken.map { let location = firstNonCommentToken.map {
Location(file: file, byteOffset: $0.offset) Location(file: file, byteOffset: $0.offset)
} ?? Location(file: file.path, line: 1) } ?? Location(file: file.path, line: 1)
return [ return [makeViolation(at: location)]
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severityConfiguration.severity,
location: location,
reason: type(of: self).reason)
]
} }
return violationsOffsets.map { return violationsOffsets.map { makeViolation(at: Location(file: file, characterOffset: $0)) }
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severityConfiguration.severity,
location: Location(file: file, characterOffset: $0),
reason: type(of: self).reason)
}
} }
private func isSwiftLintCommand(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool { private func isSwiftLintCommand(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool {
guard let range = file.stringView.byteRangeToNSRange(start: token.offset, guard let range = file.stringView.byteRangeToNSRange(token.range) else {
length: token.length) else { return false
return false
} }
return !file.commands(in: range).isEmpty return !file.commands(in: range).isEmpty
} }
private func makeViolation(at location: Location) -> StyleViolation {
return StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severityConfiguration.severity,
location: location,
reason: type(of: self).reason)
}
} }
private extension SyntaxKind { private extension SyntaxKind {

View File

@ -2,7 +2,7 @@ import Foundation
import SourceKittenFramework import SourceKittenFramework
public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule { public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
private typealias FileTypeOffset = (fileType: FileType, offset: Int) private typealias FileTypeOffset = (fileType: FileType, offset: ByteCount)
public var configuration = FileTypesOrderConfiguration() public var configuration = FileTypesOrderConfiguration()

View File

@ -80,7 +80,7 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule {
} }
private func validateName(dictionary: SourceKittenDictionary, private func validateName(dictionary: SourceKittenDictionary,
kind: SwiftDeclarationKind) -> (name: String, offset: Int)? { kind: SwiftDeclarationKind) -> (name: String, offset: ByteCount)? {
guard var name = dictionary.name, guard var name = dictionary.name,
let offset = dictionary.offset, let offset = dictionary.offset,
kinds.contains(kind), kinds.contains(kind),

View File

@ -189,7 +189,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
return token return token
} }
let violatingLocations = getTokens.compactMap { token -> (Int, SwiftDeclarationKind?)? in let violatingLocations = getTokens.compactMap { token -> (ByteCount, SwiftDeclarationKind?)? in
// the last element is the deepest structure // the last element is the deepest structure
guard let dict = declarations(forByteOffset: token.offset, guard let dict = declarations(forByteOffset: token.offset,
structureDictionary: file.structureDictionary).last else { structureDictionary: file.structureDictionary).last else {
@ -220,7 +220,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
} }
private extension ImplicitGetterRule { private extension ImplicitGetterRule {
func declarations(forByteOffset byteOffset: Int, func declarations(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]() var results = [SourceKittenDictionary]()
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter]) let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
@ -230,11 +230,10 @@ private extension ImplicitGetterRule {
// Only accepts declarations which contains a body and contains the // Only accepts declarations which contains a body and contains the
// searched byteOffset // searched byteOffset
guard let kind = dictionary.declarationKind, guard let kind = dictionary.declarationKind,
let bodyOffset = dictionary.bodyOffset, let byteRange = dictionary.byteRange,
let bodyLength = dictionary.bodyLength, byteRange.contains(byteOffset)
case let byteRange = NSRange(location: bodyOffset, length: bodyLength), else {
NSLocationInRange(byteOffset, byteRange) else { return
return
} }
if parentKind != .protocol && allowedKinds.contains(kind) { if parentKind != .protocol && allowedKinds.contains(kind) {

View File

@ -35,11 +35,10 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, SubstitutionCorrect
return file.matchesAndSyntaxKinds(matching: pattern).compactMap { result, kinds in return file.matchesAndSyntaxKinds(matching: pattern).compactMap { result, kinds in
let range = result.range let range = result.range
guard kinds == [.keyword, .keyword] || kinds == [.keyword], guard kinds == [.keyword, .keyword] || kinds == [.keyword],
let byteRange = contents.NSRangeToByteRange(start: range.location, let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
length: range.length),
let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind
else { else {
return nil return nil
} }
func isKindIncluded(_ kind: ImplicitReturnConfiguration.ReturnKind) -> Bool { func isKindIncluded(_ kind: ImplicitReturnConfiguration.ReturnKind) -> Bool {

View File

@ -119,7 +119,8 @@ public struct LetVarWhitespaceRule: ConfigurationProviderRule, OptInRule, Automa
private func lineOffsets(file: SwiftLintFile, statement: SourceKittenDictionary) -> (Int, Int)? { private func lineOffsets(file: SwiftLintFile, statement: SourceKittenDictionary) -> (Int, Int)? {
guard let offset = statement.offset, guard let offset = statement.offset,
let length = statement.length else { let length = statement.length
else {
return nil return nil
} }
let startLine = file.line(byteOffset: offset) let startLine = file.line(byteOffset: offset)
@ -230,7 +231,7 @@ private extension SwiftDeclarationKind {
private extension SwiftLintFile { private extension SwiftLintFile {
// Zero based line number for specified byte offset // Zero based line number for specified byte offset
func line(byteOffset: Int) -> Int { func line(byteOffset: ByteCount) -> Int {
let lineIndex = lines.firstIndexAssumingSorted { line in let lineIndex = lines.firstIndexAssumingSorted { line in
return line.byteRange.location > byteOffset return line.byteRange.location > byteOffset
} }

Some files were not shown because too many files have changed in this diff Show More