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
[submodule "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"]
path = Carthage/Checkouts/SwiftyTextTable
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

View File

@ -1,6 +1,6 @@
github "Carthage/Commandant" "0.17.0"
github "PaulTaykalo/SourceKitten" "76350746e111778cc9e5893d35ddbcabe5f366dd"
github "drmohundro/SWXMLHash" "5.0.1"
github "jpsim/SourceKitten" "0.28.0"
github "jpsim/Yams" "2.0.0"
github "jspahrsummers/xcconfigs" "0.12"
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",
"state": {
"branch": null,
"revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0",
"version": "8.0.5"
"revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6",
"version": "8.0.4"
}
},
{
@ -48,11 +48,11 @@
},
{
"package": "SourceKitten",
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"repositoryURL": "https://github.com/PaulTaykalo/SourceKitten.git",
"state": {
"branch": null,
"revision": "97b5848e5692150d75b5cf0b81d7ebef5f4d5071",
"version": "0.28.0"
"branch": "update/framework-api-visibility",
"revision": "76350746e111778cc9e5893d35ddbcabe5f366dd",
"version": null
}
},
{

View File

@ -15,7 +15,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")),
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.28.0"),
.package(url: "https://github.com/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/scottrhoyt/SwiftyTextTable.git", from: "0.9.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
var bodyLength: Int? {
return (value["key.bodylength"] as? Int64).flatMap({ Int($0) })
var bodyLength: ByteCount? {
return (value["key.bodylength"] as? Int64).map(ByteCount.init)
}
/// Body offset.
var bodyOffset: Int? {
return (value["key.bodyoffset"] as? Int64).flatMap({ Int($0) })
var bodyOffset: ByteCount? {
return (value["key.bodyoffset"] as? Int64).map(ByteCount.init)
}
/// Body byte range.
var bodyByteRange: ByteRange? {
guard let offset = bodyOffset, let length = bodyLength else { return nil }
return ByteRange(location: offset, length: length)
}
/// Kind.
@ -53,8 +59,8 @@ public struct SourceKittenDictionary {
}
/// Length.
var length: Int? {
return (value["key.length"] as? Int64).flatMap({ Int($0) })
var length: ByteCount? {
return (value["key.length"] as? Int64).map(ByteCount.init)
}
/// Name.
var name: String? {
@ -62,24 +68,30 @@ public struct SourceKittenDictionary {
}
/// Name length.
var nameLength: Int? {
return (value["key.namelength"] as? Int64).flatMap({ Int($0) })
var nameLength: ByteCount? {
return (value["key.namelength"] as? Int64).map(ByteCount.init)
}
/// Name offset.
var nameOffset: Int? {
return (value["key.nameoffset"] as? Int64).flatMap({ Int($0) })
var nameOffset: ByteCount? {
return (value["key.nameoffset"] as? Int64).map(ByteCount.init)
}
/// Byte range of name.
var nameByteRange: ByteRange? {
guard let offset = nameOffset, let length = nameLength else { return nil }
return ByteRange(location: offset, length: length)
}
/// Offset.
var offset: Int? {
return (value["key.offset"] as? Int64).flatMap({ Int($0) })
var offset: ByteCount? {
return (value["key.offset"] as? Int64).map(ByteCount.init)
}
/// Returns byte range starting from `offset` with `length` bytes
var byteRange: NSRange? {
var byteRange: ByteRange? {
guard let offset = offset, let length = length else { return nil }
return NSRange(location: offset, length: length)
return ByteRange(location: offset, length: length)
}
/// Setter accessibility.
@ -93,13 +105,13 @@ public struct SourceKittenDictionary {
}
/// Documentation offset.
var docOffset: Int? {
return (value["key.docoffset"] as? Int64).flatMap({ Int($0) })
var docOffset: ByteCount? {
return (value["key.docoffset"] as? Int64).flatMap(ByteCount.init)
}
/// Documentation length.
var docLength: Int? {
return (value["key.doclength"] as? Int64).flatMap({ Int($0) })
var docLength: ByteCount? {
return (value["key.doclength"] as? Int64).flatMap(ByteCount.init)
}
/// The attribute for this dictionary, as returned by SourceKit.

View File

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

View File

@ -109,8 +109,10 @@ extension SwiftLintFile {
let syntax = syntaxMap
return regex(pattern).matches(in: contents, options: [], range: range).map { match in
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location,
length: match.range.length) ?? match.range
let tokensInRange = syntax.tokens(inByteRange: matchByteRange)
length: match.range.length)
let fallbackRange = ByteRange(location: ByteCount(match.range.location),
length: ByteCount(match.range.length))
let tokensInRange = syntax.tokens(inByteRange: matchByteRange ?? fallbackRange)
return (match, tokensInRange)
}
}
@ -143,12 +145,11 @@ extension SwiftLintFile {
var maybeLine = lineIterator.next()
var maybeStructure = structureIterator.next()
while let line = maybeLine, let structure = maybeStructure {
if NSLocationInRange(structure.byteRange.location, line.byteRange),
if line.byteRange.contains(structure.byteRange.location),
let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) {
results[line.index].append(swiftDeclarationKind)
}
let lineEnd = NSMaxRange(line.byteRange)
if structure.byteRange.location >= lineEnd {
if structure.byteRange.location >= line.byteRange.upperBound {
maybeLine = lineIterator.next()
} else {
maybeStructure = structureIterator.next()
@ -168,12 +169,12 @@ extension SwiftLintFile {
var maybeToken = tokenGenerator.next()
while let line = maybeLine, let token = maybeToken {
let tokenRange = token.range
if NSLocationInRange(token.offset, line.byteRange) ||
NSLocationInRange(line.byteRange.location, tokenRange) {
if line.byteRange.contains(token.offset) ||
tokenRange.contains(line.byteRange.location) {
results[line.index].append(token)
}
let tokenEnd = NSMaxRange(tokenRange)
let lineEnd = NSMaxRange(line.byteRange)
let tokenEnd = tokenRange.upperBound
let lineEnd = line.byteRange.upperBound
if tokenEnd < lineEnd {
maybeToken = tokenGenerator.next()
} else if tokenEnd > lineEnd {
@ -337,6 +338,6 @@ extension SwiftLintFile {
}
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 {
let name: String
let kind: SwiftDeclarationKind
let offset: Int
let offset: ByteCount
let dictionary: SourceKittenDictionary
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.
/// Fails if tthe specified offset was not a valid location in the file.
/// Fails if the specified offset was not a valid location in the file.
///
/// - parameter file: The file for this location.
/// - parameter offset: The offset in bytes into the file for this location.
public init(file: SwiftLintFile, byteOffset offset: Int) {
public init(file: SwiftLintFile, byteOffset offset: ByteCount) {
self.file = file.path
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
line = lineAndCharacter.line

View File

@ -22,7 +22,7 @@ public struct SwiftLintSyntaxMap {
/// - parameter byteRange: Byte-based NSRange.
///
/// - returns: The array of syntax tokens intersecting with byte range.
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
internal func tokens(inByteRange byteRange: ByteRange) -> [SwiftLintSyntaxToken] {
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
return token.range.intersects(byteRange)
}
@ -46,10 +46,39 @@ public struct SwiftLintSyntaxMap {
/// Returns the syntax kinds in the specified byte range.
///
/// - parameter byteRange: Byte-based NSRange.
/// - parameter byteRange: Byte range.
///
/// - returns: The syntax kinds in the specified byte range.
internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
internal func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] {
return tokens(inByteRange: byteRange).compactMap { $0.kind }
}
}
// 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.
public var range: NSRange {
return NSRange(location: value.offset, length: value.length)
public var range: ByteRange {
return value.range
}
/// The starting byte offset in a source file for this token.
public var offset: Int {
public var offset: ByteCount {
return value.offset
}
/// The length in bytes for this token.
public var length: Int {
public var length: ByteCount {
return value.length
}
}

View File

@ -111,7 +111,8 @@ public extension SwiftVersion {
let decl = file.structureDictionary.kinds()
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) {
return .init(rawValue: file.contents.substring(from: token.offset + 1, length: token.length - 2))
let offsetRange = ByteRange(location: token.offset + 1, length: token.length - 2)
return .init(rawValue: file.stringView.substringWithByteRange(offsetRange)!)
}
return .three

View File

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

View File

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

View File

@ -31,7 +31,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
private func variableViolations(file: SwiftLintFile,
kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [Int] {
dictionary: SourceKittenDictionary) -> [ByteCount] {
guard
SwiftDeclarationKind.variableKinds.contains(kind),
let offset = dictionary.offset,
@ -42,7 +42,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
private func functionViolations(file: SwiftLintFile,
kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [Int] {
dictionary: SourceKittenDictionary) -> [ByteCount] {
guard
SwiftDeclarationKind.functionKinds.contains(kind),
let nameOffset = dictionary.nameOffset,
@ -51,8 +51,9 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
let offset = dictionary.offset,
case let start = nameOffset + nameLength,
case let end = dictionary.bodyOffset ?? offset + length,
case let byteRange = ByteRange(location: start, length: end - start),
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
else { return [] }

View File

@ -22,19 +22,18 @@ public struct DuplicateImportsRule: ConfigurationProviderRule, AutomaticTestable
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples
)
private func rangesInConditionalCompilation(file: SwiftLintFile) -> [NSRange] {
private func rangesInConditionalCompilation(file: SwiftLintFile) -> [ByteRange] {
let contents = file.stringView
let ranges = file.syntaxMap.tokens
.filter { $0.kind == .buildconfigKeyword }
.map { $0.range }
.filter { range in
let keyword = contents.substringWithByteRange(start: range.location, length: range.length)
return ["#if", "#endif"].contains(keyword)
return ["#if", "#endif"].contains(contents.substringWithByteRange(range))
}
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
return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap {
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,
thatAreNotInRanges ranges: [NSRange]) -> [Int] {
thatAreNotInRanges ranges: [ByteRange]) -> [ByteCount] {
let extensionKinds: Set<SwiftDeclarationKind> = [.extension, .extensionClass, .extensionEnum,
.extensionProtocol, .extensionStruct]
@ -83,7 +83,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
// the "internal" token correspond to the type if there're only
// attributeBuiltin (`final` for example) tokens between them
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]
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 })
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)
.compactMap { substructureElements(of: $0, matching: .enumelement) }
.flatMap(enumElementsMissingInitExpr)

View File

@ -71,14 +71,14 @@ public struct ExplicitInitRule: SubstitutionCorrectableASTRule, ConfigurationPro
&& initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0
}
let length = ".init".utf8.count
let length = ByteCount(".init".utf8.count)
guard kind == .call,
let name = dictionary.name, isExpected(name),
let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength,
let range = file.stringView
.byteRangeToNSRange(start: nameOffset + nameLength - length, length: length)
.byteRangeToNSRange(ByteRange(location: nameOffset + nameLength - length, length: length))
else { return [] }
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)
let dictionary = file.structureDictionary
let internalTypesOffsets = dictionary.substructure.compactMap { element -> Int? in
let internalTypesOffsets = dictionary.substructure.compactMap { element -> ByteCount? in
// ignore extensions
guard let kind = element.declarationKind,
!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
// attributeBuiltin (`final` for example) tokens between them
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]
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 })
return firstPartition.last
}

View File

@ -108,7 +108,8 @@ private extension SourceKittenDictionary {
guard
let nameOffset = nameOffset,
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 {
return false
}
@ -125,7 +126,8 @@ private extension SourceKittenDictionary {
guard
let nameOffset = nameOffset,
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 {
return false
}
@ -137,11 +139,11 @@ private extension SourceKittenDictionary {
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")
}
var caseExpressionRanges: [NSRange] {
var caseExpressionRanges: [ByteRange] {
return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr")
}
@ -152,30 +154,27 @@ private extension SourceKittenDictionary {
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 {
return []
}
return elements
.filter { elementKind == $0.kind }
.compactMap {
guard let location = $0.offset, let length = $0.length else { return nil }
return NSRange(location: location, length: length)
}
.compactMap { $0.byteRange }
}
}
private extension SwiftLintFile {
var captureGroupByteRanges: [NSRange] {
var captureGroupByteRanges: [ByteRange] {
return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]",
excludingSyntaxKinds: SyntaxKind.commentKinds)
.compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) }
}
}
private extension Collection where Element == NSRange {
func contains(_ index: Int) -> Bool {
private extension Collection where Element == ByteRange {
func contains(_ index: ByteCount) -> Bool {
return contains { $0.contains(index) }
}
}

View File

@ -79,20 +79,23 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .extension, let offset = dictionary.offset,
dictionary.inheritedTypes.isEmpty else {
return []
dictionary.inheritedTypes.isEmpty
else {
return []
}
let declarations = dictionary.substructure.compactMap { entry -> (acl: AccessControlLevel, offset: Int)? in
guard entry.declarationKind != nil,
let acl = entry.accessibility,
let offset = entry.offset else {
return nil
let declarations = dictionary.substructure
.compactMap { entry -> (acl: AccessControlLevel, offset: ByteCount)? in
guard entry.declarationKind != nil,
let acl = entry.accessibility,
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 allowedACLs: Set<AccessControlLevel> = [.internal, .private, .open]
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,
declarationOffsets: [Int],
declarationOffsets: [ByteCount],
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard let offset = dictionary.offset, let length = dictionary.length,
guard let byteRange = dictionary.byteRange,
case let contents = file.stringView,
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
let range = contents.byteRangeToNSRange(byteRange) else {
return []
}
@ -138,7 +141,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
// the ACL token correspond to the type if there're only
// attributeBuiltin (`final` for example) tokens between them
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]
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
return firstPartition.last
}

View File

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

View File

@ -133,9 +133,9 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
return false
}
let beforeIfRange = NSRange(location: offset, length: ifOffset - offset)
let beforeIfRange = ByteRange(location: offset, length: ifOffset - offset)
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) +
file.syntaxMap.kinds(inByteRange: afterIfRange)
@ -148,13 +148,11 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
let kind = "source.lang.swift.structure.elem.condition_expr"
let contents = file.stringView
return dictionary.elements.contains { element in
guard element.kind == kind,
let offset = element.offset,
let length = element.length,
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
return false
let range = element.byteRange.flatMap(file.stringView.byteRangeToNSRange)
else {
return false
}
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`.
private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SwiftLintSyntaxMap,
private func isFirstRangeExcludedToken(byteRange: ByteRange, syntaxMap: SwiftLintSyntaxMap,
file: SwiftLintFile) -> Bool {
let tokens = syntaxMap.tokens(inByteRange: byteRange)
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
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)
guard let lastItem = kinds.last,
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.*"
let byteOffset = lastItem.byteRange.location
let byteLength = byteRange.location - byteOffset
if let varDeclarationString = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength),
let varRange = ByteRange(location: lastItem.byteRange.location,
length: byteRange.location - lastItem.byteRange.location)
if let varDeclarationString = file.stringView.substringWithByteRange(varRange),
varDeclarationString.contains("=") {
// if declarations contains "=", range is not type annotation
return false

View File

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

View File

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

View File

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

View File

@ -181,8 +181,8 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv
var adjustedLocations = [Int]()
for dictionary in violatingDictionaries.reversed() {
guard let offset = dictionary.offset, let length = dictionary.length,
let range = file.stringView.byteRangeToNSRange(start: offset, length: length),
guard let byteRange = dictionary.byteRange,
let range = file.stringView.byteRangeToNSRange(byteRange),
let name = dictionary.name,
let correctedName = type(of: self).constructorsToCorrectedNames[name],
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] {
let contents = file.stringView
return arguments.compactMap { argument -> String? in
guard argument.name == nil,
let offset = argument.offset,
let length = argument.length else {
return nil
guard argument.name == nil, let byteRange = argument.byteRange 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,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard kind == .case,
let length = dictionary.length,
let offset = dictionary.offset,
let byteRange = dictionary.byteRange,
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)
else {
return []
@ -40,7 +39,7 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
let nsRange = nonCommentCaseBody[0].0
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,
severity: configuration.severity,
location: Location(file: file, characterOffset: nsRange.location))]
@ -49,13 +48,12 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
return []
}
private func isNextTokenUnknownAttribute(afterOffset offset: Int, file: SwiftLintFile) -> Bool {
private func isNextTokenUnknownAttribute(afterOffset offset: ByteCount, file: SwiftLintFile) -> Bool {
let nextNonCommentToken = file.syntaxMap.tokens
.first { token in
guard let kind = token.kind, !kind.isCommentLike else {
return false
}
return token.offset > offset
}

View File

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

View File

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

View File

@ -43,10 +43,10 @@ public struct PatternMatchingKeywordsRule: ASTRule, ConfigurationProviderRule, O
let contents = file.stringView
return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
let offset = subDictionary.offset,
let length = subDictionary.length,
let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else {
return []
let caseByteRange = subDictionary.byteRange,
let caseRange = contents.byteRangeToNSRange(caseByteRange)
else {
return []
}
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 }
guard let lastKind = parts.last,
lastKind.kind == .attributeBuiltin,
let aclName = contents.substringWithByteRange(start: lastKind.offset, length: lastKind.length),
let aclName = contents.substringWithByteRange(lastKind.range),
AccessControlLevel(description: aclName) == .fileprivate,
let range = contents.byteRangeToNSRange(start: lastKind.offset, length: lastKind.length) else {
return nil
let range = contents.byteRangeToNSRange(lastKind.range)
else {
return nil
}
return range

View File

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

View File

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

View File

@ -81,7 +81,8 @@ public struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCo
case let start = nameOffset + nameLength,
case let end = dictionary.bodyOffset ?? offset + length,
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,
let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else {
return []

View File

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

View File

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

View File

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

View File

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

View File

@ -54,13 +54,13 @@ public struct XCTFailMessageRule: ASTRule, ConfigurationProviderRule, AutomaticT
}
private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
guard
let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength else { return false }
guard let bodyRange = dictionary.bodyByteRange 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 == "\"\""
}
}

View File

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

View File

@ -61,15 +61,14 @@ public struct AnyObjectProtocolRule: SubstitutionCorrectableASTRule, OptInRule,
return dictionary.elements.compactMap { subDict -> NSRange? in
guard
let offset = subDict.offset,
let length = subDict.length,
let content = file.stringView.substringWithByteRange(start: offset, length: length),
let byteRange = subDict.byteRange,
let content = file.stringView.substringWithByteRange(byteRange),
content == "class"
else {
return nil
else {
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"),
let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength,
let bodyRange = dictionary.bodyByteRange,
let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength,
let offset = dictionary.offset else {
return []
}
let range = NSRange(location: bodyOffset, length: bodyLength)
let tokens = file.syntaxMap.tokens(inByteRange: range).filter { token in
let tokens = file.syntaxMap.tokens(inByteRange: bodyRange).filter { token in
guard let kind = token.kind else {
return false
}
@ -84,37 +84,39 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
}
private func isClosureParameter(firstToken: SwiftLintSyntaxToken,
nameEndPosition: Int,
nameEndPosition: ByteCount,
file: SwiftLintFile) -> Bool {
let length = firstToken.offset - nameEndPosition
guard length > 0,
case let contents = file.stringView,
let byteRange = contents.byteRangeToNSRange(start: nameEndPosition, length: length) else {
return false
case let byteRange = ByteRange(location: nameEndPosition, length: length),
let nsRange = contents.byteRangeToNSRange(byteRange)
else {
return false
}
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,
bodyEndPosition: Int,
bodyEndPosition: ByteCount,
file: SwiftLintFile) -> Bool {
let lastTokenEnd = lastToken.offset + lastToken.length
let remainingLength = bodyEndPosition - lastTokenEnd
let remainingRange = NSRange(location: lastTokenEnd, length: remainingLength)
let remainingRange = ByteRange(location: lastTokenEnd, length: remainingLength)
return containsContent(inByteRange: remainingRange, file: file)
}
private func containsLeadingContent(tokens: [SwiftLintSyntaxToken],
bodyStartPosition: Int,
bodyStartPosition: ByteCount,
file: SwiftLintFile) -> Bool {
let inTokenPosition = tokens.firstIndex(where: { token in
token.kind == .keyword && file.contents(for: token) == "in"
})
let firstToken: SwiftLintSyntaxToken
let start: Int
let start: ByteCount
if let position = inTokenPosition {
let index = tokens.index(after: position)
firstToken = tokens[index]
@ -126,25 +128,26 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
}
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)
}
private func containsContent(inByteRange byteRange: NSRange, file: SwiftLintFile) -> Bool {
let nsstring = file.stringView
private func containsContent(inByteRange byteRange: ByteRange, file: SwiftLintFile) -> Bool {
let stringView = file.stringView
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 {
ranges.remove(in: token.range)
let ranges = NSMutableIndexSet(indexesIn: nsRange)
for tokenNSRange in remainingTokens.compactMap({ stringView.byteRangeToNSRange($0.range) }) {
ranges.remove(in: tokenNSRange)
}
var containsContent = false
ranges.enumerateRanges(options: []) { range, stop in
guard let substring = nsstring.substringWithByteRange(start: range.location, length: range.length) else {
return
}
let substring = stringView.substring(with: range)
let processedSubstring = substring
.trimmingCharacters(in: CharacterSet(charactersIn: "{}"))
.trimmingCharacters(in: .whitespacesAndNewlines)

View File

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

View File

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

View File

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

View File

@ -48,7 +48,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
}
private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
kind: SwiftExpressionKind) -> [Int] {
kind: SwiftExpressionKind) -> [ByteCount] {
guard kind == .call,
let name = dictionary.name,
name.hasSuffix(".addObserver"),
@ -57,7 +57,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
argumentsNames == ["forName", "object", "queue"] ||
argumentsNames == ["forName", "object", "queue", "using"],
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 []
}
@ -83,7 +83,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
}
private extension SourceKittenDictionary {
func functions(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
func functions(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
return structures(forByteOffset: byteOffset)
.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) }
.flatMap { $0 }
var elementsByName: [String: [Int]] = [:]
var elementsByName: [String: [ByteCount]] = [:]
for element in enumElements {
guard let name = element.name,
let nameWithoutParameters = name.split(separator: "(").first,
let offset = element.offset else {
let offset = element.offset
else {
continue
}

View File

@ -34,9 +34,7 @@ public struct DynamicInlineRule: ASTRule, ConfigurationProviderRule, AutomaticTe
case let attributes = dictionary.enclosedSwiftAttributes,
attributes.contains(.dynamic),
attributes.contains(.inline),
let funcByteOffset = dictionary.offset,
let funcOffset = file.stringView
.byteRangeToNSRange(start: funcByteOffset, length: 0)?.location,
let funcOffset = dictionary.offset.flatMap(file.stringView.location),
case let inlinePattern = regex("@inline"),
case let range = NSRange(location: 0, length: funcOffset),
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,
length: leftmostToken.length)
let violationRange = file.stringView.byteRangeToNSRange(leftmostToken.range)
return violationRange
}
@ -175,12 +174,13 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom
private extension StringView {
func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? {
return substringWithByteRange(start: syntaxToken.offset, length: syntaxToken.length)
return substringWithByteRange(syntaxToken.range)
}
func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? {
return substringWithByteRange(start: startToken.offset + startToken.length,
length: endToken.offset - startToken.offset - startToken.length)
let byteRange = ByteRange(location: startToken.range.upperBound,
length: endToken.offset - startToken.range.upperBound)
return substringWithByteRange(byteRange)
}
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 outerKind = kinds[outerKindIndex],
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),
!tokens.contains(where: isNotComment) else {
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
}

View File

@ -41,14 +41,14 @@ public struct LowerACLThanParentRule: OptInRule, ConfigurationProviderRule, Auto
}
private func validateACL(isHigherThan parentAccessibility: AccessControlLevel,
in substructure: SourceKittenDictionary) -> [Int] {
return substructure.substructure.flatMap { element -> [Int] in
in substructure: SourceKittenDictionary) -> [ByteCount] {
return substructure.substructure.flatMap { element -> [ByteCount] in
guard let elementKind = element.declarationKind,
elementKind.isRelevantDeclaration else {
return []
}
var violationOffset: Int?
var violationOffset: ByteCount?
let accessibility = element.accessibility ?? .internal
// Swift 5 infers members of private types with no explicit ACL attribute to be `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)
}.compactMap { range, syntaxTokens in
let identifierRange = file.stringView
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
let byteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
let identifierRange = file.stringView.byteRangeToNSRange(byteRange)
return identifierRange.map { NSUnionRange($0, range) }
}
}

View File

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

View File

@ -28,9 +28,8 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
dictionary.name == "NSLocalizedString",
let firstArgument = dictionary.enclosedArguments.first,
firstArgument.name == nil,
let offset = firstArgument.offset,
let length = firstArgument.length,
case let kinds = file.syntaxMap.kinds(inByteRange: NSRange(location: offset, length: length)),
let byteRange = firstArgument.byteRange,
case let kinds = file.syntaxMap.kinds(inByteRange: byteRange),
!kinds.allSatisfy({ $0 == .string }) else {
return []
}
@ -38,7 +37,7 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
return [
StyleViolation(ruleDescription: type(of: self).description,
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,
dictionary: SourceKittenDictionary) -> [Int] {
return dictionary.substructure.flatMap { subDict -> [Int] in
dictionary: SourceKittenDictionary) -> [ByteCount] {
return dictionary.substructure.flatMap { subDict -> [ByteCount] in
// complete detachment is allowed on `deinit`
if subDict.declarationKind == .functionMethodInstance,
subDict.name == "deinit" {
@ -51,16 +51,11 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu
private var methodName = "NotificationCenter.default.removeObserver"
private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
guard let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength else {
return false
}
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 {
guard let bodyRange = dictionary.bodyByteRange,
case let tokens = file.syntaxMap.tokens(inByteRange: bodyRange),
tokens.kinds == [.keyword],
let token = tokens.first
else {
return false
}

View File

@ -55,12 +55,12 @@ public struct OrphanedDocCommentRule: ConfigurationProviderRule {
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 {
return nil
}
return [NSRange(location: docOffset, length: docLength)]
return [ByteRange(location: docOffset, length: docLength)]
}.sorted { $0.location < $1.location }
return docStringsTokens

View File

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

View File

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

View File

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

View File

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

View File

@ -29,26 +29,28 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi
public func validate(file: SwiftLintFile, kind: SwiftExpressionKind,
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,
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 inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location,
length: inTokenRange.length) else {
return []
length: inTokenRange.length)
else {
return []
}
let length = inTokenByteRange.location - bodyOffset
let variables = localVariableDeclarations(inByteRange: NSRange(location: bodyOffset, length: length),
let length = inTokenByteRange.location - bodyRange.location
let variables = localVariableDeclarations(inByteRange: ByteRange(location: bodyRange.location, length: length),
structureDictionary: file.structureDictionary)
let unownedVariableOffsets = variables.compactMap { dictionary in
return dictionary.swiftAttributes.first { attributeDict in
guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak,
let offset = attributeDict.offset, let length = attributeDict.length else {
return false
let attributeByteRange = attributeDict.byteRange
else {
return false
}
return contents.substringWithByteRange(start: offset, length: length) == "unowned"
return contents.substringWithByteRange(attributeByteRange) == "unowned"
}?.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] {
return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .varLocal,
let variableByteRange = dictionary.byteRange,
byteRange.intersects(variableByteRange) else {
return nil
byteRange.intersects(variableByteRange)
else {
return nil
}
return [dictionary]
}

View File

@ -80,12 +80,14 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat
guard kind == .closure,
let offset = dictionary.offset,
let length = dictionary.length,
let closureRange = contents.byteRangeToNSRange(start: offset, length: length)
let closureByteRange = dictionary.byteRange,
let closureRange = contents.byteRangeToNSRange(closureByteRange)
else { return [] }
let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length)
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)
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
.tokens(inByteRange: byteRange)
.compactMap { token -> String? in

View File

@ -127,54 +127,62 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config
let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength,
let bodyLength = dictionary.bodyLength,
bodyLength > 0 else {
return []
bodyLength > 0
else {
return []
}
let rangeStart = nameOffset + nameLength
let rangeLength = (offset + length) - (nameOffset + nameLength)
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
let parameters = dictionary.enclosedVarParameters
let contents = file.stringView
return parameters.compactMap { param -> (NSRange, String)? in
guard let paramOffset = param.offset,
let name = param.name,
name != "_",
let regex = try? NSRegularExpression(pattern: name,
options: [.ignoreMetacharacters]),
let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength)
self.rangeAndName(parameter: param, contents: contents, byteRange: byteRange, file: file)
}
}
private func rangeAndName(parameter: SourceKittenDictionary, contents: StringView, byteRange: ByteRange,
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 {
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
}
let paramLength = 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 {
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
}
let violationByteRange = ByteRange(location: paramOffset, length: paramLength)
return contents.byteRangeToNSRange(violationByteRange).map { range in
return (range, name)
}
}

View File

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

View File

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

View File

@ -203,7 +203,7 @@ private extension SwiftLintFile {
if syntaxKindsToSkip.contains(tokenKind) {
continue
}
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: Int64(token.offset),
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: token.offset,
arguments: compilerArguments)
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else {
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
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())
.map(SourceKittenDictionary.init) else {
queuedPrintError("Could not get index")
@ -271,9 +271,9 @@ private extension SwiftLintFile {
let offset = lineOffset + column - 1
// 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())
.map(SourceKittenDictionary.init) else {
queuedPrintError("Could not get cursor info")

View File

@ -101,43 +101,43 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
public func validate(file: SwiftLintFile) -> [StyleViolation] {
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
guard let dict = declarations(forByteOffset: setToken.offset,
structureDictionary: file.structureDictionary).last,
let bodyOffset = dict.bodyOffset, let bodyLength = dict.bodyLength,
let bodyByteRange = dict.bodyByteRange,
case let contents = file.stringView,
let propertyRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength),
let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict) else {
return nil
let propertyRange = contents.byteRangeToNSRange(bodyByteRange),
let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict)
else {
return nil
}
let argument = findNamedArgument(after: setToken, file: file)
let propertyEndOffset = bodyOffset + bodyLength
let setterByteRange: NSRange
let propertyEndOffset = bodyByteRange.upperBound
let setterByteRange: ByteRange
if setToken.offset > getToken.offset { // get {} set {}
let startOfBody: Int
let startOfBody: ByteCount
if let argumentToken = argument?.token {
startOfBody = argumentToken.offset + argumentToken.length
} else {
startOfBody = setToken.offset
}
setterByteRange = NSRange(location: startOfBody,
length: propertyEndOffset - startOfBody)
setterByteRange = ByteRange(location: startOfBody,
length: propertyEndOffset - startOfBody)
} else { // set {} get {}
let startOfBody: Int
let startOfBody: ByteCount
if let argumentToken = argument?.token {
startOfBody = argumentToken.offset + argumentToken.length
} else {
startOfBody = setToken.offset
}
setterByteRange = NSRange(location: startOfBody,
length: getToken.offset - startOfBody)
setterByteRange = ByteRange(location: startOfBody,
length: getToken.offset - startOfBody)
}
guard let setterRange = contents.byteRangeToNSRange(start: setterByteRange.location,
length: setterByteRange.length) else {
guard let setterRange = contents.byteRangeToNSRange(setterByteRange) else {
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] {
var results = [SourceKittenDictionary]()
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
// searched byteOffset
guard let kind = dictionary.declarationKind,
let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength,
case let byteRange = NSRange(location: bodyOffset, length: bodyLength),
NSLocationInRange(byteOffset, byteRange) else {
return
let byteRange = dictionary.bodyByteRange,
byteRange.contains(byteOffset)
else {
return
}
if parentKind != .protocol && allowedKinds.contains(kind) {

View File

@ -74,7 +74,10 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
guard !isComputed else { return [] }
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]
}
@ -83,13 +86,14 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
return (violationRange, "weak var")
}
private func protocolDeclarations(forByteOffset byteOffset: Int,
private func protocolDeclarations(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
return structureDictionary.traverseBreadthFirst { dictionary in
guard dictionary.declarationKind == .protocol,
let byteRange = dictionary.byteRange,
NSLocationInRange(byteOffset, byteRange) else {
return nil
byteRange.contains(byteOffset)
else {
return nil
}
return [dictionary]
}

View File

@ -61,11 +61,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule,
public func validate(file: SwiftLintFile,
kind: StatementKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
guard observedStatements.contains(kind),
let offset = dictionary.offset,
let length = dictionary.length
else {
return []
guard observedStatements.contains(kind), let offset = dictionary.offset else {
return []
}
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
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,
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,
dictionary: SourceKittenDictionary) -> Int {
let bodyOffset = dictionary.bodyOffset ?? 0
let bodyLength = dictionary.bodyLength ?? 0
let bodyRange = dictionary.bodyByteRange ?? ByteRange(location: 0, length: 0)
let contents = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) ?? ""
let contents = file.stringView.substringWithByteRange(bodyRange) ?? ""
let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1
return complexity - fallthroughCount

View File

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

View File

@ -73,7 +73,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
}
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])
guard kinds.contains(kind),
let type = dictionary.typeName,
@ -86,12 +86,11 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
}
private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] {
kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] {
let contents = file.stringView
guard SwiftDeclarationKind.functionKinds.contains(kind),
let returnRange = returnRangeForFunction(dictionary: dictionary),
let returnSubstring = contents.substringWithByteRange(start: returnRange.location,
length: returnRange.length) else {
let returnSubstring = contents.substringWithByteRange(returnRange) else {
return []
}
@ -99,13 +98,13 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
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 {
return []
}
var text = text.bridge()
var offsets = [(offset: Int, size: Int)]()
var offsets = [(offset: ByteCount, size: Int)]()
for (range, kind) in ranges {
let substring = text.substring(with: range)
@ -124,7 +123,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
return offsets
}
private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> NSRange? {
private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> ByteRange? {
guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength,
let length = dictionary.length,
@ -139,7 +138,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
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)] {

View File

@ -47,11 +47,11 @@ public struct FirstWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule
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
}
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength))
let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
return !syntaxKinds.contains(.string)
}
}

View File

@ -40,11 +40,11 @@ public struct LastWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule,
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
}
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength))
let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
return !syntaxKinds.contains(.string)
}
}

View File

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

View File

@ -138,7 +138,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
}
private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)],
attributesTokens: [(String, NSRange)],
attributesTokens: [(String, ByteRange)],
line: Line, file: SwiftLintFile) -> Set<String> {
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
let hasParameter = attributeContainsParameter(attributeRange: $1,
@ -241,16 +241,17 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
return allTokens
}
private func attributeContainsParameter(attributeRange: NSRange,
private func attributeContainsParameter(attributeRange: ByteRange,
line: Line, file: SwiftLintFile) -> Bool {
let restOfLineOffset = attributeRange.location + attributeRange.length
let restOfLineLength = line.byteRange.location + line.byteRange.length - restOfLineOffset
let restOfLineOffset = attributeRange.upperBound
let restOfLineLength = line.byteRange.upperBound - restOfLineOffset
let regex = AttributesRule.regularExpression
let contents = file.stringView
// 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,
regex.firstMatch(in: restOfLine, options: [], range: range) != nil else {
return false
@ -259,7 +260,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
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 {
return nil
}

View File

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

View File

@ -52,8 +52,9 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
guard let nameOffset = dictionary.nameOffset,
let nameLength = dictionary.nameLength,
let bodyLength = dictionary.bodyLength,
bodyLength > 0 else {
return []
bodyLength > 0
else {
return []
}
let parameters = dictionary.enclosedVarParameters
@ -70,14 +71,16 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
let rangeLength = paramOffset - rangeStart
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,
match.location != NSNotFound,
let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location,
let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset),
let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset),
braceLine != paramLine else {
return nil
braceLine != paramLine
else {
return nil
}
return StyleViolation(ruleDescription: type(of: self).description,

View File

@ -90,7 +90,7 @@ public struct ClosureSpacingRule: CorrectableRule, ConfigurationProviderRule, Op
return kindsToExclude.contains(tokenKind)
}
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) }))
}

View File

@ -86,12 +86,13 @@ public struct CollectionAlignmentRule: ASTRule, ConfigurationProviderRule, OptIn
}
}
private func colonLocation(with file: SwiftLintFile, keyOffset: Int, keyLength: Int,
valueOffset: Int) -> Location? {
private func colonLocation(with file: SwiftLintFile, keyOffset: ByteCount, keyLength: ByteCount,
valueOffset: ByteCount) -> Location? {
let contents = file.stringView
let matchStart = keyOffset + keyLength
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)
return matches.first.map { Location(file: file, characterOffset: $0.location) }

View File

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

View File

@ -3,7 +3,7 @@ import SourceKittenFramework
extension ColonRule {
internal func functionCallColonViolationRanges(in file: SwiftLintFile,
dictionary: SourceKittenDictionary) -> [NSRange] {
dictionary: SourceKittenDictionary) -> [ByteRange] {
return dictionary.traverseDepthFirst { subDict in
guard let kind = subDict.expressionKind else { return nil }
return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict)
@ -11,15 +11,16 @@ extension ColonRule {
}
internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
dictionary: SourceKittenDictionary) -> [NSRange] {
dictionary: SourceKittenDictionary) -> [ByteRange] {
guard kind == .argument,
let ranges = functionCallColonRanges(dictionary: dictionary) else {
return []
let ranges = functionCallColonRanges(dictionary: dictionary)
else {
return []
}
let contents = file.stringView
return ranges.filter {
guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else {
guard let colon = contents.substringWithByteRange($0) else {
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,
let nameLength = dictionary.nameLength, nameLength > 0,
let bodyOffset = dictionary.bodyOffset,
case let location = nameOffset + nameLength,
bodyOffset > location else {
return nil
bodyOffset > location
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)
}.compactMap { match, syntaxTokens in
let identifierRange = contents
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
let firstSyntaxTokenByteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
let identifierRange = contents.byteRangeToNSRange(firstSyntaxTokenByteRange)
return identifierRange.map { NSUnionRange($0, match.range) }
}
}
@ -53,7 +53,7 @@ internal extension ColonRule {
case (.identifier, .keyword),
(.typeidentifier, .keyword):
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,
file.contents(for: syntaxTokens[1]) == "Self" {
validKinds = false

View File

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

View File

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

View File

@ -82,12 +82,14 @@ public struct EmptyParenthesesWithTrailingClosureRule: SubstitutionCorrectableAS
let rangeStart = nameOffset + nameLength
let rangeLength = (offset + length) - (nameOffset + nameLength)
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
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,
match.location == range.location else {
return []
match.location == range.location
else {
return []
}
return [match]

View File

@ -140,12 +140,12 @@ public struct ExplicitSelfRule: CorrectableRule, ConfigurationProviderRule, Anal
let contents = file.stringView
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")
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 {
func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [Int]) throws
func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [ByteCount]) throws
-> [[String: SourceKitRepresentable]] {
return try byteOffsets.compactMap { offset in
if stringView.substringWithByteRange(start: offset - 1, length: 1)! == "." { return nil }
var cursorInfo = try Request.cursorInfo(file: self.path!, offset: Int64(offset),
if stringView.substringWithByteRange(ByteRange(location: offset - 1, length: 1))! == "." { return nil }
var cursorInfo = try Request.cursorInfo(file: self.path!, offset: offset,
arguments: compilerArguments).sendIfNotDisabled()
cursorInfo["swiftlint.offset"] = Int64(offset)
cursorInfo["swiftlint.offset"] = Int64(offset.value)
return cursorInfo
}
}
}
private extension StringView {
func byteOffset(forLine line: Int, column: Int) -> Int {
var byteOffset = 0
func byteOffset(forLine line: Int, column: Int) -> ByteCount {
var byteOffset = ByteCount(0)
for line in lines[..<(line - 1)] {
byteOffset += line.byteRange.length
}
return byteOffset + column - 1
return byteOffset + ByteCount(column - 1)
}
func recursiveByteOffsets(_ dict: [String: Any]) -> [Int] {
let cur: [Int]
func recursiveByteOffsets(_ dict: [String: Any]) -> [ByteCount] {
let cur: [ByteCount]
if let line = dict["key.line"] as? Int64,
let column = dict["key.column"] as? Int64,
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 index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled()
let binaryOffsets = file.stringView.recursiveByteOffsets(index)

View File

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

View File

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

View File

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

View File

@ -189,7 +189,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
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
guard let dict = declarations(forByteOffset: token.offset,
structureDictionary: file.structureDictionary).last else {
@ -220,7 +220,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
}
private extension ImplicitGetterRule {
func declarations(forByteOffset byteOffset: Int,
func declarations(forByteOffset byteOffset: ByteCount,
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
@ -230,11 +230,10 @@ private extension ImplicitGetterRule {
// Only accepts declarations which contains a body and contains the
// searched byteOffset
guard let kind = dictionary.declarationKind,
let bodyOffset = dictionary.bodyOffset,
let bodyLength = dictionary.bodyLength,
case let byteRange = NSRange(location: bodyOffset, length: bodyLength),
NSLocationInRange(byteOffset, byteRange) else {
return
let byteRange = dictionary.byteRange,
byteRange.contains(byteOffset)
else {
return
}
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
let range = result.range
guard kinds == [.keyword, .keyword] || kinds == [.keyword],
let byteRange = contents.NSRangeToByteRange(start: range.location,
length: range.length),
let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind
else {
return nil
else {
return nil
}
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)? {
guard let offset = statement.offset,
let length = statement.length else {
let length = statement.length
else {
return nil
}
let startLine = file.line(byteOffset: offset)
@ -230,7 +231,7 @@ private extension SwiftDeclarationKind {
private extension SwiftLintFile {
// Zero based line number for specified byte offset
func line(byteOffset: Int) -> Int {
func line(byteOffset: ByteCount) -> Int {
let lineIndex = lines.firstIndexAssumingSorted { line in
return line.byteRange.location > byteOffset
}

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