Compare commits
3 Commits
main
...
pt-byte-ap
Author | SHA1 | Date |
---|---|---|
![]() |
6240b5a606 | |
![]() |
4e01095004 | |
![]() |
1a28275891 |
|
@ -9,7 +9,7 @@
|
||||||
url = https://github.com/Carthage/Commandant.git
|
url = https://github.com/Carthage/Commandant.git
|
||||||
[submodule "Carthage/Checkouts/SourceKitten"]
|
[submodule "Carthage/Checkouts/SourceKitten"]
|
||||||
path = Carthage/Checkouts/SourceKitten
|
path = Carthage/Checkouts/SourceKitten
|
||||||
url = https://github.com/jpsim/SourceKitten.git
|
url = https://github.com/PaulTaykalo/SourceKitten.git
|
||||||
[submodule "Carthage/Checkouts/SwiftyTextTable"]
|
[submodule "Carthage/Checkouts/SwiftyTextTable"]
|
||||||
path = Carthage/Checkouts/SwiftyTextTable
|
path = Carthage/Checkouts/SwiftyTextTable
|
||||||
url = https://github.com/scottrhoyt/SwiftyTextTable.git
|
url = https://github.com/scottrhoyt/SwiftyTextTable.git
|
||||||
|
|
2
Cartfile
2
Cartfile
|
@ -1,2 +1,2 @@
|
||||||
github "jpsim/SourceKitten" ~> 0.28.0
|
github "PaulTaykalo/SourceKitten" "update/framework-api-visibility"
|
||||||
github "scottrhoyt/SwiftyTextTable" ~> 0.9.0
|
github "scottrhoyt/SwiftyTextTable" ~> 0.9.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
github "Carthage/Commandant" "0.17.0"
|
github "Carthage/Commandant" "0.17.0"
|
||||||
|
github "PaulTaykalo/SourceKitten" "76350746e111778cc9e5893d35ddbcabe5f366dd"
|
||||||
github "drmohundro/SWXMLHash" "5.0.1"
|
github "drmohundro/SWXMLHash" "5.0.1"
|
||||||
github "jpsim/SourceKitten" "0.28.0"
|
|
||||||
github "jpsim/Yams" "2.0.0"
|
github "jpsim/Yams" "2.0.0"
|
||||||
github "jspahrsummers/xcconfigs" "0.12"
|
github "jspahrsummers/xcconfigs" "0.12"
|
||||||
github "scottrhoyt/SwiftyTextTable" "0.9.0"
|
github "scottrhoyt/SwiftyTextTable" "0.9.0"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 97b5848e5692150d75b5cf0b81d7ebef5f4d5071
|
Subproject commit 003611a7f24b192ba86eba27e88b39718cd16eba
|
|
@ -33,8 +33,8 @@
|
||||||
"repositoryURL": "https://github.com/Quick/Nimble.git",
|
"repositoryURL": "https://github.com/Quick/Nimble.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "b02b00b30b6353632aa4a5fb6124f8147f7140c0",
|
"revision": "6abeb3f5c03beba2b9e4dbe20886e773b5b629b6",
|
||||||
"version": "8.0.5"
|
"version": "8.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -48,11 +48,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"package": "SourceKitten",
|
"package": "SourceKitten",
|
||||||
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
|
"repositoryURL": "https://github.com/PaulTaykalo/SourceKitten.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": "update/framework-api-visibility",
|
||||||
"revision": "97b5848e5692150d75b5cf0b81d7ebef5f4d5071",
|
"revision": "76350746e111778cc9e5893d35ddbcabe5f366dd",
|
||||||
"version": "0.28.0"
|
"version": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@ let package = Package(
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")),
|
.package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")),
|
||||||
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.28.0"),
|
.package(url: "https://github.com/PaulTaykalo/SourceKitten.git", .branch("update/framework-api-visibility")),
|
||||||
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"),
|
.package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"),
|
||||||
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),
|
.package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"),
|
||||||
] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []),
|
] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []),
|
||||||
|
|
|
@ -38,13 +38,19 @@ public struct SourceKittenDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Body length
|
/// Body length
|
||||||
var bodyLength: Int? {
|
var bodyLength: ByteCount? {
|
||||||
return (value["key.bodylength"] as? Int64).flatMap({ Int($0) })
|
return (value["key.bodylength"] as? Int64).map(ByteCount.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Body offset.
|
/// Body offset.
|
||||||
var bodyOffset: Int? {
|
var bodyOffset: ByteCount? {
|
||||||
return (value["key.bodyoffset"] as? Int64).flatMap({ Int($0) })
|
return (value["key.bodyoffset"] as? Int64).map(ByteCount.init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Body byte range.
|
||||||
|
var bodyByteRange: ByteRange? {
|
||||||
|
guard let offset = bodyOffset, let length = bodyLength else { return nil }
|
||||||
|
return ByteRange(location: offset, length: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kind.
|
/// Kind.
|
||||||
|
@ -53,8 +59,8 @@ public struct SourceKittenDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Length.
|
/// Length.
|
||||||
var length: Int? {
|
var length: ByteCount? {
|
||||||
return (value["key.length"] as? Int64).flatMap({ Int($0) })
|
return (value["key.length"] as? Int64).map(ByteCount.init)
|
||||||
}
|
}
|
||||||
/// Name.
|
/// Name.
|
||||||
var name: String? {
|
var name: String? {
|
||||||
|
@ -62,24 +68,30 @@ public struct SourceKittenDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Name length.
|
/// Name length.
|
||||||
var nameLength: Int? {
|
var nameLength: ByteCount? {
|
||||||
return (value["key.namelength"] as? Int64).flatMap({ Int($0) })
|
return (value["key.namelength"] as? Int64).map(ByteCount.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Name offset.
|
/// Name offset.
|
||||||
var nameOffset: Int? {
|
var nameOffset: ByteCount? {
|
||||||
return (value["key.nameoffset"] as? Int64).flatMap({ Int($0) })
|
return (value["key.nameoffset"] as? Int64).map(ByteCount.init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Byte range of name.
|
||||||
|
var nameByteRange: ByteRange? {
|
||||||
|
guard let offset = nameOffset, let length = nameLength else { return nil }
|
||||||
|
return ByteRange(location: offset, length: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offset.
|
/// Offset.
|
||||||
var offset: Int? {
|
var offset: ByteCount? {
|
||||||
return (value["key.offset"] as? Int64).flatMap({ Int($0) })
|
return (value["key.offset"] as? Int64).map(ByteCount.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns byte range starting from `offset` with `length` bytes
|
/// Returns byte range starting from `offset` with `length` bytes
|
||||||
var byteRange: NSRange? {
|
var byteRange: ByteRange? {
|
||||||
guard let offset = offset, let length = length else { return nil }
|
guard let offset = offset, let length = length else { return nil }
|
||||||
return NSRange(location: offset, length: length)
|
return ByteRange(location: offset, length: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setter accessibility.
|
/// Setter accessibility.
|
||||||
|
@ -93,13 +105,13 @@ public struct SourceKittenDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Documentation offset.
|
/// Documentation offset.
|
||||||
var docOffset: Int? {
|
var docOffset: ByteCount? {
|
||||||
return (value["key.docoffset"] as? Int64).flatMap({ Int($0) })
|
return (value["key.docoffset"] as? Int64).flatMap(ByteCount.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Documentation length.
|
/// Documentation length.
|
||||||
var docLength: Int? {
|
var docLength: ByteCount? {
|
||||||
return (value["key.doclength"] as? Int64).flatMap({ Int($0) })
|
return (value["key.doclength"] as? Int64).flatMap(ByteCount.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The attribute for this dictionary, as returned by SourceKit.
|
/// The attribute for this dictionary, as returned by SourceKit.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SourceKittenFramework
|
||||||
|
|
||||||
extension SourceKittenDictionary {
|
extension SourceKittenDictionary {
|
||||||
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure
|
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure
|
||||||
|
@ -7,20 +8,19 @@ extension SourceKittenDictionary {
|
||||||
/// - parameter byteOffset: Int?
|
/// - parameter byteOffset: Int?
|
||||||
///
|
///
|
||||||
/// - returns: The kinds and byte ranges.
|
/// - returns: The kinds and byte ranges.
|
||||||
internal func kinds(forByteOffset byteOffset: Int? = nil)
|
internal func kinds(forByteOffset byteOffset: ByteCount? = nil)
|
||||||
-> [(kind: String, byteRange: NSRange)] {
|
-> [(kind: String, byteRange: ByteRange)] {
|
||||||
var results = [(kind: String, byteRange: NSRange)]()
|
var results = [(kind: String, byteRange: ByteRange)]()
|
||||||
|
|
||||||
func parse(_ dictionary: SourceKittenDictionary) {
|
func parse(_ dictionary: SourceKittenDictionary) {
|
||||||
guard let offset = dictionary.offset,
|
guard let range = dictionary.byteRange else {
|
||||||
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }) else {
|
return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if let byteOffset = byteOffset, !NSLocationInRange(byteOffset, byteRange) {
|
if let byteOffset = byteOffset, !range.contains(byteOffset) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let kind = dictionary.kind {
|
if let kind = dictionary.kind {
|
||||||
results.append((kind: kind, byteRange: byteRange))
|
results.append((kind: kind, byteRange: range))
|
||||||
}
|
}
|
||||||
dictionary.substructure.forEach(parse)
|
dictionary.substructure.forEach(parse)
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,12 @@ extension SourceKittenDictionary {
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func structures(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
|
internal func structures(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
|
||||||
var results = [SourceKittenDictionary]()
|
var results = [SourceKittenDictionary]()
|
||||||
|
|
||||||
func parse(_ dictionary: SourceKittenDictionary) {
|
func parse(_ dictionary: SourceKittenDictionary) {
|
||||||
guard let offset = dictionary.offset,
|
guard let byteRange = dictionary.byteRange, byteRange.contains(byteOffset) else {
|
||||||
let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }),
|
return
|
||||||
NSLocationInRange(byteOffset, byteRange) else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.append(dictionary)
|
results.append(dictionary)
|
||||||
|
|
|
@ -109,8 +109,10 @@ extension SwiftLintFile {
|
||||||
let syntax = syntaxMap
|
let syntax = syntaxMap
|
||||||
return regex(pattern).matches(in: contents, options: [], range: range).map { match in
|
return regex(pattern).matches(in: contents, options: [], range: range).map { match in
|
||||||
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location,
|
let matchByteRange = contents.NSRangeToByteRange(start: match.range.location,
|
||||||
length: match.range.length) ?? match.range
|
length: match.range.length)
|
||||||
let tokensInRange = syntax.tokens(inByteRange: matchByteRange)
|
let fallbackRange = ByteRange(location: ByteCount(match.range.location),
|
||||||
|
length: ByteCount(match.range.length))
|
||||||
|
let tokensInRange = syntax.tokens(inByteRange: matchByteRange ?? fallbackRange)
|
||||||
return (match, tokensInRange)
|
return (match, tokensInRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,12 +145,11 @@ extension SwiftLintFile {
|
||||||
var maybeLine = lineIterator.next()
|
var maybeLine = lineIterator.next()
|
||||||
var maybeStructure = structureIterator.next()
|
var maybeStructure = structureIterator.next()
|
||||||
while let line = maybeLine, let structure = maybeStructure {
|
while let line = maybeLine, let structure = maybeStructure {
|
||||||
if NSLocationInRange(structure.byteRange.location, line.byteRange),
|
if line.byteRange.contains(structure.byteRange.location),
|
||||||
let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) {
|
let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) {
|
||||||
results[line.index].append(swiftDeclarationKind)
|
results[line.index].append(swiftDeclarationKind)
|
||||||
}
|
}
|
||||||
let lineEnd = NSMaxRange(line.byteRange)
|
if structure.byteRange.location >= line.byteRange.upperBound {
|
||||||
if structure.byteRange.location >= lineEnd {
|
|
||||||
maybeLine = lineIterator.next()
|
maybeLine = lineIterator.next()
|
||||||
} else {
|
} else {
|
||||||
maybeStructure = structureIterator.next()
|
maybeStructure = structureIterator.next()
|
||||||
|
@ -168,12 +169,12 @@ extension SwiftLintFile {
|
||||||
var maybeToken = tokenGenerator.next()
|
var maybeToken = tokenGenerator.next()
|
||||||
while let line = maybeLine, let token = maybeToken {
|
while let line = maybeLine, let token = maybeToken {
|
||||||
let tokenRange = token.range
|
let tokenRange = token.range
|
||||||
if NSLocationInRange(token.offset, line.byteRange) ||
|
if line.byteRange.contains(token.offset) ||
|
||||||
NSLocationInRange(line.byteRange.location, tokenRange) {
|
tokenRange.contains(line.byteRange.location) {
|
||||||
results[line.index].append(token)
|
results[line.index].append(token)
|
||||||
}
|
}
|
||||||
let tokenEnd = NSMaxRange(tokenRange)
|
let tokenEnd = tokenRange.upperBound
|
||||||
let lineEnd = NSMaxRange(line.byteRange)
|
let lineEnd = line.byteRange.upperBound
|
||||||
if tokenEnd < lineEnd {
|
if tokenEnd < lineEnd {
|
||||||
maybeToken = tokenGenerator.next()
|
maybeToken = tokenGenerator.next()
|
||||||
} else if tokenEnd > lineEnd {
|
} else if tokenEnd > lineEnd {
|
||||||
|
@ -337,6 +338,6 @@ extension SwiftLintFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func contents(for token: SwiftLintSyntaxToken) -> String? {
|
internal func contents(for token: SwiftLintSyntaxToken) -> String? {
|
||||||
return stringView.substringWithByteRange(start: token.offset, length: token.length)
|
return stringView.substringWithByteRange(token.range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct NamespaceCollector {
|
||||||
struct Element {
|
struct Element {
|
||||||
let name: String
|
let name: String
|
||||||
let kind: SwiftDeclarationKind
|
let kind: SwiftDeclarationKind
|
||||||
let offset: Int
|
let offset: ByteCount
|
||||||
let dictionary: SourceKittenDictionary
|
let dictionary: SourceKittenDictionary
|
||||||
|
|
||||||
init?(dictionary: SourceKittenDictionary, namespace: [String]) {
|
init?(dictionary: SourceKittenDictionary, namespace: [String]) {
|
||||||
|
|
|
@ -37,11 +37,11 @@ public struct Location: CustomStringConvertible, Comparable, Codable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `Location` based on a `SwiftLintFile` and a byte-offset into the file.
|
/// Creates a `Location` based on a `SwiftLintFile` and a byte-offset into the file.
|
||||||
/// Fails if tthe specified offset was not a valid location in the file.
|
/// Fails if the specified offset was not a valid location in the file.
|
||||||
///
|
///
|
||||||
/// - parameter file: The file for this location.
|
/// - parameter file: The file for this location.
|
||||||
/// - parameter offset: The offset in bytes into the file for this location.
|
/// - parameter offset: The offset in bytes into the file for this location.
|
||||||
public init(file: SwiftLintFile, byteOffset offset: Int) {
|
public init(file: SwiftLintFile, byteOffset offset: ByteCount) {
|
||||||
self.file = file.path
|
self.file = file.path
|
||||||
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
|
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
|
||||||
line = lineAndCharacter.line
|
line = lineAndCharacter.line
|
||||||
|
|
|
@ -22,7 +22,7 @@ public struct SwiftLintSyntaxMap {
|
||||||
/// - parameter byteRange: Byte-based NSRange.
|
/// - parameter byteRange: Byte-based NSRange.
|
||||||
///
|
///
|
||||||
/// - returns: The array of syntax tokens intersecting with byte range.
|
/// - returns: The array of syntax tokens intersecting with byte range.
|
||||||
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
|
internal func tokens(inByteRange byteRange: ByteRange) -> [SwiftLintSyntaxToken] {
|
||||||
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
|
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
|
||||||
return token.range.intersects(byteRange)
|
return token.range.intersects(byteRange)
|
||||||
}
|
}
|
||||||
|
@ -46,10 +46,39 @@ public struct SwiftLintSyntaxMap {
|
||||||
|
|
||||||
/// Returns the syntax kinds in the specified byte range.
|
/// Returns the syntax kinds in the specified byte range.
|
||||||
///
|
///
|
||||||
/// - parameter byteRange: Byte-based NSRange.
|
/// - parameter byteRange: Byte range.
|
||||||
///
|
///
|
||||||
/// - returns: The syntax kinds in the specified byte range.
|
/// - returns: The syntax kinds in the specified byte range.
|
||||||
internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
|
internal func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] {
|
||||||
return tokens(inByteRange: byteRange).compactMap { $0.kind }
|
return tokens(inByteRange: byteRange).compactMap { $0.kind }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move to SourceKitten
|
||||||
|
|
||||||
|
extension ByteRange {
|
||||||
|
func contains(_ value: ByteCount) -> Bool {
|
||||||
|
return location <= value && upperBound >= value
|
||||||
|
}
|
||||||
|
|
||||||
|
func intersects(_ otherRange: ByteRange) -> Bool {
|
||||||
|
return contains(otherRange.location) ||
|
||||||
|
contains(otherRange.location + otherRange.length) ||
|
||||||
|
otherRange.contains(location) ||
|
||||||
|
otherRange.contains(location + length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func intersects(_ ranges: [ByteRange]) -> Bool {
|
||||||
|
return ranges.contains { intersects($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func union(with otherRange: ByteRange) -> ByteRange {
|
||||||
|
let maxUpperBound = max(upperBound, otherRange.upperBound)
|
||||||
|
let minLocation = min(location, otherRange.location)
|
||||||
|
return ByteRange(location: minLocation, length: maxUpperBound - minLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
var upperBound: ByteCount {
|
||||||
|
return location + length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,17 +18,17 @@ public struct SwiftLintSyntaxToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The byte range in a source file for this token.
|
/// The byte range in a source file for this token.
|
||||||
public var range: NSRange {
|
public var range: ByteRange {
|
||||||
return NSRange(location: value.offset, length: value.length)
|
return value.range
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The starting byte offset in a source file for this token.
|
/// The starting byte offset in a source file for this token.
|
||||||
public var offset: Int {
|
public var offset: ByteCount {
|
||||||
return value.offset
|
return value.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The length in bytes for this token.
|
/// The length in bytes for this token.
|
||||||
public var length: Int {
|
public var length: ByteCount {
|
||||||
return value.length
|
return value.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,8 @@ public extension SwiftVersion {
|
||||||
let decl = file.structureDictionary.kinds()
|
let decl = file.structureDictionary.kinds()
|
||||||
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
|
.first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }),
|
||||||
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) {
|
let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) {
|
||||||
return .init(rawValue: file.contents.substring(from: token.offset + 1, length: token.length - 2))
|
let offsetRange = ByteRange(location: token.offset + 1, length: token.length - 2)
|
||||||
|
return .init(rawValue: file.stringView.substringWithByteRange(offsetRange)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
return .three
|
return .three
|
||||||
|
|
|
@ -36,7 +36,7 @@ extension CallPairRule {
|
||||||
let stringView = file.stringView
|
let stringView = file.stringView
|
||||||
let dictionary = file.structureDictionary
|
let dictionary = file.structureDictionary
|
||||||
|
|
||||||
let violatingLocations: [Int] = firstRanges.compactMap { range in
|
let violatingLocations: [ByteCount] = firstRanges.compactMap { range in
|
||||||
guard let bodyByteRange = stringView.NSRangeToByteRange(start: range.location, length: range.length),
|
guard let bodyByteRange = stringView.NSRangeToByteRange(start: range.location, length: range.length),
|
||||||
case let firstLocation = range.location + range.length - 1,
|
case let firstLocation = range.location + range.length - 1,
|
||||||
let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else {
|
let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else {
|
||||||
|
@ -63,18 +63,14 @@ extension CallPairRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func methodCall(forByteOffset byteOffset: Int, excludingOffset: Int,
|
private func methodCall(forByteOffset byteOffset: ByteCount, excludingOffset: ByteCount,
|
||||||
dictionary: SourceKittenDictionary,
|
dictionary: SourceKittenDictionary,
|
||||||
predicate: (SourceKittenDictionary) -> Bool) -> Int? {
|
predicate: (SourceKittenDictionary) -> Bool) -> ByteCount? {
|
||||||
if dictionary.expressionKind == .call,
|
if dictionary.expressionKind == .call, let byteRange = dictionary.byteRange {
|
||||||
let bodyOffset = dictionary.offset,
|
if byteRange.contains(byteOffset) &&
|
||||||
let bodyLength = dictionary.length,
|
!byteRange.contains(excludingOffset) &&
|
||||||
let offset = dictionary.offset {
|
predicate(dictionary) {
|
||||||
let byteRange = NSRange(location: bodyOffset, length: bodyLength)
|
return dictionary.offset
|
||||||
|
|
||||||
if NSLocationInRange(byteOffset, byteRange) &&
|
|
||||||
!NSLocationInRange(excludingOffset, byteRange) && predicate(dictionary) {
|
|
||||||
return offset
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,9 +97,9 @@ public struct ConvenienceTypeRule: ASTRule, OptInRule, ConfigurationProviderRule
|
||||||
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
||||||
return dictionary.swiftAttributes.contains { dict -> Bool in
|
return dictionary.swiftAttributes.contains { dict -> Bool in
|
||||||
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
|
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
|
||||||
let offset = dict.offset, let length = dict.length,
|
let contents = dict.byteRange.flatMap(file.stringView.substringWithByteRange)
|
||||||
let contents = file.stringView.substringWithByteRange(start: offset, length: length) else {
|
else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents.contains("unavailable")
|
return contents.contains("unavailable")
|
||||||
|
|
|
@ -31,7 +31,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
|
||||||
|
|
||||||
private func variableViolations(file: SwiftLintFile,
|
private func variableViolations(file: SwiftLintFile,
|
||||||
kind: SwiftDeclarationKind,
|
kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [Int] {
|
dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
guard
|
guard
|
||||||
SwiftDeclarationKind.variableKinds.contains(kind),
|
SwiftDeclarationKind.variableKinds.contains(kind),
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
|
@ -42,7 +42,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
|
||||||
|
|
||||||
private func functionViolations(file: SwiftLintFile,
|
private func functionViolations(file: SwiftLintFile,
|
||||||
kind: SwiftDeclarationKind,
|
kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [Int] {
|
dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
guard
|
guard
|
||||||
SwiftDeclarationKind.functionKinds.contains(kind),
|
SwiftDeclarationKind.functionKinds.contains(kind),
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
|
@ -51,8 +51,9 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
case let start = nameOffset + nameLength,
|
case let start = nameOffset + nameLength,
|
||||||
case let end = dictionary.bodyOffset ?? offset + length,
|
case let end = dictionary.bodyOffset ?? offset + length,
|
||||||
|
case let byteRange = ByteRange(location: start, length: end - start),
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: start, length: end - start),
|
let range = file.stringView.byteRangeToNSRange(byteRange),
|
||||||
let match = file.match(pattern: "->\\s*(.*?)\\{", excludingSyntaxKinds: excludingKinds, range: range).first
|
let match = file.match(pattern: "->\\s*(.*?)\\{", excludingSyntaxKinds: excludingKinds, range: range).first
|
||||||
else { return [] }
|
else { return [] }
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,18 @@ public struct DuplicateImportsRule: ConfigurationProviderRule, AutomaticTestable
|
||||||
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples
|
triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples
|
||||||
)
|
)
|
||||||
|
|
||||||
private func rangesInConditionalCompilation(file: SwiftLintFile) -> [NSRange] {
|
private func rangesInConditionalCompilation(file: SwiftLintFile) -> [ByteRange] {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
let ranges = file.syntaxMap.tokens
|
let ranges = file.syntaxMap.tokens
|
||||||
.filter { $0.kind == .buildconfigKeyword }
|
.filter { $0.kind == .buildconfigKeyword }
|
||||||
.map { $0.range }
|
.map { $0.range }
|
||||||
.filter { range in
|
.filter { range in
|
||||||
let keyword = contents.substringWithByteRange(start: range.location, length: range.length)
|
return ["#if", "#endif"].contains(contents.substringWithByteRange(range))
|
||||||
return ["#if", "#endif"].contains(keyword)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stride(from: 0, to: ranges.count, by: 2).reduce(into: []) { result, rangeIndex in
|
return stride(from: 0, to: ranges.count, by: 2).reduce(into: []) { result, rangeIndex in
|
||||||
result.append(NSUnionRange(ranges[rangeIndex], ranges[rangeIndex + 1]))
|
result.append(ranges[rangeIndex].union(with: ranges[rangeIndex + 1]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [NSRange] {
|
private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [ByteRange] {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap {
|
return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap {
|
||||||
contents.NSRangeToByteRange(start: $0.location, length: $0.length)
|
contents.NSRangeToByteRange(start: $0.location, length: $0.length)
|
||||||
|
@ -61,7 +61,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
|
||||||
}
|
}
|
||||||
|
|
||||||
private func offsetOfElements(from elements: [SourceKittenElement], in file: SwiftLintFile,
|
private func offsetOfElements(from elements: [SourceKittenElement], in file: SwiftLintFile,
|
||||||
thatAreNotInRanges ranges: [NSRange]) -> [Int] {
|
thatAreNotInRanges ranges: [ByteRange]) -> [ByteCount] {
|
||||||
let extensionKinds: Set<SwiftDeclarationKind> = [.extension, .extensionClass, .extensionEnum,
|
let extensionKinds: Set<SwiftDeclarationKind> = [.extension, .extensionClass, .extensionEnum,
|
||||||
.extensionProtocol, .extensionStruct]
|
.extensionProtocol, .extensionStruct]
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
|
||||||
// the "internal" token correspond to the type if there're only
|
// the "internal" token correspond to the type if there're only
|
||||||
// attributeBuiltin (`final` for example) tokens between them
|
// attributeBuiltin (`final` for example) tokens between them
|
||||||
let length = typeOffset - previousInternalByteRange.location
|
let length = typeOffset - previousInternalByteRange.location
|
||||||
let range = NSRange(location: previousInternalByteRange.location, length: length)
|
let range = ByteRange(location: previousInternalByteRange.location, length: length)
|
||||||
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
|
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
|
||||||
|
|
||||||
return internalDoesntBelongToType ? typeOffset : nil
|
return internalDoesntBelongToType ? typeOffset : nil
|
||||||
|
@ -109,7 +109,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? {
|
private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
|
||||||
let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
|
let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
|
||||||
return firstPartition.last
|
return firstPartition.last
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ public struct ExplicitEnumRawValueRule: ASTRule, OptInRule, ConfigurationProvide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] {
|
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
let locs = substructureElements(of: dictionary, matching: .enumcase)
|
let locs = substructureElements(of: dictionary, matching: .enumcase)
|
||||||
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
||||||
.flatMap(enumElementsMissingInitExpr)
|
.flatMap(enumElementsMissingInitExpr)
|
||||||
|
|
|
@ -71,14 +71,14 @@ public struct ExplicitInitRule: SubstitutionCorrectableASTRule, ConfigurationPro
|
||||||
&& initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0
|
&& initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = ".init".utf8.count
|
let length = ByteCount(".init".utf8.count)
|
||||||
|
|
||||||
guard kind == .call,
|
guard kind == .call,
|
||||||
let name = dictionary.name, isExpected(name),
|
let name = dictionary.name, isExpected(name),
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let range = file.stringView
|
let range = file.stringView
|
||||||
.byteRangeToNSRange(start: nameOffset + nameLength - length, length: length)
|
.byteRangeToNSRange(ByteRange(location: nameOffset + nameLength - length, length: length))
|
||||||
else { return [] }
|
else { return [] }
|
||||||
return [range]
|
return [range]
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
|
||||||
|
|
||||||
// find all top-level types marked as internal (either explictly or implictly)
|
// find all top-level types marked as internal (either explictly or implictly)
|
||||||
let dictionary = file.structureDictionary
|
let dictionary = file.structureDictionary
|
||||||
let internalTypesOffsets = dictionary.substructure.compactMap { element -> Int? in
|
let internalTypesOffsets = dictionary.substructure.compactMap { element -> ByteCount? in
|
||||||
// ignore extensions
|
// ignore extensions
|
||||||
guard let kind = element.declarationKind,
|
guard let kind = element.declarationKind,
|
||||||
!extensionKinds.contains(kind) else {
|
!extensionKinds.contains(kind) else {
|
||||||
|
@ -72,7 +72,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
|
||||||
// the "internal" token correspond to the type if there're only
|
// the "internal" token correspond to the type if there're only
|
||||||
// attributeBuiltin (`final` for example) tokens between them
|
// attributeBuiltin (`final` for example) tokens between them
|
||||||
let length = typeOffset - previousInternalByteRange.location
|
let length = typeOffset - previousInternalByteRange.location
|
||||||
let range = NSRange(location: previousInternalByteRange.location, length: length)
|
let range = ByteRange(location: previousInternalByteRange.location, length: length)
|
||||||
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
|
let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin]
|
||||||
|
|
||||||
return internalDoesntBelongToType
|
return internalDoesntBelongToType
|
||||||
|
@ -85,7 +85,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? {
|
private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
|
||||||
let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
|
let firstPartition = ranges.prefix(while: { typeOffset > $0.location })
|
||||||
return firstPartition.last
|
return firstPartition.last
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,8 @@ private extension SourceKittenDictionary {
|
||||||
guard
|
guard
|
||||||
let nameOffset = nameOffset,
|
let nameOffset = nameOffset,
|
||||||
let nameLength = nameLength,
|
let nameLength = nameLength,
|
||||||
let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0)
|
case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0),
|
||||||
|
let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange)
|
||||||
else {
|
else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -125,7 +126,8 @@ private extension SourceKittenDictionary {
|
||||||
guard
|
guard
|
||||||
let nameOffset = nameOffset,
|
let nameOffset = nameOffset,
|
||||||
let nameLength = nameLength,
|
let nameLength = nameLength,
|
||||||
let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0)
|
case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0),
|
||||||
|
let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange)
|
||||||
else {
|
else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -137,11 +139,11 @@ private extension SourceKittenDictionary {
|
||||||
return typeAssignment.firstMatch(in: contentAfterName, options: [], range: contentAfterName.fullNSRange) != nil
|
return typeAssignment.firstMatch(in: contentAfterName, options: [], range: contentAfterName.fullNSRange) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var caseStatementPatternRanges: [NSRange] {
|
var caseStatementPatternRanges: [ByteRange] {
|
||||||
return ranges(with: StatementKind.case.rawValue, for: "source.lang.swift.structure.elem.pattern")
|
return ranges(with: StatementKind.case.rawValue, for: "source.lang.swift.structure.elem.pattern")
|
||||||
}
|
}
|
||||||
|
|
||||||
var caseExpressionRanges: [NSRange] {
|
var caseExpressionRanges: [ByteRange] {
|
||||||
return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr")
|
return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,30 +154,27 @@ private extension SourceKittenDictionary {
|
||||||
return statements.contains(statement)
|
return statements.contains(statement)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ranges(with parentKind: String, for elementKind: String) -> [NSRange] {
|
func ranges(with parentKind: String, for elementKind: String) -> [ByteRange] {
|
||||||
guard parentKind == kind else {
|
guard parentKind == kind else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements
|
return elements
|
||||||
.filter { elementKind == $0.kind }
|
.filter { elementKind == $0.kind }
|
||||||
.compactMap {
|
.compactMap { $0.byteRange }
|
||||||
guard let location = $0.offset, let length = $0.length else { return nil }
|
|
||||||
return NSRange(location: location, length: length)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SwiftLintFile {
|
private extension SwiftLintFile {
|
||||||
var captureGroupByteRanges: [NSRange] {
|
var captureGroupByteRanges: [ByteRange] {
|
||||||
return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]",
|
return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]",
|
||||||
excludingSyntaxKinds: SyntaxKind.commentKinds)
|
excludingSyntaxKinds: SyntaxKind.commentKinds)
|
||||||
.compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) }
|
.compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Collection where Element == NSRange {
|
private extension Collection where Element == ByteRange {
|
||||||
func contains(_ index: Int) -> Bool {
|
func contains(_ index: ByteCount) -> Bool {
|
||||||
return contains { $0.contains(index) }
|
return contains { $0.contains(index) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,20 +79,23 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
|
||||||
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
|
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
guard kind == .extension, let offset = dictionary.offset,
|
guard kind == .extension, let offset = dictionary.offset,
|
||||||
dictionary.inheritedTypes.isEmpty else {
|
dictionary.inheritedTypes.isEmpty
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let declarations = dictionary.substructure.compactMap { entry -> (acl: AccessControlLevel, offset: Int)? in
|
let declarations = dictionary.substructure
|
||||||
guard entry.declarationKind != nil,
|
.compactMap { entry -> (acl: AccessControlLevel, offset: ByteCount)? in
|
||||||
let acl = entry.accessibility,
|
guard entry.declarationKind != nil,
|
||||||
let offset = entry.offset else {
|
let acl = entry.accessibility,
|
||||||
return nil
|
let offset = entry.offset
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return (acl: acl, offset: offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (acl: acl, offset: offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
let declarationsACLs = declarations.map { $0.acl }.unique
|
let declarationsACLs = declarations.map { $0.acl }.unique
|
||||||
let allowedACLs: Set<AccessControlLevel> = [.internal, .private, .open]
|
let allowedACLs: Set<AccessControlLevel> = [.internal, .private, .open]
|
||||||
guard declarationsACLs.count == 1, !allowedACLs.contains(declarationsACLs[0]) else {
|
guard declarationsACLs.count == 1, !allowedACLs.contains(declarationsACLs[0]) else {
|
||||||
|
@ -115,11 +118,11 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
|
||||||
}
|
}
|
||||||
|
|
||||||
private func declarationsViolations(file: SwiftLintFile, acl: AccessControlLevel,
|
private func declarationsViolations(file: SwiftLintFile, acl: AccessControlLevel,
|
||||||
declarationOffsets: [Int],
|
declarationOffsets: [ByteCount],
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
guard let offset = dictionary.offset, let length = dictionary.length,
|
guard let byteRange = dictionary.byteRange,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
let range = contents.byteRangeToNSRange(byteRange) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +141,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
|
||||||
// the ACL token correspond to the type if there're only
|
// the ACL token correspond to the type if there're only
|
||||||
// attributeBuiltin (`final` for example) tokens between them
|
// attributeBuiltin (`final` for example) tokens between them
|
||||||
let length = typeOffset - previousInternalByteRange.location
|
let length = typeOffset - previousInternalByteRange.location
|
||||||
let range = NSRange(location: previousInternalByteRange.location, length: length)
|
let range = ByteRange(location: previousInternalByteRange.location, length: length)
|
||||||
let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin]
|
let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin]
|
||||||
|
|
||||||
return internalBelongsToType
|
return internalBelongsToType
|
||||||
|
@ -151,7 +154,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lastACLByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? {
|
private func lastACLByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? {
|
||||||
let firstPartition = ranges.partitioned(by: { $0.location > typeOffset }).first
|
let firstPartition = ranges.partitioned(by: { $0.location > typeOffset }).first
|
||||||
return firstPartition.last
|
return firstPartition.last
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,16 +53,15 @@ public struct FatalErrorMessageRule: ASTRule, ConfigurationProviderRule, OptInRu
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hasEmptyBody(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
private func hasEmptyBody(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
||||||
guard let bodyOffset = dictionary.bodyOffset,
|
guard let bodyRange = dictionary.bodyByteRange else {
|
||||||
let bodyLength = dictionary.bodyLength else {
|
return false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if bodyLength == 0 {
|
if bodyRange.length == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength)
|
let body = file.stringView.substringWithByteRange(bodyRange)
|
||||||
return body == "\"\""
|
return body == "\"\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,9 +133,9 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let beforeIfRange = NSRange(location: offset, length: ifOffset - offset)
|
let beforeIfRange = ByteRange(location: offset, length: ifOffset - offset)
|
||||||
let ifFinalPosition = ifOffset + ifLength
|
let ifFinalPosition = ifOffset + ifLength
|
||||||
let afterIfRange = NSRange(location: ifFinalPosition, length: offset + length - ifFinalPosition)
|
let afterIfRange = ByteRange(location: ifFinalPosition, length: offset + length - ifFinalPosition)
|
||||||
let allKinds = file.syntaxMap.kinds(inByteRange: beforeIfRange) +
|
let allKinds = file.syntaxMap.kinds(inByteRange: beforeIfRange) +
|
||||||
file.syntaxMap.kinds(inByteRange: afterIfRange)
|
file.syntaxMap.kinds(inByteRange: afterIfRange)
|
||||||
|
|
||||||
|
@ -148,13 +148,11 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl
|
||||||
|
|
||||||
private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
||||||
let kind = "source.lang.swift.structure.elem.condition_expr"
|
let kind = "source.lang.swift.structure.elem.condition_expr"
|
||||||
let contents = file.stringView
|
|
||||||
return dictionary.elements.contains { element in
|
return dictionary.elements.contains { element in
|
||||||
guard element.kind == kind,
|
guard element.kind == kind,
|
||||||
let offset = element.offset,
|
let range = element.byteRange.flatMap(file.stringView.byteRangeToNSRange)
|
||||||
let length = element.length,
|
else {
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
return false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let containsKeyword = !file.match(pattern: "\\blet|var|case\\b", with: [.keyword], range: range).isEmpty
|
let containsKeyword = !file.match(pattern: "\\blet|var|case\\b", with: [.keyword], range: range).isEmpty
|
||||||
|
|
|
@ -152,7 +152,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`.
|
// check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`.
|
||||||
private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SwiftLintSyntaxMap,
|
private func isFirstRangeExcludedToken(byteRange: ByteRange, syntaxMap: SwiftLintSyntaxMap,
|
||||||
file: SwiftLintFile) -> Bool {
|
file: SwiftLintFile) -> Bool {
|
||||||
let tokens = syntaxMap.tokens(inByteRange: byteRange)
|
let tokens = syntaxMap.tokens(inByteRange: byteRange)
|
||||||
return tokens.contains { token in
|
return tokens.contains { token in
|
||||||
|
@ -166,7 +166,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
|
||||||
}
|
}
|
||||||
|
|
||||||
// check deepest kind matching range in structure is a typeAnnotation
|
// check deepest kind matching range in structure is a typeAnnotation
|
||||||
private func isTypeAnnotation(in file: SwiftLintFile, byteRange: NSRange) -> Bool {
|
private func isTypeAnnotation(in file: SwiftLintFile, byteRange: ByteRange) -> Bool {
|
||||||
let kinds = file.structureDictionary.kinds(forByteOffset: byteRange.location)
|
let kinds = file.structureDictionary.kinds(forByteOffset: byteRange.location)
|
||||||
guard let lastItem = kinds.last,
|
guard let lastItem = kinds.last,
|
||||||
let lastKind = SwiftDeclarationKind(rawValue: lastItem.kind),
|
let lastKind = SwiftDeclarationKind(rawValue: lastItem.kind),
|
||||||
|
@ -175,9 +175,9 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat
|
||||||
}
|
}
|
||||||
|
|
||||||
// range is in some "source.lang.swift.decl.var.*"
|
// range is in some "source.lang.swift.decl.var.*"
|
||||||
let byteOffset = lastItem.byteRange.location
|
let varRange = ByteRange(location: lastItem.byteRange.location,
|
||||||
let byteLength = byteRange.location - byteOffset
|
length: byteRange.location - lastItem.byteRange.location)
|
||||||
if let varDeclarationString = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength),
|
if let varDeclarationString = file.stringView.substringWithByteRange(varRange),
|
||||||
varDeclarationString.contains("=") {
|
varDeclarationString.contains("=") {
|
||||||
// if declarations contains "=", range is not type annotation
|
// if declarations contains "=", range is not type annotation
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -97,10 +97,8 @@ public struct FunctionDefaultParameterAtEndRule: ASTRule, ConfigurationProviderR
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isDefaultParameter(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
private func isDefaultParameter(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
||||||
let contents = file.stringView
|
guard let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange) else {
|
||||||
guard let offset = dictionary.offset, let length = dictionary.length,
|
return false
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil
|
return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil
|
||||||
|
|
|
@ -71,15 +71,16 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
} else {
|
} else {
|
||||||
guard kind == .genericTypeParam,
|
guard kind == .genericTypeParam,
|
||||||
let name = dictionary.name,
|
let name = dictionary.name,
|
||||||
let offset = dictionary.offset else {
|
let offset = dictionary.offset
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return validate(name: name, file: file, offset: offset)
|
return validate(name: name, file: file, offset: offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validate(name: String, file: SwiftLintFile, offset: Int) -> [StyleViolation] {
|
private func validate(name: String, file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] {
|
||||||
guard !configuration.excluded.contains(name) else {
|
guard !configuration.excluded.contains(name) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ extension GenericTypeNameRule {
|
||||||
|
|
||||||
private func validateGenericTypeAliases(in file: SwiftLintFile) -> [StyleViolation] {
|
private func validateGenericTypeAliases(in file: SwiftLintFile) -> [StyleViolation] {
|
||||||
let pattern = "typealias\\s+\\w+?\\s*" + type(of: self).genericTypePattern + "\\s*="
|
let pattern = "typealias\\s+\\w+?\\s*" + type(of: self).genericTypePattern + "\\s*="
|
||||||
return file.match(pattern: pattern).flatMap { range, tokens -> [(String, Int)] in
|
return file.match(pattern: pattern).flatMap { range, tokens -> [(String, ByteCount)] in
|
||||||
guard tokens.first == .keyword,
|
guard tokens.first == .keyword,
|
||||||
Set(tokens.dropFirst()) == [.identifier],
|
Set(tokens.dropFirst()) == [.identifier],
|
||||||
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
|
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
|
||||||
|
@ -136,7 +137,7 @@ extension GenericTypeNameRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func genericTypesForType(in file: SwiftLintFile, kind: SwiftDeclarationKind,
|
private func genericTypesForType(in file: SwiftLintFile, kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [(String, Int)] {
|
dictionary: SourceKittenDictionary) -> [(String, ByteCount)] {
|
||||||
guard SwiftDeclarationKind.typeKinds.contains(kind),
|
guard SwiftDeclarationKind.typeKinds.contains(kind),
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
|
@ -144,7 +145,8 @@ extension GenericTypeNameRule {
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
case let start = nameOffset + nameLength,
|
case let start = nameOffset + nameLength,
|
||||||
case let length = bodyOffset - start,
|
case let length = bodyOffset - start,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: start, length: length),
|
case let byteRange = ByteRange(location: start, length: length),
|
||||||
|
let range = file.stringView.byteRangeToNSRange(byteRange),
|
||||||
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
|
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [],
|
||||||
range: range)?.range(at: 1) else {
|
range: range)?.range(at: 1) else {
|
||||||
return []
|
return []
|
||||||
|
@ -155,16 +157,18 @@ extension GenericTypeNameRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func genericTypesForFunction(in file: SwiftLintFile, kind: SwiftDeclarationKind,
|
private func genericTypesForFunction(in file: SwiftLintFile, kind: SwiftDeclarationKind,
|
||||||
dictionary: SourceKittenDictionary) -> [(String, Int)] {
|
dictionary: SourceKittenDictionary) -> [(String, ByteCount)] {
|
||||||
guard SwiftDeclarationKind.functionKinds.contains(kind),
|
guard SwiftDeclarationKind.functionKinds.contains(kind),
|
||||||
let offset = dictionary.nameOffset,
|
let offset = dictionary.nameOffset,
|
||||||
let length = dictionary.nameLength,
|
let length = dictionary.nameLength,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length),
|
case let byteRange = ByteRange(location: offset, length: length),
|
||||||
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents,
|
let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents,
|
||||||
options: [], range: range)?.range(at: 1),
|
options: [], range: range)?.range(at: 1),
|
||||||
match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file) else {
|
match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file)
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let genericConstraint = contents.substring(with: match)
|
let genericConstraint = contents.substring(with: match)
|
||||||
|
@ -174,7 +178,7 @@ extension GenericTypeNameRule {
|
||||||
private func minParameterOffset(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> Int {
|
private func minParameterOffset(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> Int {
|
||||||
let offsets = parameters.compactMap { param -> Int? in
|
let offsets = parameters.compactMap { param -> Int? in
|
||||||
return param.offset.flatMap {
|
return param.offset.flatMap {
|
||||||
file.stringView.byteRangeToNSRange(start: $0, length: 0)?.location
|
file.stringView.byteRangeToNSRange(ByteRange(location: $0, length: 0))?.location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +186,7 @@ extension GenericTypeNameRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func extractTypes(fromGenericConstraint constraint: String, offset: Int,
|
private func extractTypes(fromGenericConstraint constraint: String, offset: Int,
|
||||||
file: SwiftLintFile) -> [(String, Int)] {
|
file: SwiftLintFile) -> [(String, ByteCount)] {
|
||||||
guard let beforeWhere = constraint.components(separatedBy: "where").first else {
|
guard let beforeWhere = constraint.components(separatedBy: "where").first else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -196,7 +200,7 @@ extension GenericTypeNameRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return namesAndRanges.compactMap { name, range -> (String, Int)? in
|
return namesAndRanges.compactMap { name, range -> (String, ByteCount)? in
|
||||||
guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset,
|
guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset,
|
||||||
length: range.length),
|
length: range.length),
|
||||||
file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else {
|
file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else {
|
||||||
|
@ -228,8 +232,9 @@ private extension String {
|
||||||
let bridged = bridge()
|
let bridged = bridge()
|
||||||
let range = NSRange(location: 0, length: bridged.length)
|
let range = NSRange(location: 0, length: bridged.length)
|
||||||
guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range),
|
guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range),
|
||||||
NSEqualRanges(range, match.range) else {
|
NSEqualRanges(range, match.range)
|
||||||
return (self, range)
|
else {
|
||||||
|
return (self, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
let trimmedRange = match.range(at: 1)
|
let trimmedRange = match.range(at: 1)
|
||||||
|
|
|
@ -70,23 +70,18 @@ public struct JoinedDefaultParameterRule: SubstitutionCorrectableASTRule, Config
|
||||||
guard
|
guard
|
||||||
// is this single argument called 'separator'?
|
// is this single argument called 'separator'?
|
||||||
let argument = dictionary.enclosedArguments.first,
|
let argument = dictionary.enclosedArguments.first,
|
||||||
let offset = argument.offset,
|
let argumentByteRange = argument.byteRange,
|
||||||
let length = argument.length,
|
argument.name == "separator",
|
||||||
argument.name == "separator"
|
let argumentNSRange = file.stringView.byteRangeToNSRange(argumentByteRange)
|
||||||
else { return [] }
|
else { return [] }
|
||||||
|
|
||||||
guard
|
guard
|
||||||
// is this single argument the default parameter?
|
// is this single argument the default parameter?
|
||||||
let bodyOffset = argument.bodyOffset,
|
let bodyRange = argument.bodyByteRange,
|
||||||
let bodyLength = argument.bodyLength,
|
let body = file.stringView.substringWithByteRange(bodyRange),
|
||||||
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength),
|
|
||||||
body == "\"\""
|
body == "\"\""
|
||||||
else { return [] }
|
else { return [] }
|
||||||
|
|
||||||
guard
|
return [argumentNSRange]
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length)
|
|
||||||
else { return [] }
|
|
||||||
|
|
||||||
return [range]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,8 +181,8 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv
|
||||||
var adjustedLocations = [Int]()
|
var adjustedLocations = [Int]()
|
||||||
|
|
||||||
for dictionary in violatingDictionaries.reversed() {
|
for dictionary in violatingDictionaries.reversed() {
|
||||||
guard let offset = dictionary.offset, let length = dictionary.length,
|
guard let byteRange = dictionary.byteRange,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length),
|
let range = file.stringView.byteRangeToNSRange(byteRange),
|
||||||
let name = dictionary.name,
|
let name = dictionary.name,
|
||||||
let correctedName = type(of: self).constructorsToCorrectedNames[name],
|
let correctedName = type(of: self).constructorsToCorrectedNames[name],
|
||||||
file.ruleEnabled(violatingRanges: [range], for: self) == [range],
|
file.ruleEnabled(violatingRanges: [range], for: self) == [range],
|
||||||
|
@ -213,13 +213,11 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv
|
||||||
private func argumentsContents(file: SwiftLintFile, arguments: [SourceKittenDictionary]) -> [String] {
|
private func argumentsContents(file: SwiftLintFile, arguments: [SourceKittenDictionary]) -> [String] {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return arguments.compactMap { argument -> String? in
|
return arguments.compactMap { argument -> String? in
|
||||||
guard argument.name == nil,
|
guard argument.name == nil, let byteRange = argument.byteRange else {
|
||||||
let offset = argument.offset,
|
return nil
|
||||||
let length = argument.length else {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents.substringWithByteRange(start: offset, length: length)
|
return contents.substringWithByteRange(byteRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,9 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
|
||||||
kind: StatementKind,
|
kind: StatementKind,
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
guard kind == .case,
|
guard kind == .case,
|
||||||
let length = dictionary.length,
|
let byteRange = dictionary.byteRange,
|
||||||
let offset = dictionary.offset,
|
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length),
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
let colonLocation = findCaseColon(text: file.stringView.nsString, range: range)
|
let colonLocation = findCaseColon(text: file.stringView.nsString, range: range)
|
||||||
else {
|
else {
|
||||||
return []
|
return []
|
||||||
|
@ -40,7 +39,7 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
|
||||||
|
|
||||||
let nsRange = nonCommentCaseBody[0].0
|
let nsRange = nonCommentCaseBody[0].0
|
||||||
if contents.substring(with: nsRange) == "fallthrough" && nonCommentCaseBody[0].1 == [.keyword] &&
|
if contents.substring(with: nsRange) == "fallthrough" && nonCommentCaseBody[0].1 == [.keyword] &&
|
||||||
!isNextTokenUnknownAttribute(afterOffset: offset + length, file: file) {
|
!isNextTokenUnknownAttribute(afterOffset: byteRange.upperBound, file: file) {
|
||||||
return [StyleViolation(ruleDescription: type(of: self).description,
|
return [StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.severity,
|
severity: configuration.severity,
|
||||||
location: Location(file: file, characterOffset: nsRange.location))]
|
location: Location(file: file, characterOffset: nsRange.location))]
|
||||||
|
@ -49,13 +48,12 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isNextTokenUnknownAttribute(afterOffset offset: Int, file: SwiftLintFile) -> Bool {
|
private func isNextTokenUnknownAttribute(afterOffset offset: ByteCount, file: SwiftLintFile) -> Bool {
|
||||||
let nextNonCommentToken = file.syntaxMap.tokens
|
let nextNonCommentToken = file.syntaxMap.tokens
|
||||||
.first { token in
|
.first { token in
|
||||||
guard let kind = token.kind, !kind.isCommentLike else {
|
guard let kind = token.kind, !kind.isCommentLike else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return token.offset > offset
|
return token.offset > offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,18 +45,14 @@ public struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule, Aut
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hasWhereClause(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
private func hasWhereClause(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
||||||
let contents = file.stringView
|
|
||||||
|
|
||||||
guard let nameOffset = dictionary.nameOffset,
|
guard let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let bodyOffset = dictionary.bodyOffset else {
|
let bodyOffset = dictionary.bodyOffset,
|
||||||
return false
|
case let contents = file.stringView,
|
||||||
}
|
case let rangeStart = nameOffset + nameLength,
|
||||||
|
case let rangeLength = bodyOffset - rangeStart,
|
||||||
let rangeStart = nameOffset + nameLength
|
let range = contents.byteRangeToNSRange(ByteRange(location: rangeStart, length: rangeLength))
|
||||||
let rangeLength = bodyOffset - rangeStart
|
else {
|
||||||
|
|
||||||
guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,11 +94,6 @@ public struct ObjectLiteralRule: ASTRule, ConfigurationProviderRule, OptInRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func kinds(forArgument argument: SourceKittenDictionary, file: SwiftLintFile) -> Set<SyntaxKind> {
|
private func kinds(forArgument argument: SourceKittenDictionary, file: SwiftLintFile) -> Set<SyntaxKind> {
|
||||||
guard let offset = argument.bodyOffset, let length = argument.bodyLength else {
|
return argument.bodyByteRange.map { Set(file.syntaxMap.kinds(inByteRange: $0)) } ?? []
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
let range = NSRange(location: offset, length: length)
|
|
||||||
return Set(file.syntaxMap.kinds(inByteRange: range))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,10 @@ public struct PatternMatchingKeywordsRule: ASTRule, ConfigurationProviderRule, O
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in
|
return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in
|
||||||
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
||||||
let offset = subDictionary.offset,
|
let caseByteRange = subDictionary.byteRange,
|
||||||
let length = subDictionary.length,
|
let caseRange = contents.byteRangeToNSRange(caseByteRange)
|
||||||
let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else {
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange)
|
let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange)
|
||||||
|
|
|
@ -81,10 +81,11 @@ public struct PrivateOverFilePrivateRule: ConfigurationProviderRule, Substitutio
|
||||||
let parts = syntaxTokens.prefix { offset > $0.offset }
|
let parts = syntaxTokens.prefix { offset > $0.offset }
|
||||||
guard let lastKind = parts.last,
|
guard let lastKind = parts.last,
|
||||||
lastKind.kind == .attributeBuiltin,
|
lastKind.kind == .attributeBuiltin,
|
||||||
let aclName = contents.substringWithByteRange(start: lastKind.offset, length: lastKind.length),
|
let aclName = contents.substringWithByteRange(lastKind.range),
|
||||||
AccessControlLevel(description: aclName) == .fileprivate,
|
AccessControlLevel(description: aclName) == .fileprivate,
|
||||||
let range = contents.byteRangeToNSRange(start: lastKind.offset, length: lastKind.length) else {
|
let range = contents.byteRangeToNSRange(lastKind.range)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return range
|
return range
|
||||||
|
|
|
@ -41,10 +41,10 @@ public struct RedundantObjcAttributeRule: SubstitutionCorrectableRule, Configura
|
||||||
parentStructure: SourceKittenDictionary?) -> [NSRange] {
|
parentStructure: SourceKittenDictionary?) -> [NSRange] {
|
||||||
let objcAttribute = dictionary.swiftAttributes
|
let objcAttribute = dictionary.swiftAttributes
|
||||||
.first(where: { $0.attribute == SwiftDeclarationAttributeKind.objc.rawValue })
|
.first(where: { $0.attribute == SwiftDeclarationAttributeKind.objc.rawValue })
|
||||||
guard let objcOffset = objcAttribute?.offset,
|
guard let objcByteRange = objcAttribute?.byteRange,
|
||||||
let objcLength = objcAttribute?.length,
|
let range = file.stringView.byteRangeToNSRange(objcByteRange),
|
||||||
let range = file.stringView.byteRangeToNSRange(start: objcOffset, length: objcLength),
|
!dictionary.isObjcAndIBDesignableDeclaredExtension
|
||||||
!dictionary.isObjcAndIBDesignableDeclaredExtension else {
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,9 +127,9 @@ public struct RedundantOptionalInitializationRule: SubstitutionCorrectableASTRul
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
if let bodyOffset = dictionary.bodyOffset {
|
if let bodyOffset = dictionary.bodyOffset {
|
||||||
return contents.byteRangeToNSRange(start: offset, length: bodyOffset - offset)
|
return contents.byteRangeToNSRange(ByteRange(location: offset, length: bodyOffset - offset))
|
||||||
} else {
|
} else {
|
||||||
return contents.byteRangeToNSRange(start: offset, length: length)
|
return contents.byteRangeToNSRange(ByteRange(location: offset, length: length))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ extension SourceKittenDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isVariable(file: SwiftLintFile) -> Bool {
|
private func isVariable(file: SwiftLintFile) -> Bool {
|
||||||
guard let start = offset, let length = length,
|
guard let byteRange = byteRange,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: start, length: length),
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
!file.match(pattern: "\\Avar\\b", with: [.keyword], range: range).isEmpty else {
|
!file.match(pattern: "\\Avar\\b", with: [.keyword], range: range).isEmpty else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,9 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] {
|
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] {
|
||||||
var caseCount = 0
|
var caseCount = 0
|
||||||
var violations = [Int]()
|
var violations = [ByteCount]()
|
||||||
|
|
||||||
for enumCase in children(of: dictionary, matching: .enumcase) {
|
for enumCase in children(of: dictionary, matching: .enumcase) {
|
||||||
caseCount += enumElementsCount(dictionary: enumCase)
|
caseCount += enumElementsCount(dictionary: enumCase)
|
||||||
|
@ -112,8 +112,8 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
|
||||||
}).count
|
}).count
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] {
|
private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] {
|
||||||
return children(of: dictionary, matching: .enumelement).flatMap { element -> [Int] in
|
return children(of: dictionary, matching: .enumelement).flatMap { element -> [ByteCount] in
|
||||||
guard let name = element.name else {
|
guard let name = element.name else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -122,18 +122,18 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violatingOffsetsForEnumElement(dictionary: SourceKittenDictionary, name: String,
|
private func violatingOffsetsForEnumElement(dictionary: SourceKittenDictionary, name: String,
|
||||||
file: SwiftLintFile) -> [Int] {
|
file: SwiftLintFile) -> [ByteCount] {
|
||||||
let enumInits = filterEnumInits(dictionary: dictionary)
|
let enumInits = filterEnumInits(dictionary: dictionary)
|
||||||
|
|
||||||
return enumInits.compactMap { dictionary -> Int? in
|
return enumInits.compactMap { dictionary -> ByteCount? in
|
||||||
guard let offset = dictionary.offset,
|
guard let offset = dictionary.offset,
|
||||||
let length = dictionary.length else {
|
let length = dictionary.length else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// the string would be quoted if offset and length were used directly
|
// the string would be quoted if offset and length were used directly
|
||||||
let enumCaseName = file.stringView
|
let rangeWithoutQuotes = ByteRange(location: offset + 1, length: length - 2)
|
||||||
.substringWithByteRange(start: offset + 1, length: length - 2) ?? ""
|
let enumCaseName = file.stringView.substringWithByteRange(rangeWithoutQuotes) ?? ""
|
||||||
guard enumCaseName == name else {
|
guard enumCaseName == name else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,8 @@ public struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCo
|
||||||
case let start = nameOffset + nameLength,
|
case let start = nameOffset + nameLength,
|
||||||
case let end = dictionary.bodyOffset ?? offset + length,
|
case let end = dictionary.bodyOffset ?? offset + length,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: start, length: end - start),
|
case let byteRange = ByteRange(location: start, length: end - start),
|
||||||
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
file.match(pattern: "->", excludingSyntaxKinds: excludingKinds, range: range).count == 1,
|
file.match(pattern: "->", excludingSyntaxKinds: excludingKinds, range: range).count == 1,
|
||||||
let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else {
|
let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else {
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -61,7 +61,7 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validate(name: String, dictionary: SourceKittenDictionary = SourceKittenDictionary([:]),
|
private func validate(name: String, dictionary: SourceKittenDictionary = SourceKittenDictionary([:]),
|
||||||
file: SwiftLintFile, offset: Int) -> [StyleViolation] {
|
file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] {
|
||||||
guard !configuration.excluded.contains(name) else {
|
guard !configuration.excluded.contains(name) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn
|
||||||
|
|
||||||
guard let offset = dictionary.offset, containsFatalError,
|
guard let offset = dictionary.offset, containsFatalError,
|
||||||
!isFunctionUnavailable(file: file, dictionary: dictionary),
|
!isFunctionUnavailable(file: file, dictionary: dictionary),
|
||||||
let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength,
|
let bodyRange = dictionary.bodyByteRange,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: bodyOffset, length: bodyLength),
|
let range = file.stringView.byteRangeToNSRange(bodyRange),
|
||||||
file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else {
|
file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn
|
||||||
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
|
||||||
return dictionary.swiftAttributes.contains { dict -> Bool in
|
return dictionary.swiftAttributes.contains { dict -> Bool in
|
||||||
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
|
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
|
||||||
let offset = dict.offset, let length = dict.length,
|
let byteRange = dict.byteRange,
|
||||||
let contents = file.stringView.substringWithByteRange(start: offset, length: length) else {
|
let contents = file.stringView.substringWithByteRange(byteRange) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,13 +38,11 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes
|
||||||
guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
|
guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
|
||||||
let innerStructure = file.structureDictionary.structures(forByteOffset: byteRange.location).last,
|
let innerStructure = file.structureDictionary.structures(forByteOffset: byteRange.location).last,
|
||||||
innerStructure.statementKind == .case,
|
innerStructure.statementKind == .case,
|
||||||
let caseOffset = innerStructure.offset,
|
let caseRange = innerStructure.byteRange,
|
||||||
let caseLength = innerStructure.length,
|
|
||||||
let lastPatternEnd = patternEnd(dictionary: innerStructure) else {
|
let lastPatternEnd = patternEnd(dictionary: innerStructure) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let caseRange = NSRange(location: caseOffset, length: caseLength)
|
|
||||||
let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in
|
let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in
|
||||||
guard let kind = token.kind,
|
guard let kind = token.kind,
|
||||||
token.offset > lastPatternEnd else {
|
token.offset > lastPatternEnd else {
|
||||||
|
@ -73,8 +71,8 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func patternEnd(dictionary: SourceKittenDictionary) -> Int? {
|
private func patternEnd(dictionary: SourceKittenDictionary) -> ByteCount? {
|
||||||
let patternEnds = dictionary.elements.compactMap { subDictionary -> Int? in
|
let patternEnds = dictionary.elements.compactMap { subDictionary -> ByteCount? in
|
||||||
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
||||||
let offset = subDictionary.offset,
|
let offset = subDictionary.offset,
|
||||||
let length = subDictionary.length else {
|
let length = subDictionary.length else {
|
||||||
|
|
|
@ -44,7 +44,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset: Int
|
let offset: ByteCount
|
||||||
let reason: String
|
let reason: String
|
||||||
if firstTokenIsUnderscore {
|
if firstTokenIsUnderscore {
|
||||||
offset = tokens[0].offset
|
offset = tokens[0].offset
|
||||||
|
@ -83,7 +83,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> NSRange? {
|
private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> ByteRange? {
|
||||||
let expectedKind = "source.lang.swift.structure.elem.id"
|
let expectedKind = "source.lang.swift.structure.elem.id"
|
||||||
for subDict in dictionary.elements where subDict.kind == expectedKind {
|
for subDict in dictionary.elements where subDict.kind == expectedKind {
|
||||||
guard let offset = subDict.offset,
|
guard let offset = subDict.offset,
|
||||||
|
@ -91,7 +91,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return NSRange(location: offset, length: length)
|
return ByteRange(location: offset, length: length)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -54,13 +54,13 @@ public struct XCTFailMessageRule: ASTRule, ConfigurationProviderRule, AutomaticT
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
||||||
guard
|
guard let bodyRange = dictionary.bodyByteRange else {
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
return false
|
||||||
let bodyLength = dictionary.bodyLength else { return false }
|
}
|
||||||
|
|
||||||
guard bodyLength > 0 else { return true }
|
guard bodyRange.length > 0 else { return true }
|
||||||
|
|
||||||
let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength)
|
let body = file.stringView.substringWithByteRange(bodyRange)
|
||||||
return body == "\"\""
|
return body == "\"\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,15 +46,7 @@ public struct XCTSpecificMatcherRule: ASTRule, OptInRule, ConfigurationProviderR
|
||||||
return firstOffset < secondOffset
|
return firstOffset < secondOffset
|
||||||
}
|
}
|
||||||
.prefix(2)
|
.prefix(2)
|
||||||
.compactMap { argument -> String? in
|
.compactMap { $0.byteRange.flatMap(file.stringView.substringWithByteRange) }
|
||||||
guard
|
|
||||||
let argOffset = argument.offset,
|
|
||||||
let argLength = argument.length,
|
|
||||||
let body = file.stringView.substringWithByteRange(start: argOffset, length: argLength)
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
.sorted { arg1, _ -> Bool in
|
.sorted { arg1, _ -> Bool in
|
||||||
return protectedArguments.contains(arg1)
|
return protectedArguments.contains(arg1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,15 +61,14 @@ public struct AnyObjectProtocolRule: SubstitutionCorrectableASTRule, OptInRule,
|
||||||
|
|
||||||
return dictionary.elements.compactMap { subDict -> NSRange? in
|
return dictionary.elements.compactMap { subDict -> NSRange? in
|
||||||
guard
|
guard
|
||||||
let offset = subDict.offset,
|
let byteRange = subDict.byteRange,
|
||||||
let length = subDict.length,
|
let content = file.stringView.substringWithByteRange(byteRange),
|
||||||
let content = file.stringView.substringWithByteRange(start: offset, length: length),
|
|
||||||
content == "class"
|
content == "class"
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.stringView.byteRangeToNSRange(start: offset, length: length)
|
return file.stringView.byteRangeToNSRange(byteRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,14 +49,14 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
|
||||||
guard kind == .call, let name = dictionary.name, name.hasSuffix(".map"),
|
guard kind == .call, let name = dictionary.name, name.hasSuffix(".map"),
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
let bodyOffset = dictionary.bodyOffset,
|
||||||
let bodyLength = dictionary.bodyLength,
|
let bodyLength = dictionary.bodyLength,
|
||||||
|
let bodyRange = dictionary.bodyByteRange,
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let offset = dictionary.offset else {
|
let offset = dictionary.offset else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = NSRange(location: bodyOffset, length: bodyLength)
|
let tokens = file.syntaxMap.tokens(inByteRange: bodyRange).filter { token in
|
||||||
let tokens = file.syntaxMap.tokens(inByteRange: range).filter { token in
|
|
||||||
guard let kind = token.kind else {
|
guard let kind = token.kind else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -84,37 +84,39 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isClosureParameter(firstToken: SwiftLintSyntaxToken,
|
private func isClosureParameter(firstToken: SwiftLintSyntaxToken,
|
||||||
nameEndPosition: Int,
|
nameEndPosition: ByteCount,
|
||||||
file: SwiftLintFile) -> Bool {
|
file: SwiftLintFile) -> Bool {
|
||||||
let length = firstToken.offset - nameEndPosition
|
let length = firstToken.offset - nameEndPosition
|
||||||
guard length > 0,
|
guard length > 0,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let byteRange = contents.byteRangeToNSRange(start: nameEndPosition, length: length) else {
|
case let byteRange = ByteRange(location: nameEndPosition, length: length),
|
||||||
return false
|
let nsRange = contents.byteRangeToNSRange(byteRange)
|
||||||
|
else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let pattern = regex("\\A\\s*\\(?\\s*\\{")
|
let pattern = regex("\\A\\s*\\(?\\s*\\{")
|
||||||
return pattern.firstMatch(in: file.contents, options: .anchored, range: byteRange) != nil
|
return pattern.firstMatch(in: file.contents, options: .anchored, range: nsRange) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func containsTrailingContent(lastToken: SwiftLintSyntaxToken,
|
private func containsTrailingContent(lastToken: SwiftLintSyntaxToken,
|
||||||
bodyEndPosition: Int,
|
bodyEndPosition: ByteCount,
|
||||||
file: SwiftLintFile) -> Bool {
|
file: SwiftLintFile) -> Bool {
|
||||||
let lastTokenEnd = lastToken.offset + lastToken.length
|
let lastTokenEnd = lastToken.offset + lastToken.length
|
||||||
let remainingLength = bodyEndPosition - lastTokenEnd
|
let remainingLength = bodyEndPosition - lastTokenEnd
|
||||||
let remainingRange = NSRange(location: lastTokenEnd, length: remainingLength)
|
let remainingRange = ByteRange(location: lastTokenEnd, length: remainingLength)
|
||||||
return containsContent(inByteRange: remainingRange, file: file)
|
return containsContent(inByteRange: remainingRange, file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func containsLeadingContent(tokens: [SwiftLintSyntaxToken],
|
private func containsLeadingContent(tokens: [SwiftLintSyntaxToken],
|
||||||
bodyStartPosition: Int,
|
bodyStartPosition: ByteCount,
|
||||||
file: SwiftLintFile) -> Bool {
|
file: SwiftLintFile) -> Bool {
|
||||||
let inTokenPosition = tokens.firstIndex(where: { token in
|
let inTokenPosition = tokens.firstIndex(where: { token in
|
||||||
token.kind == .keyword && file.contents(for: token) == "in"
|
token.kind == .keyword && file.contents(for: token) == "in"
|
||||||
})
|
})
|
||||||
|
|
||||||
let firstToken: SwiftLintSyntaxToken
|
let firstToken: SwiftLintSyntaxToken
|
||||||
let start: Int
|
let start: ByteCount
|
||||||
if let position = inTokenPosition {
|
if let position = inTokenPosition {
|
||||||
let index = tokens.index(after: position)
|
let index = tokens.index(after: position)
|
||||||
firstToken = tokens[index]
|
firstToken = tokens[index]
|
||||||
|
@ -126,25 +128,26 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = firstToken.offset - start
|
let length = firstToken.offset - start
|
||||||
let remainingRange = NSRange(location: start, length: length)
|
let remainingRange = ByteRange(location: start, length: length)
|
||||||
return containsContent(inByteRange: remainingRange, file: file)
|
return containsContent(inByteRange: remainingRange, file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func containsContent(inByteRange byteRange: NSRange, file: SwiftLintFile) -> Bool {
|
private func containsContent(inByteRange byteRange: ByteRange, file: SwiftLintFile) -> Bool {
|
||||||
let nsstring = file.stringView
|
let stringView = file.stringView
|
||||||
let remainingTokens = file.syntaxMap.tokens(inByteRange: byteRange)
|
let remainingTokens = file.syntaxMap.tokens(inByteRange: byteRange)
|
||||||
let ranges = NSMutableIndexSet(indexesIn: byteRange)
|
guard let nsRange = stringView.byteRangeToNSRange(byteRange) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for token in remainingTokens {
|
let ranges = NSMutableIndexSet(indexesIn: nsRange)
|
||||||
ranges.remove(in: token.range)
|
|
||||||
|
for tokenNSRange in remainingTokens.compactMap({ stringView.byteRangeToNSRange($0.range) }) {
|
||||||
|
ranges.remove(in: tokenNSRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
var containsContent = false
|
var containsContent = false
|
||||||
ranges.enumerateRanges(options: []) { range, stop in
|
ranges.enumerateRanges(options: []) { range, stop in
|
||||||
guard let substring = nsstring.substringWithByteRange(start: range.location, length: range.length) else {
|
let substring = stringView.substring(with: range)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let processedSubstring = substring
|
let processedSubstring = substring
|
||||||
.trimmingCharacters(in: CharacterSet(charactersIn: "{}"))
|
.trimmingCharacters(in: CharacterSet(charactersIn: "{}"))
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
|
@ -65,8 +65,10 @@ public struct ClassDelegateProtocolRule: ASTRule, ConfigurationProviderRule, Aut
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
let bodyOffset = dictionary.bodyOffset,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
case let start = nameOffset + nameLength,
|
case let start = nameOffset + nameLength,
|
||||||
let range = contents.byteRangeToNSRange(start: start, length: bodyOffset - start),
|
case let byteRange = ByteRange(location: start, length: bodyOffset - start),
|
||||||
!isClassProtocol(file: file, range: range) else {
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
|
!isClassProtocol(file: file, range: range)
|
||||||
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,10 +52,9 @@ public struct CompilerProtocolInitRule: ASTRule, ConfigurationProviderRule {
|
||||||
guard compilerProtocol.initCallNames.contains(name),
|
guard compilerProtocol.initCallNames.contains(name),
|
||||||
case let arguments = dictionary.enclosedArguments.compactMap({ $0.name }),
|
case let arguments = dictionary.enclosedArguments.compactMap({ $0.name }),
|
||||||
compilerProtocol.match(arguments: arguments),
|
compilerProtocol.match(arguments: arguments),
|
||||||
let offset = dictionary.offset,
|
let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange)
|
||||||
let length = dictionary.length,
|
else {
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else {
|
continue
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [(compilerProtocol, range)]
|
return [(compilerProtocol, range)]
|
||||||
|
|
|
@ -53,9 +53,9 @@ public struct DeploymentTargetRule: ConfigurationProviderRule {
|
||||||
return file.rangesAndTokens(matching: pattern).flatMap { range, tokens -> [StyleViolation] in
|
return file.rangesAndTokens(matching: pattern).flatMap { range, tokens -> [StyleViolation] in
|
||||||
guard let availabilityToken = tokens.first,
|
guard let availabilityToken = tokens.first,
|
||||||
availabilityToken.kind == .keyword,
|
availabilityToken.kind == .keyword,
|
||||||
let tokenRange = file.stringView.byteRangeToNSRange(start: availabilityToken.offset,
|
let tokenRange = file.stringView.byteRangeToNSRange(availabilityToken.range)
|
||||||
length: availabilityToken.length) else {
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let rangeToSearch = NSRange(location: tokenRange.upperBound, length: range.length - tokenRange.length)
|
let rangeToSearch = NSRange(location: tokenRange.upperBound, length: range.length - tokenRange.length)
|
||||||
|
@ -83,17 +83,19 @@ public struct DeploymentTargetRule: ConfigurationProviderRule {
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return attributes.flatMap { dictionary -> [StyleViolation] in
|
return attributes.flatMap { dictionary -> [StyleViolation] in
|
||||||
guard let offset = dictionary.offset, let length = dictionary.length,
|
guard let byteRange = dictionary.byteRange,
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
let range = contents.byteRangeToNSRange(byteRange)
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return validate(range: range, file: file, violationType: "attribute", byteOffsetToReport: offset)
|
return validate(range: range, file: file, violationType: "attribute",
|
||||||
|
byteOffsetToReport: byteRange.location)
|
||||||
}.unique
|
}.unique
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validate(range: NSRange, file: SwiftLintFile, violationType: String,
|
private func validate(range: NSRange, file: SwiftLintFile, violationType: String,
|
||||||
byteOffsetToReport: Int) -> [StyleViolation] {
|
byteOffsetToReport: ByteCount) -> [StyleViolation] {
|
||||||
let platformToConfiguredMinVersion = self.platformToConfiguredMinVersion
|
let platformToConfiguredMinVersion = self.platformToConfiguredMinVersion
|
||||||
let allPlatforms = "(?:" + platformToConfiguredMinVersion.keys.joined(separator: "|") + ")"
|
let allPlatforms = "(?:" + platformToConfiguredMinVersion.keys.joined(separator: "|") + ")"
|
||||||
let pattern = "\(allPlatforms) [\\d\\.]+"
|
let pattern = "\(allPlatforms) [\\d\\.]+"
|
||||||
|
|
|
@ -48,7 +48,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
||||||
kind: SwiftExpressionKind) -> [Int] {
|
kind: SwiftExpressionKind) -> [ByteCount] {
|
||||||
guard kind == .call,
|
guard kind == .call,
|
||||||
let name = dictionary.name,
|
let name = dictionary.name,
|
||||||
name.hasSuffix(".addObserver"),
|
name.hasSuffix(".addObserver"),
|
||||||
|
@ -57,7 +57,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
|
||||||
argumentsNames == ["forName", "object", "queue"] ||
|
argumentsNames == ["forName", "object", "queue"] ||
|
||||||
argumentsNames == ["forName", "object", "queue", "using"],
|
argumentsNames == ["forName", "object", "queue", "using"],
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: 0, length: offset) else {
|
let range = file.stringView.byteRangeToNSRange(ByteRange(location: 0, length: offset)) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SourceKittenDictionary {
|
private extension SourceKittenDictionary {
|
||||||
func functions(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] {
|
func functions(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
|
||||||
return structures(forByteOffset: byteOffset)
|
return structures(forByteOffset: byteOffset)
|
||||||
.filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true }
|
.filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true }
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,11 +47,12 @@ public struct DuplicateEnumCasesRule: ConfigurationProviderRule, ASTRule, Automa
|
||||||
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
||||||
.flatMap { $0 }
|
.flatMap { $0 }
|
||||||
|
|
||||||
var elementsByName: [String: [Int]] = [:]
|
var elementsByName: [String: [ByteCount]] = [:]
|
||||||
for element in enumElements {
|
for element in enumElements {
|
||||||
guard let name = element.name,
|
guard let name = element.name,
|
||||||
let nameWithoutParameters = name.split(separator: "(").first,
|
let nameWithoutParameters = name.split(separator: "(").first,
|
||||||
let offset = element.offset else {
|
let offset = element.offset
|
||||||
|
else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,7 @@ public struct DynamicInlineRule: ASTRule, ConfigurationProviderRule, AutomaticTe
|
||||||
case let attributes = dictionary.enclosedSwiftAttributes,
|
case let attributes = dictionary.enclosedSwiftAttributes,
|
||||||
attributes.contains(.dynamic),
|
attributes.contains(.dynamic),
|
||||||
attributes.contains(.inline),
|
attributes.contains(.inline),
|
||||||
let funcByteOffset = dictionary.offset,
|
let funcOffset = dictionary.offset.flatMap(file.stringView.location),
|
||||||
let funcOffset = file.stringView
|
|
||||||
.byteRangeToNSRange(start: funcByteOffset, length: 0)?.location,
|
|
||||||
case let inlinePattern = regex("@inline"),
|
case let inlinePattern = regex("@inline"),
|
||||||
case let range = NSRange(location: 0, length: funcOffset),
|
case let range = NSRange(location: 0, length: funcOffset),
|
||||||
let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range)
|
let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range)
|
||||||
|
|
|
@ -131,8 +131,7 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let violationRange = file.stringView.byteRangeToNSRange(start: leftmostToken.offset,
|
let violationRange = file.stringView.byteRangeToNSRange(leftmostToken.range)
|
||||||
length: leftmostToken.length)
|
|
||||||
return violationRange
|
return violationRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,12 +174,13 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom
|
||||||
|
|
||||||
private extension StringView {
|
private extension StringView {
|
||||||
func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? {
|
func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? {
|
||||||
return substringWithByteRange(start: syntaxToken.offset, length: syntaxToken.length)
|
return substringWithByteRange(syntaxToken.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? {
|
func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? {
|
||||||
return substringWithByteRange(start: startToken.offset + startToken.length,
|
let byteRange = ByteRange(location: startToken.range.upperBound,
|
||||||
length: endToken.offset - startToken.offset - startToken.length)
|
length: endToken.offset - startToken.range.upperBound)
|
||||||
|
return substringWithByteRange(byteRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDotOrOptionalChainingBetweenTokens(_ startToken: SwiftLintSyntaxToken,
|
func isDotOrOptionalChainingBetweenTokens(_ startToken: SwiftLintSyntaxToken,
|
||||||
|
|
|
@ -63,7 +63,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule {
|
||||||
case let outerKindIndex = kinds.index(before: brace.offset),
|
case let outerKindIndex = kinds.index(before: brace.offset),
|
||||||
case let outerKind = kinds[outerKindIndex],
|
case let outerKind = kinds[outerKindIndex],
|
||||||
case let braceEnd = brace.element.byteRange.upperBound,
|
case let braceEnd = brace.element.byteRange.upperBound,
|
||||||
case let tokensRange = NSRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd),
|
case let tokensRange = ByteRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd),
|
||||||
case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange),
|
case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange),
|
||||||
!tokens.contains(where: isNotComment) else {
|
!tokens.contains(where: isNotComment) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -76,7 +76,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isBrace(offset: Int, element: (kind: String, byteRange: NSRange)) -> Bool {
|
private func isBrace(offset: Int, element: (kind: String, byteRange: ByteRange)) -> Bool {
|
||||||
return StatementKind(rawValue: element.kind) == .brace
|
return StatementKind(rawValue: element.kind) == .brace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,14 +41,14 @@ public struct LowerACLThanParentRule: OptInRule, ConfigurationProviderRule, Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validateACL(isHigherThan parentAccessibility: AccessControlLevel,
|
private func validateACL(isHigherThan parentAccessibility: AccessControlLevel,
|
||||||
in substructure: SourceKittenDictionary) -> [Int] {
|
in substructure: SourceKittenDictionary) -> [ByteCount] {
|
||||||
return substructure.substructure.flatMap { element -> [Int] in
|
return substructure.substructure.flatMap { element -> [ByteCount] in
|
||||||
guard let elementKind = element.declarationKind,
|
guard let elementKind = element.declarationKind,
|
||||||
elementKind.isRelevantDeclaration else {
|
elementKind.isRelevantDeclaration else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var violationOffset: Int?
|
var violationOffset: ByteCount?
|
||||||
let accessibility = element.accessibility ?? .internal
|
let accessibility = element.accessibility ?? .internal
|
||||||
// Swift 5 infers members of private types with no explicit ACL attribute to be `internal`.
|
// Swift 5 infers members of private types with no explicit ACL attribute to be `internal`.
|
||||||
let isInferredACL = accessibility == .internal && !element.enclosedSwiftAttributes.contains(.internal)
|
let isInferredACL = accessibility == .internal && !element.enclosedSwiftAttributes.contains(.internal)
|
||||||
|
|
|
@ -189,8 +189,8 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
return !syntaxTokens.isEmpty && SyntaxKind.commentKinds.contains(syntaxKind)
|
return !syntaxTokens.isEmpty && SyntaxKind.commentKinds.contains(syntaxKind)
|
||||||
}.compactMap { range, syntaxTokens in
|
}.compactMap { range, syntaxTokens in
|
||||||
let identifierRange = file.stringView
|
let byteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
|
||||||
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
let identifierRange = file.stringView.byteRangeToNSRange(byteRange)
|
||||||
return identifierRange.map { NSUnionRange($0, range) }
|
return identifierRange.map { NSUnionRange($0, range) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import SourceKittenFramework
|
||||||
|
|
||||||
private extension SwiftLintFile {
|
private extension SwiftLintFile {
|
||||||
func missingDocOffsets(in dictionary: SourceKittenDictionary,
|
func missingDocOffsets(in dictionary: SourceKittenDictionary,
|
||||||
acls: [AccessControlLevel]) -> [(Int, AccessControlLevel)] {
|
acls: [AccessControlLevel]) -> [(ByteCount, AccessControlLevel)] {
|
||||||
if dictionary.enclosedSwiftAttributes.contains(.override) ||
|
if dictionary.enclosedSwiftAttributes.contains(.override) ||
|
||||||
!dictionary.inheritedTypes.isEmpty {
|
!dictionary.inheritedTypes.isEmpty {
|
||||||
return []
|
return []
|
||||||
|
@ -77,8 +77,7 @@ public struct MissingDocsRule: OptInRule, ConfigurationProviderRule, AutomaticTe
|
||||||
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||||
let acls = configuration.parameters.map { $0.value }
|
let acls = configuration.parameters.map { $0.value }
|
||||||
let dict = file.structureDictionary
|
let dict = file.structureDictionary
|
||||||
return file.missingDocOffsets(in: dict,
|
return file.missingDocOffsets(in: dict, acls: acls).map { offset, acl in
|
||||||
acls: acls).map { (offset: Int, acl: AccessControlLevel) in
|
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.parameters.first { $0.value == acl }?.severity ?? .warning,
|
severity: configuration.parameters.first { $0.value == acl }?.severity ?? .warning,
|
||||||
location: Location(file: file, byteOffset: offset),
|
location: Location(file: file, byteOffset: offset),
|
||||||
|
|
|
@ -28,9 +28,8 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
|
||||||
dictionary.name == "NSLocalizedString",
|
dictionary.name == "NSLocalizedString",
|
||||||
let firstArgument = dictionary.enclosedArguments.first,
|
let firstArgument = dictionary.enclosedArguments.first,
|
||||||
firstArgument.name == nil,
|
firstArgument.name == nil,
|
||||||
let offset = firstArgument.offset,
|
let byteRange = firstArgument.byteRange,
|
||||||
let length = firstArgument.length,
|
case let kinds = file.syntaxMap.kinds(inByteRange: byteRange),
|
||||||
case let kinds = file.syntaxMap.kinds(inByteRange: NSRange(location: offset, length: length)),
|
|
||||||
!kinds.allSatisfy({ $0 == .string }) else {
|
!kinds.allSatisfy({ $0 == .string }) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,7 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide
|
||||||
return [
|
return [
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
StyleViolation(ruleDescription: type(of: self).description,
|
||||||
severity: configuration.severity,
|
severity: configuration.severity,
|
||||||
location: Location(file: file, byteOffset: offset))
|
location: Location(file: file, byteOffset: byteRange.location))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsets(file: SwiftLintFile,
|
private func violationOffsets(file: SwiftLintFile,
|
||||||
dictionary: SourceKittenDictionary) -> [Int] {
|
dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
return dictionary.substructure.flatMap { subDict -> [Int] in
|
return dictionary.substructure.flatMap { subDict -> [ByteCount] in
|
||||||
// complete detachment is allowed on `deinit`
|
// complete detachment is allowed on `deinit`
|
||||||
if subDict.declarationKind == .functionMethodInstance,
|
if subDict.declarationKind == .functionMethodInstance,
|
||||||
subDict.name == "deinit" {
|
subDict.name == "deinit" {
|
||||||
|
@ -51,16 +51,11 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu
|
||||||
private var methodName = "NotificationCenter.default.removeObserver"
|
private var methodName = "NotificationCenter.default.removeObserver"
|
||||||
|
|
||||||
private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool {
|
||||||
guard let bodyOffset = dictionary.bodyOffset,
|
guard let bodyRange = dictionary.bodyByteRange,
|
||||||
let bodyLength = dictionary.bodyLength else {
|
case let tokens = file.syntaxMap.tokens(inByteRange: bodyRange),
|
||||||
return false
|
tokens.kinds == [.keyword],
|
||||||
}
|
let token = tokens.first
|
||||||
|
else {
|
||||||
let range = NSRange(location: bodyOffset, length: bodyLength)
|
|
||||||
let tokens = file.syntaxMap.tokens(inByteRange: range)
|
|
||||||
let types = tokens.kinds
|
|
||||||
|
|
||||||
guard types == [.keyword], let token = tokens.first else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,12 @@ public struct OrphanedDocCommentRule: ConfigurationProviderRule {
|
||||||
return token.kind == .docComment || token.kind == .docCommentField
|
return token.kind == .docComment || token.kind == .docCommentField
|
||||||
}
|
}
|
||||||
|
|
||||||
let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [NSRange]? in
|
let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [ByteRange]? in
|
||||||
guard let docOffset = dictionary.docOffset, let docLength = dictionary.docLength else {
|
guard let docOffset = dictionary.docOffset, let docLength = dictionary.docLength else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return [NSRange(location: docOffset, length: docLength)]
|
return [ByteRange(location: docOffset, length: docLength)]
|
||||||
}.sorted { $0.location < $1.location }
|
}.sorted { $0.location < $1.location }
|
||||||
|
|
||||||
return docStringsTokens
|
return docStringsTokens
|
||||||
|
|
|
@ -40,11 +40,12 @@ public struct OverrideInExtensionRule: ConfigurationProviderRule, OptInRule, Aut
|
||||||
return elements
|
return elements
|
||||||
.filter { $0.kind == .extension && !susceptibleNames.contains($0.name) }
|
.filter { $0.kind == .extension && !susceptibleNames.contains($0.name) }
|
||||||
.flatMap { element in
|
.flatMap { element in
|
||||||
return element.dictionary.substructure.compactMap { element -> Int? in
|
return element.dictionary.substructure.compactMap { element -> ByteCount? in
|
||||||
guard element.declarationKind != nil,
|
guard element.declarationKind != nil,
|
||||||
element.enclosedSwiftAttributes.contains(.override),
|
element.enclosedSwiftAttributes.contains(.override),
|
||||||
let offset = element.offset else {
|
let offset = element.offset
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset
|
return offset
|
||||||
|
|
|
@ -60,8 +60,8 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [Int] {
|
private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [ByteCount] {
|
||||||
return substructure.flatMap { dictionary -> [Int] in
|
return substructure.flatMap { dictionary -> [ByteCount] in
|
||||||
let substructure = dictionary.substructure.flatMap { dict -> [SourceKittenDictionary] in
|
let substructure = dictionary.substructure.flatMap { dict -> [SourceKittenDictionary] in
|
||||||
if dict.expressionKind == .closure {
|
if dict.expressionKind == .closure {
|
||||||
return dict.substructure
|
return dict.substructure
|
||||||
|
@ -74,7 +74,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [Int] {
|
private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
guard
|
guard
|
||||||
dictionary.kind != nil,
|
dictionary.kind != nil,
|
||||||
let offset = dictionary.offset
|
let offset = dictionary.offset
|
||||||
|
@ -90,7 +90,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au
|
||||||
return dictionary.substructure.compactMap(toViolationOffset)
|
return dictionary.substructure.compactMap(toViolationOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toViolationOffset(dictionary: SourceKittenDictionary) -> Int? {
|
private func toViolationOffset(dictionary: SourceKittenDictionary) -> ByteCount? {
|
||||||
guard
|
guard
|
||||||
let name = dictionary.name,
|
let name = dictionary.name,
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
public struct RawValueForCamelCasedCodableEnumRule: ASTRule, OptInRule, ConfigurationProviderRule,
|
public struct RawValueForCamelCasedCodableEnumRule: ASTRule, OptInRule, ConfigurationProviderRule,
|
||||||
AutomaticTestableRule {
|
AutomaticTestableRule {
|
||||||
public var configuration = SeverityConfiguration(.warning)
|
public var configuration = SeverityConfiguration(.warning)
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
@ -104,13 +104,11 @@ AutomaticTestableRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] {
|
private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] {
|
||||||
let locs = substructureElements(of: dictionary, matching: .enumcase)
|
return substructureElements(of: dictionary, matching: .enumcase)
|
||||||
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
.compactMap { substructureElements(of: $0, matching: .enumelement) }
|
||||||
.flatMap(camelCasedEnumCasesMissingRawValue)
|
.flatMap(camelCasedEnumCasesMissingRawValue)
|
||||||
.compactMap { $0.offset }
|
.compactMap { $0.offset }
|
||||||
|
|
||||||
return locs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func substructureElements(of dict: SourceKittenDictionary,
|
private func substructureElements(of dict: SourceKittenDictionary,
|
||||||
|
|
|
@ -89,7 +89,7 @@ public struct RequiredEnumCaseRule: ASTRule, OptInRule, ConfigurationProviderRul
|
||||||
///
|
///
|
||||||
/// - returns: Location of where the enum declaration starts.
|
/// - returns: Location of where the enum declaration starts.
|
||||||
static func location(from dictionary: SourceKittenDictionary, in file: SwiftLintFile) -> Location {
|
static func location(from dictionary: SourceKittenDictionary, in file: SwiftLintFile) -> Location {
|
||||||
return Location(file: file, characterOffset: dictionary.offset ?? 0)
|
return Location(file: file, byteOffset: dictionary.offset ?? 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the names of cases found in the enum.
|
/// Determines the names of cases found in the enum.
|
||||||
|
|
|
@ -27,8 +27,9 @@ public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, OptInRule,
|
||||||
case let attributes = dictionary.enclosedSwiftAttributes,
|
case let attributes = dictionary.enclosedSwiftAttributes,
|
||||||
attributes.contains(.iboutlet),
|
attributes.contains(.iboutlet),
|
||||||
attributes.contains(.weak),
|
attributes.contains(.weak),
|
||||||
let offset = dictionary.offset else {
|
let offset = dictionary.offset
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -29,26 +29,28 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi
|
||||||
|
|
||||||
public func validate(file: SwiftLintFile, kind: SwiftExpressionKind,
|
public func validate(file: SwiftLintFile, kind: SwiftExpressionKind,
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
guard kind == .closure, let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength,
|
guard kind == .closure, let bodyRange = dictionary.bodyByteRange,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let closureRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength),
|
let closureRange = contents.byteRangeToNSRange(bodyRange),
|
||||||
let inTokenRange = file.match(pattern: "\\bin\\b", with: [.keyword], range: closureRange).first,
|
let inTokenRange = file.match(pattern: "\\bin\\b", with: [.keyword], range: closureRange).first,
|
||||||
let inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location,
|
let inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location,
|
||||||
length: inTokenRange.length) else {
|
length: inTokenRange.length)
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = inTokenByteRange.location - bodyOffset
|
let length = inTokenByteRange.location - bodyRange.location
|
||||||
let variables = localVariableDeclarations(inByteRange: NSRange(location: bodyOffset, length: length),
|
let variables = localVariableDeclarations(inByteRange: ByteRange(location: bodyRange.location, length: length),
|
||||||
structureDictionary: file.structureDictionary)
|
structureDictionary: file.structureDictionary)
|
||||||
let unownedVariableOffsets = variables.compactMap { dictionary in
|
let unownedVariableOffsets = variables.compactMap { dictionary in
|
||||||
return dictionary.swiftAttributes.first { attributeDict in
|
return dictionary.swiftAttributes.first { attributeDict in
|
||||||
guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak,
|
guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak,
|
||||||
let offset = attributeDict.offset, let length = attributeDict.length else {
|
let attributeByteRange = attributeDict.byteRange
|
||||||
return false
|
else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents.substringWithByteRange(start: offset, length: length) == "unowned"
|
return contents.substringWithByteRange(attributeByteRange) == "unowned"
|
||||||
}?.offset
|
}?.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,13 +61,14 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func localVariableDeclarations(inByteRange byteRange: NSRange,
|
private func localVariableDeclarations(inByteRange byteRange: ByteRange,
|
||||||
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
||||||
return structureDictionary.traverseBreadthFirst { dictionary in
|
return structureDictionary.traverseBreadthFirst { dictionary in
|
||||||
guard dictionary.declarationKind == .varLocal,
|
guard dictionary.declarationKind == .varLocal,
|
||||||
let variableByteRange = dictionary.byteRange,
|
let variableByteRange = dictionary.byteRange,
|
||||||
byteRange.intersects(variableByteRange) else {
|
byteRange.intersects(variableByteRange)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return [dictionary]
|
return [dictionary]
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,12 +80,14 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat
|
||||||
guard kind == .closure,
|
guard kind == .closure,
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
let length = dictionary.length,
|
let length = dictionary.length,
|
||||||
let closureRange = contents.byteRangeToNSRange(start: offset, length: length)
|
let closureByteRange = dictionary.byteRange,
|
||||||
|
let closureRange = contents.byteRangeToNSRange(closureByteRange)
|
||||||
else { return [] }
|
else { return [] }
|
||||||
|
|
||||||
let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length)
|
let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length)
|
||||||
let captureListSearchLength = firstSubstructureOffset - offset
|
let captureListSearchLength = firstSubstructureOffset - offset
|
||||||
guard let captureListSearchRange = contents.byteRangeToNSRange(start: offset, length: captureListSearchLength),
|
let captureListSearchByteRange = ByteRange(location: offset, length: captureListSearchLength)
|
||||||
|
guard let captureListSearchRange = contents.byteRangeToNSRange(captureListSearchByteRange),
|
||||||
let match = captureListRegex.firstMatch(in: file.contents, options: [], range: captureListSearchRange)
|
let match = captureListRegex.firstMatch(in: file.contents, options: [], range: captureListSearchRange)
|
||||||
else { return [] }
|
else { return [] }
|
||||||
|
|
||||||
|
@ -131,7 +133,7 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func identifierStrings(in file: SwiftLintFile, byteRange: NSRange) -> Set<String> {
|
private func identifierStrings(in file: SwiftLintFile, byteRange: ByteRange) -> Set<String> {
|
||||||
let identifiers = file.syntaxMap
|
let identifiers = file.syntaxMap
|
||||||
.tokens(inByteRange: byteRange)
|
.tokens(inByteRange: byteRange)
|
||||||
.compactMap { token -> String? in
|
.compactMap { token -> String? in
|
||||||
|
|
|
@ -127,54 +127,62 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let bodyLength = dictionary.bodyLength,
|
let bodyLength = dictionary.bodyLength,
|
||||||
bodyLength > 0 else {
|
bodyLength > 0
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let rangeStart = nameOffset + nameLength
|
let rangeStart = nameOffset + nameLength
|
||||||
let rangeLength = (offset + length) - (nameOffset + nameLength)
|
let rangeLength = (offset + length) - (nameOffset + nameLength)
|
||||||
|
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
|
||||||
let parameters = dictionary.enclosedVarParameters
|
let parameters = dictionary.enclosedVarParameters
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
return parameters.compactMap { param -> (NSRange, String)? in
|
return parameters.compactMap { param -> (NSRange, String)? in
|
||||||
guard let paramOffset = param.offset,
|
self.rangeAndName(parameter: param, contents: contents, byteRange: byteRange, file: file)
|
||||||
let name = param.name,
|
}
|
||||||
name != "_",
|
}
|
||||||
let regex = try? NSRegularExpression(pattern: name,
|
|
||||||
options: [.ignoreMetacharacters]),
|
private func rangeAndName(parameter: SourceKittenDictionary, contents: StringView, byteRange: ByteRange,
|
||||||
let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength)
|
file: SwiftLintFile) -> (range: NSRange, name: String)? {
|
||||||
|
guard let paramOffset = parameter.offset,
|
||||||
|
let name = parameter.name,
|
||||||
|
name != "_",
|
||||||
|
let regex = try? NSRegularExpression(pattern: name,
|
||||||
|
options: [.ignoreMetacharacters]),
|
||||||
|
let range = contents.byteRangeToNSRange(byteRange)
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let paramLength = ByteCount(name.lengthOfBytes(using: .utf8))
|
||||||
|
|
||||||
|
let matches = regex.matches(in: file.contents, options: [], range: range).ranges()
|
||||||
|
for range in matches {
|
||||||
|
guard let byteRange = contents.NSRangeToByteRange(start: range.location,
|
||||||
|
length: range.length),
|
||||||
|
// if it's the parameter declaration itself, we should skip
|
||||||
|
byteRange.location > paramOffset,
|
||||||
|
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange)
|
||||||
else {
|
else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = tokens.first(where: { token -> Bool in
|
||||||
|
return (token.kind == .identifier
|
||||||
|
|| (token.kind == .keyword && name == "self")) &&
|
||||||
|
token.offset == byteRange.location &&
|
||||||
|
token.length == byteRange.length
|
||||||
|
})
|
||||||
|
|
||||||
|
// found a usage, there's no violation!
|
||||||
|
guard token == nil else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let paramLength = name.lengthOfBytes(using: .utf8)
|
let violationByteRange = ByteRange(location: paramOffset, length: paramLength)
|
||||||
|
return contents.byteRangeToNSRange(violationByteRange).map { range in
|
||||||
let matches = regex.matches(in: file.contents, options: [], range: range).ranges()
|
return (range, name)
|
||||||
for range in matches {
|
|
||||||
guard let byteRange = contents.NSRangeToByteRange(start: range.location,
|
|
||||||
length: range.length),
|
|
||||||
// if it's the parameter declaration itself, we should skip
|
|
||||||
byteRange.location > paramOffset,
|
|
||||||
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = tokens.first(where: { token -> Bool in
|
|
||||||
return (token.kind == .identifier
|
|
||||||
|| (token.kind == .keyword && name == "self")) &&
|
|
||||||
token.offset == byteRange.location &&
|
|
||||||
token.length == byteRange.length
|
|
||||||
})
|
|
||||||
|
|
||||||
// found a usage, there's no violation!
|
|
||||||
guard token == nil else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let range = contents.byteRangeToNSRange(start: paramOffset, length: paramLength) {
|
|
||||||
return (range, name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,9 +103,9 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config
|
||||||
let contentsNSString = file.stringView
|
let contentsNSString = file.stringView
|
||||||
if let byteRange = contentsNSString.NSRangeToByteRange(start: violationRange.location,
|
if let byteRange = contentsNSString.NSRangeToByteRange(start: violationRange.location,
|
||||||
length: violationRange.length),
|
length: violationRange.length),
|
||||||
let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }),
|
let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }) {
|
||||||
let nextTokenLocation = contentsNSString.byteRangeToNSRange(start: nextToken.offset, length: 0) {
|
let nextTokenLocation = contentsNSString.location(fromByteOffset: nextToken.offset)
|
||||||
rangeToRemove.length = nextTokenLocation.location - violationRange.location
|
rangeToRemove.length = nextTokenLocation - violationRange.location
|
||||||
}
|
}
|
||||||
|
|
||||||
return (rangeToRemove, "")
|
return (rangeToRemove, "")
|
||||||
|
@ -114,22 +114,18 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config
|
||||||
public func violationRanges(in file: SwiftLintFile, kind: StatementKind,
|
public func violationRanges(in file: SwiftLintFile, kind: StatementKind,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
dictionary: SourceKittenDictionary) -> [NSRange] {
|
||||||
guard type(of: self).kinds.contains(kind),
|
guard type(of: self).kinds.contains(kind),
|
||||||
let offset = dictionary.offset, let length = dictionary.length,
|
let byteRange = dictionary.byteRange,
|
||||||
case let byteRange = NSRange(location: offset, length: length),
|
|
||||||
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange),
|
case let tokens = file.syntaxMap.tokens(inByteRange: byteRange),
|
||||||
let firstToken = tokens.first,
|
let firstToken = tokens.first,
|
||||||
firstToken.kind == .identifier,
|
firstToken.kind == .identifier,
|
||||||
let tokenContent = file.contents(for: firstToken),
|
let tokenContent = file.contents(for: firstToken),
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
let range = contents.byteRangeToNSRange(byteRange),
|
||||||
return []
|
case let pattern = "(?:break|continue)\\s+\(tokenContent)\\b",
|
||||||
}
|
file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty,
|
||||||
|
let violationRange = contents.byteRangeToNSRange(firstToken.range)
|
||||||
let pattern = "(?:break|continue)\\s+\(tokenContent)\\b"
|
else {
|
||||||
guard file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty,
|
return []
|
||||||
let violationRange = contents.byteRangeToNSRange(start: firstToken.offset,
|
|
||||||
length: firstToken.length) else {
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [violationRange]
|
return [violationRange]
|
||||||
|
|
|
@ -4,7 +4,7 @@ import SourceKittenFramework
|
||||||
public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProviderRule, AnalyzerRule, CollectingRule {
|
public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProviderRule, AnalyzerRule, CollectingRule {
|
||||||
public struct FileUSRs {
|
public struct FileUSRs {
|
||||||
var referenced: Set<String>
|
var referenced: Set<String>
|
||||||
var declared: [(usr: String, nameOffset: Int)]
|
var declared: [(usr: String, nameOffset: ByteCount)]
|
||||||
var testCaseUSRs: Set<String>
|
var testCaseUSRs: Set<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +121,9 @@ public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsets(in file: SwiftLintFile, compilerArguments: [String],
|
private func violationOffsets(in file: SwiftLintFile, compilerArguments: [String],
|
||||||
declaredUSRs: [(usr: String, nameOffset: Int)],
|
declaredUSRs: [(usr: String, nameOffset: ByteCount)],
|
||||||
allReferencedUSRs: Set<String>,
|
allReferencedUSRs: Set<String>,
|
||||||
allTestCaseUSRs: Set<String>) -> [Int] {
|
allTestCaseUSRs: Set<String>) -> [ByteCount] {
|
||||||
// Unused declarations are:
|
// Unused declarations are:
|
||||||
// 1. all declarations
|
// 1. all declarations
|
||||||
// 2. minus all references
|
// 2. minus all references
|
||||||
|
@ -160,7 +160,7 @@ private extension SwiftLintFile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = Int64(token.offset)
|
let offset = token.offset
|
||||||
let request = Request.cursorInfo(file: path, offset: offset, arguments: compilerArguments)
|
let request = Request.cursorInfo(file: path, offset: offset, arguments: compilerArguments)
|
||||||
guard var cursorInfo = try? request.sendIfNotDisabled() else {
|
guard var cursorInfo = try? request.sendIfNotDisabled() else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -169,14 +169,14 @@ private extension SwiftLintFile {
|
||||||
if let acl = editorOpen.aclAtOffset(offset) {
|
if let acl = editorOpen.aclAtOffset(offset) {
|
||||||
cursorInfo["key.accessibility"] = acl.rawValue
|
cursorInfo["key.accessibility"] = acl.rawValue
|
||||||
}
|
}
|
||||||
cursorInfo["swiftlint.offset"] = offset
|
cursorInfo["swiftlint.offset"] = Int64(offset.value)
|
||||||
return cursorInfo
|
return cursorInfo
|
||||||
}
|
}
|
||||||
.map(SourceKittenDictionary.init)
|
.map(SourceKittenDictionary.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func declaredUSRs(allCursorInfo: [SourceKittenDictionary], includePublicAndOpen: Bool)
|
static func declaredUSRs(allCursorInfo: [SourceKittenDictionary], includePublicAndOpen: Bool)
|
||||||
-> [(usr: String, nameOffset: Int)] {
|
-> [(usr: String, nameOffset: ByteCount)] {
|
||||||
return allCursorInfo.compactMap { cursorInfo in
|
return allCursorInfo.compactMap { cursorInfo in
|
||||||
return declaredUSRAndOffset(cursorInfo: cursorInfo, includePublicAndOpen: includePublicAndOpen)
|
return declaredUSRAndOffset(cursorInfo: cursorInfo, includePublicAndOpen: includePublicAndOpen)
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ private extension SwiftLintFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func declaredUSRAndOffset(cursorInfo: SourceKittenDictionary, includePublicAndOpen: Bool)
|
private static func declaredUSRAndOffset(cursorInfo: SourceKittenDictionary, includePublicAndOpen: Bool)
|
||||||
-> (usr: String, nameOffset: Int)? {
|
-> (usr: String, nameOffset: ByteCount)? {
|
||||||
if let offset = cursorInfo.swiftlintOffset,
|
if let offset = cursorInfo.swiftlintOffset,
|
||||||
let usr = cursorInfo.usr,
|
let usr = cursorInfo.usr,
|
||||||
let kind = cursorInfo.declarationKind,
|
let kind = cursorInfo.declarationKind,
|
||||||
|
@ -231,7 +231,7 @@ private extension SwiftLintFile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return (usr, Int(offset))
|
return (usr, ByteCount(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -276,7 +276,7 @@ private extension SourceKittenDictionary {
|
||||||
return value["key.annotated_decl"] as? String
|
return value["key.annotated_decl"] as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
func aclAtOffset(_ offset: Int64) -> AccessControlLevel? {
|
func aclAtOffset(_ offset: ByteCount) -> AccessControlLevel? {
|
||||||
if let nameOffset = nameOffset,
|
if let nameOffset = nameOffset,
|
||||||
nameOffset == offset,
|
nameOffset == offset,
|
||||||
let acl = accessibility {
|
let acl = accessibility {
|
||||||
|
|
|
@ -203,7 +203,7 @@ private extension SwiftLintFile {
|
||||||
if syntaxKindsToSkip.contains(tokenKind) {
|
if syntaxKindsToSkip.contains(tokenKind) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: Int64(token.offset),
|
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: token.offset,
|
||||||
arguments: compilerArguments)
|
arguments: compilerArguments)
|
||||||
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else {
|
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else {
|
||||||
queuedPrintError("Could not get cursor info")
|
queuedPrintError("Could not get cursor info")
|
||||||
|
@ -252,7 +252,7 @@ private extension SwiftLintFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request
|
// Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request
|
||||||
func operatorImports(arguments: [String], processedTokenOffsets: Set<Int>) -> Set<String> {
|
func operatorImports(arguments: [String], processedTokenOffsets: Set<ByteCount>) -> Set<String> {
|
||||||
guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled())
|
guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled())
|
||||||
.map(SourceKittenDictionary.init) else {
|
.map(SourceKittenDictionary.init) else {
|
||||||
queuedPrintError("Could not get index")
|
queuedPrintError("Could not get index")
|
||||||
|
@ -271,9 +271,9 @@ private extension SwiftLintFile {
|
||||||
let offset = lineOffset + column - 1
|
let offset = lineOffset + column - 1
|
||||||
|
|
||||||
// Filter already processed tokens such as static methods that are not operators
|
// Filter already processed tokens such as static methods that are not operators
|
||||||
guard !processedTokenOffsets.contains(Int(offset)) else { continue }
|
guard !processedTokenOffsets.contains(ByteCount(offset)) else { continue }
|
||||||
|
|
||||||
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: offset, arguments: arguments)
|
let cursorInfoRequest = Request.cursorInfo(file: path!, offset: ByteCount(offset), arguments: arguments)
|
||||||
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled())
|
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled())
|
||||||
.map(SourceKittenDictionary.init) else {
|
.map(SourceKittenDictionary.init) else {
|
||||||
queuedPrintError("Could not get cursor info")
|
queuedPrintError("Could not get cursor info")
|
||||||
|
|
|
@ -101,43 +101,43 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
|
||||||
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||||
let setTokens = file.rangesAndTokens(matching: "\\bset\\b").keywordTokens()
|
let setTokens = file.rangesAndTokens(matching: "\\bset\\b").keywordTokens()
|
||||||
|
|
||||||
let violatingLocations = setTokens.compactMap { setToken -> Int? in
|
let violatingLocations = setTokens.compactMap { setToken -> ByteCount? in
|
||||||
// the last element is the deepest structure
|
// the last element is the deepest structure
|
||||||
guard let dict = declarations(forByteOffset: setToken.offset,
|
guard let dict = declarations(forByteOffset: setToken.offset,
|
||||||
structureDictionary: file.structureDictionary).last,
|
structureDictionary: file.structureDictionary).last,
|
||||||
let bodyOffset = dict.bodyOffset, let bodyLength = dict.bodyLength,
|
let bodyByteRange = dict.bodyByteRange,
|
||||||
case let contents = file.stringView,
|
case let contents = file.stringView,
|
||||||
let propertyRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength),
|
let propertyRange = contents.byteRangeToNSRange(bodyByteRange),
|
||||||
let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict) else {
|
let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument = findNamedArgument(after: setToken, file: file)
|
let argument = findNamedArgument(after: setToken, file: file)
|
||||||
|
|
||||||
let propertyEndOffset = bodyOffset + bodyLength
|
let propertyEndOffset = bodyByteRange.upperBound
|
||||||
let setterByteRange: NSRange
|
let setterByteRange: ByteRange
|
||||||
if setToken.offset > getToken.offset { // get {} set {}
|
if setToken.offset > getToken.offset { // get {} set {}
|
||||||
let startOfBody: Int
|
let startOfBody: ByteCount
|
||||||
if let argumentToken = argument?.token {
|
if let argumentToken = argument?.token {
|
||||||
startOfBody = argumentToken.offset + argumentToken.length
|
startOfBody = argumentToken.offset + argumentToken.length
|
||||||
} else {
|
} else {
|
||||||
startOfBody = setToken.offset
|
startOfBody = setToken.offset
|
||||||
}
|
}
|
||||||
setterByteRange = NSRange(location: startOfBody,
|
setterByteRange = ByteRange(location: startOfBody,
|
||||||
length: propertyEndOffset - startOfBody)
|
length: propertyEndOffset - startOfBody)
|
||||||
} else { // set {} get {}
|
} else { // set {} get {}
|
||||||
let startOfBody: Int
|
let startOfBody: ByteCount
|
||||||
if let argumentToken = argument?.token {
|
if let argumentToken = argument?.token {
|
||||||
startOfBody = argumentToken.offset + argumentToken.length
|
startOfBody = argumentToken.offset + argumentToken.length
|
||||||
} else {
|
} else {
|
||||||
startOfBody = setToken.offset
|
startOfBody = setToken.offset
|
||||||
}
|
}
|
||||||
setterByteRange = NSRange(location: startOfBody,
|
setterByteRange = ByteRange(location: startOfBody,
|
||||||
length: getToken.offset - startOfBody)
|
length: getToken.offset - startOfBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let setterRange = contents.byteRangeToNSRange(start: setterByteRange.location,
|
guard let setterRange = contents.byteRangeToNSRange(setterByteRange) else {
|
||||||
length: setterByteRange.length) else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private func declarations(forByteOffset byteOffset: Int,
|
private func declarations(forByteOffset byteOffset: ByteCount,
|
||||||
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
||||||
var results = [SourceKittenDictionary]()
|
var results = [SourceKittenDictionary]()
|
||||||
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
|
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
|
||||||
|
@ -197,11 +197,10 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl
|
||||||
// Only accepts declarations which contains a body and contains the
|
// Only accepts declarations which contains a body and contains the
|
||||||
// searched byteOffset
|
// searched byteOffset
|
||||||
guard let kind = dictionary.declarationKind,
|
guard let kind = dictionary.declarationKind,
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
let byteRange = dictionary.bodyByteRange,
|
||||||
let bodyLength = dictionary.bodyLength,
|
byteRange.contains(byteOffset)
|
||||||
case let byteRange = NSRange(location: bodyOffset, length: bodyLength),
|
else {
|
||||||
NSLocationInRange(byteOffset, byteRange) else {
|
return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentKind != .protocol && allowedKinds.contains(kind) {
|
if parentKind != .protocol && allowedKinds.contains(kind) {
|
||||||
|
|
|
@ -74,7 +74,10 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
|
||||||
guard !isComputed else { return [] }
|
guard !isComputed else { return [] }
|
||||||
|
|
||||||
guard let offset = dictionary.offset,
|
guard let offset = dictionary.offset,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: 3) else { return [] }
|
let range = file.stringView.byteRangeToNSRange(ByteRange(location: offset, length: 3))
|
||||||
|
else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
return [range]
|
return [range]
|
||||||
}
|
}
|
||||||
|
@ -83,13 +86,14 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu
|
||||||
return (violationRange, "weak var")
|
return (violationRange, "weak var")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func protocolDeclarations(forByteOffset byteOffset: Int,
|
private func protocolDeclarations(forByteOffset byteOffset: ByteCount,
|
||||||
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
||||||
return structureDictionary.traverseBreadthFirst { dictionary in
|
return structureDictionary.traverseBreadthFirst { dictionary in
|
||||||
guard dictionary.declarationKind == .protocol,
|
guard dictionary.declarationKind == .protocol,
|
||||||
let byteRange = dictionary.byteRange,
|
let byteRange = dictionary.byteRange,
|
||||||
NSLocationInRange(byteOffset, byteRange) else {
|
byteRange.contains(byteOffset)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return [dictionary]
|
return [dictionary]
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,11 +61,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule,
|
||||||
public func validate(file: SwiftLintFile,
|
public func validate(file: SwiftLintFile,
|
||||||
kind: StatementKind,
|
kind: StatementKind,
|
||||||
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
guard observedStatements.contains(kind),
|
guard observedStatements.contains(kind), let offset = dictionary.offset else {
|
||||||
let offset = dictionary.offset,
|
return []
|
||||||
let length = dictionary.length
|
|
||||||
else {
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let matches = file.lines.filter({ $0.byteRange.contains(offset) }).reduce(into: []) { matches, line in
|
let matches = file.lines.filter({ $0.byteRange.contains(offset) }).reduce(into: []) { matches, line in
|
||||||
|
@ -75,15 +72,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches.map { _ -> StyleViolation in
|
return matches.map { _ -> StyleViolation in
|
||||||
let characterOffset = startOffset(of: offset, with: length, in: file)
|
|
||||||
let location = Location(file: file, characterOffset: characterOffset)
|
|
||||||
return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity,
|
return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity,
|
||||||
location: location)
|
location: Location(file: file, byteOffset: offset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startOffset(of offset: Int, with length: Int, in file: SwiftLintFile) -> Int {
|
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length)
|
|
||||||
return range?.location ?? offset
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,10 +89,9 @@ public struct CyclomaticComplexityRule: ASTRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
private func reduceSwitchComplexity(initialComplexity complexity: Int, file: SwiftLintFile,
|
private func reduceSwitchComplexity(initialComplexity complexity: Int, file: SwiftLintFile,
|
||||||
dictionary: SourceKittenDictionary) -> Int {
|
dictionary: SourceKittenDictionary) -> Int {
|
||||||
let bodyOffset = dictionary.bodyOffset ?? 0
|
let bodyRange = dictionary.bodyByteRange ?? ByteRange(location: 0, length: 0)
|
||||||
let bodyLength = dictionary.bodyLength ?? 0
|
|
||||||
|
|
||||||
let contents = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) ?? ""
|
let contents = file.stringView.substringWithByteRange(bodyRange) ?? ""
|
||||||
|
|
||||||
let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1
|
let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1
|
||||||
return complexity - fallthroughCount
|
return complexity - fallthroughCount
|
||||||
|
|
|
@ -40,10 +40,8 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let nameOffset = dictionary.nameOffset ?? 0
|
let nameRange = ByteRange(location: dictionary.nameOffset ?? 0, length: dictionary.nameLength ?? 0)
|
||||||
let length = dictionary.nameLength ?? 0
|
if functionIsInitializer(file: file, byteRange: nameRange) {
|
||||||
|
|
||||||
if functionIsInitializer(file: file, byteOffset: nameOffset, byteLength: length) {
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +51,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
|
|
||||||
let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <)
|
let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <)
|
||||||
|
|
||||||
let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, offset: nameOffset,
|
let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, range: nameRange)
|
||||||
length: length)
|
|
||||||
if allParameterCount < minThreshold! {
|
if allParameterCount < minThreshold! {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -62,7 +59,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
var parameterCount = allParameterCount
|
var parameterCount = allParameterCount
|
||||||
|
|
||||||
if configuration.ignoresDefaultParameters {
|
if configuration.ignoresDefaultParameters {
|
||||||
parameterCount -= defaultFunctionParameterCount(file: file, byteOffset: nameOffset, byteLength: length)
|
parameterCount -= defaultFunctionParameterCount(file: file, byteRange: nameRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value {
|
for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value {
|
||||||
|
@ -78,16 +75,14 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
private func allFunctionParameterCount(structure: [SourceKittenDictionary],
|
private func allFunctionParameterCount(structure: [SourceKittenDictionary], range: ByteRange) -> Int {
|
||||||
offset: Int, length: Int) -> Int {
|
|
||||||
var parameterCount = 0
|
var parameterCount = 0
|
||||||
for subDict in structure {
|
for subDict in structure {
|
||||||
guard subDict.kind != nil,
|
guard subDict.kind != nil, let parameterOffset = subDict.offset else {
|
||||||
let parameterOffset = subDict.offset else {
|
continue
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard offset..<(offset + length) ~= parameterOffset else {
|
guard range.contains(parameterOffset) else {
|
||||||
return parameterCount
|
return parameterCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,15 +93,15 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule {
|
||||||
return parameterCount
|
return parameterCount
|
||||||
}
|
}
|
||||||
|
|
||||||
private func defaultFunctionParameterCount(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Int {
|
private func defaultFunctionParameterCount(file: SwiftLintFile, byteRange: ByteRange) -> Int {
|
||||||
let substring = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength)!
|
let substring = file.stringView.substringWithByteRange(byteRange)!
|
||||||
let equals = substring.filter { $0 == "=" }
|
let equals = substring.filter { $0 == "=" }
|
||||||
return equals.count
|
return equals.count
|
||||||
}
|
}
|
||||||
|
|
||||||
private func functionIsInitializer(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Bool {
|
private func functionIsInitializer(file: SwiftLintFile, byteRange: ByteRange) -> Bool {
|
||||||
guard let name = file.stringView
|
guard let name = file.stringView
|
||||||
.substringWithByteRange(start: byteOffset, length: byteLength),
|
.substringWithByteRange(byteRange),
|
||||||
name.hasPrefix("init"),
|
name.hasPrefix("init"),
|
||||||
let funcName = name.components(separatedBy: CharacterSet(charactersIn: "<(")).first else {
|
let funcName = name.components(separatedBy: CharacterSet(charactersIn: "<(")).first else {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -73,7 +73,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsetsForTypes(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
private func violationOffsetsForTypes(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
||||||
kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] {
|
kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] {
|
||||||
let kinds = SwiftDeclarationKind.variableKinds.subtracting([.varLocal])
|
let kinds = SwiftDeclarationKind.variableKinds.subtracting([.varLocal])
|
||||||
guard kinds.contains(kind),
|
guard kinds.contains(kind),
|
||||||
let type = dictionary.typeName,
|
let type = dictionary.typeName,
|
||||||
|
@ -86,12 +86,11 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary,
|
||||||
kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] {
|
kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
guard SwiftDeclarationKind.functionKinds.contains(kind),
|
guard SwiftDeclarationKind.functionKinds.contains(kind),
|
||||||
let returnRange = returnRangeForFunction(dictionary: dictionary),
|
let returnRange = returnRangeForFunction(dictionary: dictionary),
|
||||||
let returnSubstring = contents.substringWithByteRange(start: returnRange.location,
|
let returnSubstring = contents.substringWithByteRange(returnRange) else {
|
||||||
length: returnRange.length) else {
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +98,13 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
|
||||||
return offsets.sorted { $0.offset < $1.offset }
|
return offsets.sorted { $0.offset < $1.offset }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func violationOffsets(for text: String, initialOffset: Int = 0) -> [(offset: Int, size: Int)] {
|
private func violationOffsets(for text: String, initialOffset: ByteCount = 0) -> [(offset: ByteCount, size: Int)] {
|
||||||
guard let ranges = try? parenthesesRanges(in: text) else {
|
guard let ranges = try? parenthesesRanges(in: text) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = text.bridge()
|
var text = text.bridge()
|
||||||
var offsets = [(offset: Int, size: Int)]()
|
var offsets = [(offset: ByteCount, size: Int)]()
|
||||||
|
|
||||||
for (range, kind) in ranges {
|
for (range, kind) in ranges {
|
||||||
let substring = text.substring(with: range)
|
let substring = text.substring(with: range)
|
||||||
|
@ -124,7 +123,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
|
||||||
return offsets
|
return offsets
|
||||||
}
|
}
|
||||||
|
|
||||||
private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> NSRange? {
|
private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> ByteRange? {
|
||||||
guard let nameOffset = dictionary.nameOffset,
|
guard let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let length = dictionary.length,
|
let length = dictionary.length,
|
||||||
|
@ -139,7 +138,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return NSRange(location: start, length: end - start)
|
return ByteRange(location: start, length: end - start)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func parenthesesRanges(in text: String) throws -> [(NSRange, RangeKind)] {
|
private func parenthesesRanges(in text: String) throws -> [(NSRange, RangeKind)] {
|
||||||
|
|
|
@ -47,11 +47,11 @@ public struct FirstWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule
|
||||||
return true // has a substructure, like a closure
|
return true // has a substructure, like a closure
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else {
|
guard let bodyRange = dictionary.bodyByteRange else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength))
|
let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
|
||||||
return !syntaxKinds.contains(.string)
|
return !syntaxKinds.contains(.string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@ public struct LastWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule,
|
||||||
return true // has a substructure, like a closure
|
return true // has a substructure, like a closure
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else {
|
guard let bodyRange = dictionary.bodyByteRange else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength))
|
let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange)
|
||||||
return !syntaxKinds.contains(.string)
|
return !syntaxKinds.contains(.string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,8 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut
|
||||||
kind == .call,
|
kind == .call,
|
||||||
let nameOffset = dictionary.nameOffset,
|
let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let nameRange = file.stringView.byteRangeToNSRange(start: nameOffset, length: nameLength),
|
case let nameByteRange = ByteRange(location: nameOffset, length: nameLength),
|
||||||
|
let nameRange = file.stringView.byteRangeToNSRange(nameByteRange),
|
||||||
let match = reduceExpression.firstMatch(in: file.contents, options: [], range: nameRange),
|
let match = reduceExpression.firstMatch(in: file.contents, options: [], range: nameRange),
|
||||||
dictionary.enclosedArguments.count == 2,
|
dictionary.enclosedArguments.count == 2,
|
||||||
// would otherwise equal "into"
|
// would otherwise equal "into"
|
||||||
|
@ -132,13 +133,11 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
guard let offset = argument.offset,
|
guard let byteRange = argument.byteRange,
|
||||||
let length = argument.length,
|
let range = contents.byteRangeToNSRange(byteRange)
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length)
|
|
||||||
else { return false }
|
else { return false }
|
||||||
|
|
||||||
// Check for string literal
|
// Check for string literal
|
||||||
let byteRange = NSRange(location: offset, length: length)
|
|
||||||
let kinds = file.syntaxMap.kinds(inByteRange: byteRange)
|
let kinds = file.syntaxMap.kinds(inByteRange: byteRange)
|
||||||
if kinds == [.string] {
|
if kinds == [.string] {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -138,7 +138,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)],
|
private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)],
|
||||||
attributesTokens: [(String, NSRange)],
|
attributesTokens: [(String, ByteRange)],
|
||||||
line: Line, file: SwiftLintFile) -> Set<String> {
|
line: Line, file: SwiftLintFile) -> Set<String> {
|
||||||
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
|
let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map {
|
||||||
let hasParameter = attributeContainsParameter(attributeRange: $1,
|
let hasParameter = attributeContainsParameter(attributeRange: $1,
|
||||||
|
@ -241,16 +241,17 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
return allTokens
|
return allTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
private func attributeContainsParameter(attributeRange: NSRange,
|
private func attributeContainsParameter(attributeRange: ByteRange,
|
||||||
line: Line, file: SwiftLintFile) -> Bool {
|
line: Line, file: SwiftLintFile) -> Bool {
|
||||||
let restOfLineOffset = attributeRange.location + attributeRange.length
|
let restOfLineOffset = attributeRange.upperBound
|
||||||
let restOfLineLength = line.byteRange.location + line.byteRange.length - restOfLineOffset
|
let restOfLineLength = line.byteRange.upperBound - restOfLineOffset
|
||||||
|
|
||||||
let regex = AttributesRule.regularExpression
|
let regex = AttributesRule.regularExpression
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
// check if after the token is a `(` with only spaces allowed between the token and `(`
|
// check if after the token is a `(` with only spaces allowed between the token and `(`
|
||||||
guard let restOfLine = contents.substringWithByteRange(start: restOfLineOffset, length: restOfLineLength),
|
let restOfLineByteRange = ByteRange(location: restOfLineOffset, length: restOfLineLength)
|
||||||
|
guard let restOfLine = contents.substringWithByteRange(restOfLineByteRange),
|
||||||
case let range = restOfLine.fullNSRange,
|
case let range = restOfLine.fullNSRange,
|
||||||
regex.firstMatch(in: restOfLine, options: [], range: range) != nil else {
|
regex.firstMatch(in: restOfLine, options: [], range: range) != nil else {
|
||||||
return false
|
return false
|
||||||
|
@ -259,7 +260,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, NSRange)? {
|
private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, ByteRange)? {
|
||||||
guard token.kind == .attributeBuiltin else {
|
guard token.kind == .attributeBuiltin else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,12 @@ public struct ClosureEndIndentationRule: Rule, OptInRule, ConfigurationProviderR
|
||||||
|
|
||||||
extension ClosureEndIndentationRule: CorrectableRule {
|
extension ClosureEndIndentationRule: CorrectableRule {
|
||||||
public func correct(file: SwiftLintFile) -> [Correction] {
|
public func correct(file: SwiftLintFile) -> [Correction] {
|
||||||
let allViolations = violations(in: file).reversed().filter {
|
let allViolations = violations(in: file).reversed().filter { violation in
|
||||||
!file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty
|
guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !file.ruleEnabled(violatingRanges: [nsRange], for: self).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !allViolations.isEmpty else {
|
guard !allViolations.isEmpty else {
|
||||||
|
@ -107,8 +111,8 @@ extension ClosureEndIndentationRule: CorrectableRule {
|
||||||
extension ClosureEndIndentationRule {
|
extension ClosureEndIndentationRule {
|
||||||
fileprivate struct Violation {
|
fileprivate struct Violation {
|
||||||
var indentationRanges: (expected: NSRange, actual: NSRange)
|
var indentationRanges: (expected: NSRange, actual: NSRange)
|
||||||
var endOffset: Int
|
var endOffset: ByteCount
|
||||||
var range: NSRange
|
var range: ByteRange
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func violations(in file: SwiftLintFile) -> [Violation] {
|
fileprivate func violations(in file: SwiftLintFile) -> [Violation] {
|
||||||
|
@ -136,11 +140,10 @@ extension ClosureEndIndentationRule {
|
||||||
private func hasTrailingClosure(in file: SwiftLintFile,
|
private func hasTrailingClosure(in file: SwiftLintFile,
|
||||||
dictionary: SourceKittenDictionary) -> Bool {
|
dictionary: SourceKittenDictionary) -> Bool {
|
||||||
guard
|
guard
|
||||||
let offset = dictionary.offset,
|
let byteRange = dictionary.byteRange,
|
||||||
let length = dictionary.length,
|
let text = file.stringView.substringWithByteRange(byteRange)
|
||||||
let text = file.stringView.substringWithByteRange(start: offset, length: length)
|
else {
|
||||||
else {
|
return false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !text.hasSuffix(")")
|
return !text.hasSuffix(")")
|
||||||
|
@ -156,15 +159,17 @@ extension ClosureEndIndentationRule {
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
bodyLength > 0,
|
bodyLength > 0,
|
||||||
case let endOffset = offset + length - 1,
|
case let endOffset = offset + length - 1,
|
||||||
contents.substringWithByteRange(start: endOffset, length: 1) == "}",
|
case let closingBraceByteRange = ByteRange(location: endOffset, length: 1),
|
||||||
|
contents.substringWithByteRange(closingBraceByteRange) == "}",
|
||||||
let startOffset = startOffset(forDictionary: dictionary, file: file),
|
let startOffset = startOffset(forDictionary: dictionary, file: file),
|
||||||
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
|
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
|
||||||
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
|
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
|
||||||
case let nameEndPosition = nameOffset + nameLength,
|
case let nameEndPosition = nameOffset + nameLength,
|
||||||
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
|
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
|
||||||
startLine != endLine, bodyOffsetLine != endLine,
|
startLine != endLine, bodyOffsetLine != endLine,
|
||||||
!containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else {
|
!containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = file.lines[startLine - 1].range
|
let range = file.lines[startLine - 1].range
|
||||||
|
@ -172,8 +177,9 @@ extension ClosureEndIndentationRule {
|
||||||
let actual = endPosition - 1
|
let actual = endPosition - 1
|
||||||
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
||||||
case let expected = match.location - range.location,
|
case let expected = match.location - range.location,
|
||||||
expected != actual else {
|
expected != actual
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedRange = range
|
var expectedRange = range
|
||||||
|
@ -184,7 +190,7 @@ extension ClosureEndIndentationRule {
|
||||||
|
|
||||||
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
|
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
|
||||||
endOffset: endOffset,
|
endOffset: endOffset,
|
||||||
range: NSRange(location: offset, length: length))
|
range: ByteRange(location: offset, length: length))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validateArguments(in file: SwiftLintFile,
|
private func validateArguments(in file: SwiftLintFile,
|
||||||
|
@ -216,15 +222,17 @@ extension ClosureEndIndentationRule {
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
bodyLength > 0,
|
bodyLength > 0,
|
||||||
case let endOffset = offset + length - 1,
|
case let endOffset = offset + length - 1,
|
||||||
contents.substringWithByteRange(start: endOffset, length: 1) == "}",
|
case let closingBraceByteRange = ByteRange(location: endOffset, length: 1),
|
||||||
|
contents.substringWithByteRange(closingBraceByteRange) == "}",
|
||||||
let startOffset = dictionary.offset,
|
let startOffset = dictionary.offset,
|
||||||
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
|
let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset),
|
||||||
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
|
let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset),
|
||||||
case let nameEndPosition = nameOffset + nameLength,
|
case let nameEndPosition = nameOffset + nameLength,
|
||||||
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
|
let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition),
|
||||||
startLine != endLine, bodyOffsetLine != endLine,
|
startLine != endLine, bodyOffsetLine != endLine,
|
||||||
!isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else {
|
!isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file)
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = file.lines[startLine - 1].range
|
let range = file.lines[startLine - 1].range
|
||||||
|
@ -232,8 +240,9 @@ extension ClosureEndIndentationRule {
|
||||||
let actual = endPosition - 1
|
let actual = endPosition - 1
|
||||||
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
||||||
case let expected = match.location - range.location,
|
case let expected = match.location - range.location,
|
||||||
expected != actual else {
|
expected != actual
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedRange = range
|
var expectedRange = range
|
||||||
|
@ -244,30 +253,28 @@ extension ClosureEndIndentationRule {
|
||||||
|
|
||||||
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
|
return Violation(indentationRanges: (expected: expectedRange, actual: actualRange),
|
||||||
endOffset: endOffset,
|
endOffset: endOffset,
|
||||||
range: NSRange(location: offset, length: length))
|
range: ByteRange(location: offset, length: length))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Int? {
|
private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> ByteCount? {
|
||||||
guard let nameOffset = dictionary.nameOffset,
|
guard let nameByteRange = dictionary.nameByteRange else {
|
||||||
let nameLength = dictionary.nameLength else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let newLineRegex = regex("\n(\\s*\\}?\\.)")
|
let newLineRegex = regex("\n(\\s*\\}?\\.)")
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
guard let range = contents.byteRangeToNSRange(start: nameOffset, length: nameLength),
|
guard let range = contents.byteRangeToNSRange(nameByteRange),
|
||||||
let match = newLineRegex.matches(in: file.contents, options: [],
|
let match = newLineRegex.matches(in: file.contents, options: [], range: range).last?.range(at: 1),
|
||||||
range: range).last?.range(at: 1),
|
let methodByteRange = contents.NSRangeToByteRange(start: match.location, length: match.length)
|
||||||
let methodByteRange = contents.NSRangeToByteRange(start: match.location,
|
else {
|
||||||
length: match.length) else {
|
return nameByteRange.location
|
||||||
return nameOffset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodByteRange.location
|
return methodByteRange.location
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isSingleLineClosure(dictionary: SourceKittenDictionary,
|
private func isSingleLineClosure(dictionary: SourceKittenDictionary,
|
||||||
endPosition: Int, file: SwiftLintFile) -> Bool {
|
endPosition: ByteCount, file: SwiftLintFile) -> Bool {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
guard let start = dictionary.bodyOffset,
|
guard let start = dictionary.bodyOffset,
|
||||||
|
@ -280,7 +287,7 @@ extension ClosureEndIndentationRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func containsSingleLineClosure(dictionary: SourceKittenDictionary,
|
private func containsSingleLineClosure(dictionary: SourceKittenDictionary,
|
||||||
endPosition: Int, file: SwiftLintFile) -> Bool {
|
endPosition: ByteCount, file: SwiftLintFile) -> Bool {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
guard let closure = trailingClosure(dictionary: dictionary, file: file),
|
guard let closure = trailingClosure(dictionary: dictionary, file: file),
|
||||||
|
@ -309,12 +316,12 @@ extension ClosureEndIndentationRule {
|
||||||
private func filterClosureArguments(_ arguments: [SourceKittenDictionary],
|
private func filterClosureArguments(_ arguments: [SourceKittenDictionary],
|
||||||
file: SwiftLintFile) -> [SourceKittenDictionary] {
|
file: SwiftLintFile) -> [SourceKittenDictionary] {
|
||||||
return arguments.filter { argument in
|
return arguments.filter { argument in
|
||||||
guard let offset = argument.bodyOffset,
|
guard let bodyByteRange = argument.bodyByteRange,
|
||||||
let length = argument.bodyLength,
|
let range = file.stringView.byteRangeToNSRange(bodyByteRange),
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length),
|
|
||||||
let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range,
|
let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range,
|
||||||
match.location == range.location else {
|
match.location == range.location
|
||||||
return false
|
else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -330,10 +337,12 @@ extension ClosureEndIndentationRule {
|
||||||
let firstArgumentOffset = firstArgument.offset,
|
let firstArgumentOffset = firstArgument.offset,
|
||||||
case let offset = nameOffset + nameLength,
|
case let offset = nameOffset + nameLength,
|
||||||
case let length = firstArgumentOffset - offset,
|
case let length = firstArgumentOffset - offset,
|
||||||
let range = file.stringView.byteRangeToNSRange(start: offset, length: length),
|
case let byteRange = ByteRange(location: offset, length: length),
|
||||||
|
let range = file.stringView.byteRangeToNSRange(byteRange),
|
||||||
let match = regex("\\(\\s*\\n\\s*").firstMatch(in: file.contents, options: [], range: range)?.range,
|
let match = regex("\\(\\s*\\n\\s*").firstMatch(in: file.contents, options: [], range: range)?.range,
|
||||||
match.location == range.location else {
|
match.location == range.location
|
||||||
return false
|
else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -52,8 +52,9 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
|
||||||
guard let nameOffset = dictionary.nameOffset,
|
guard let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength,
|
let nameLength = dictionary.nameLength,
|
||||||
let bodyLength = dictionary.bodyLength,
|
let bodyLength = dictionary.bodyLength,
|
||||||
bodyLength > 0 else {
|
bodyLength > 0
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let parameters = dictionary.enclosedVarParameters
|
let parameters = dictionary.enclosedVarParameters
|
||||||
|
@ -70,14 +71,16 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule,
|
||||||
let rangeLength = paramOffset - rangeStart
|
let rangeLength = paramOffset - rangeStart
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength),
|
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
|
||||||
|
guard let range = contents.byteRangeToNSRange(byteRange),
|
||||||
let match = regex.matches(in: file.contents, options: [], range: range).last?.range,
|
let match = regex.matches(in: file.contents, options: [], range: range).last?.range,
|
||||||
match.location != NSNotFound,
|
match.location != NSNotFound,
|
||||||
let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location,
|
let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location,
|
||||||
let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset),
|
let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset),
|
||||||
let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset),
|
let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset),
|
||||||
braceLine != paramLine else {
|
braceLine != paramLine
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return StyleViolation(ruleDescription: type(of: self).description,
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
|
|
|
@ -90,7 +90,7 @@ public struct ClosureSpacingRule: CorrectableRule, ConfigurationProviderRule, Op
|
||||||
return kindsToExclude.contains(tokenKind)
|
return kindsToExclude.contains(tokenKind)
|
||||||
}
|
}
|
||||||
let tokenRanges = tokens.compactMap {
|
let tokenRanges = tokens.compactMap {
|
||||||
file.stringView.byteRangeToNSRange(start: $0.offset, length: $0.length)
|
file.stringView.byteRangeToNSRange($0.range)
|
||||||
}
|
}
|
||||||
linesWithBraces.append(braces.filter({ !$0.intersects(tokenRanges) }))
|
linesWithBraces.append(braces.filter({ !$0.intersects(tokenRanges) }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,12 +86,13 @@ public struct CollectionAlignmentRule: ASTRule, ConfigurationProviderRule, OptIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func colonLocation(with file: SwiftLintFile, keyOffset: Int, keyLength: Int,
|
private func colonLocation(with file: SwiftLintFile, keyOffset: ByteCount, keyLength: ByteCount,
|
||||||
valueOffset: Int) -> Location? {
|
valueOffset: ByteCount) -> Location? {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
let matchStart = keyOffset + keyLength
|
let matchStart = keyOffset + keyLength
|
||||||
let matchLength = valueOffset - matchStart
|
let matchLength = valueOffset - matchStart
|
||||||
let range = contents.byteRangeToNSRange(start: matchStart, length: matchLength)
|
let byteRange = ByteRange(location: matchStart, length: matchLength)
|
||||||
|
let range = contents.byteRangeToNSRange(byteRange)
|
||||||
|
|
||||||
let matches = file.match(pattern: ":", excludingSyntaxKinds: [.comment], range: range)
|
let matches = file.match(pattern: ":", excludingSyntaxKinds: [.comment], range: range)
|
||||||
return matches.first.map { Location(file: file, characterOffset: $0.location) }
|
return matches.first.map { Location(file: file, characterOffset: $0.location) }
|
||||||
|
|
|
@ -3,12 +3,12 @@ import SourceKittenFramework
|
||||||
|
|
||||||
extension ColonRule {
|
extension ColonRule {
|
||||||
internal func dictionaryColonViolationRanges(in file: SwiftLintFile,
|
internal func dictionaryColonViolationRanges(in file: SwiftLintFile,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
dictionary: SourceKittenDictionary) -> [ByteRange] {
|
||||||
guard configuration.applyToDictionaries else {
|
guard configuration.applyToDictionaries else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let ranges: [NSRange] = dictionary.traverseDepthFirst { subDict in
|
let ranges: [ByteRange] = dictionary.traverseDepthFirst { subDict in
|
||||||
guard let kind = subDict.expressionKind else { return nil }
|
guard let kind = subDict.expressionKind else { return nil }
|
||||||
return dictionaryColonViolationRanges(in: file, kind: kind, dictionary: subDict)
|
return dictionaryColonViolationRanges(in: file, kind: kind, dictionary: subDict)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ extension ColonRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func dictionaryColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
|
internal func dictionaryColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
dictionary: SourceKittenDictionary) -> [ByteRange] {
|
||||||
guard kind == .dictionary,
|
guard kind == .dictionary,
|
||||||
let ranges = dictionaryColonRanges(dictionary: dictionary) else {
|
let ranges = dictionaryColonRanges(dictionary: dictionary) else {
|
||||||
return []
|
return []
|
||||||
|
@ -25,7 +25,7 @@ extension ColonRule {
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return ranges.filter {
|
return ranges.filter {
|
||||||
guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else {
|
guard let colon = contents.substringWithByteRange($0) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,31 +38,28 @@ extension ColonRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? {
|
private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? {
|
||||||
let elements = dictionary.elements
|
let elements = dictionary.elements
|
||||||
guard elements.count % 2 == 0 else {
|
guard elements.count % 2 == 0 else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let expectedKind = "source.lang.swift.structure.elem.expr"
|
let expectedKind = "source.lang.swift.structure.elem.expr"
|
||||||
let ranges: [NSRange] = elements.compactMap { subDict in
|
let ranges: [ByteRange] = elements.compactMap { subDict in
|
||||||
guard subDict.kind == expectedKind,
|
guard subDict.kind == expectedKind else {
|
||||||
let offset = subDict.offset,
|
return nil
|
||||||
let length = subDict.length else {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NSRange(location: offset, length: length)
|
return subDict.byteRange
|
||||||
}
|
}
|
||||||
|
|
||||||
let even = ranges.enumerated().compactMap { $0 % 2 == 0 ? $1 : nil }
|
let even = ranges.enumerated().compactMap { $0 % 2 == 0 ? $1 : nil }
|
||||||
let odd = ranges.enumerated().compactMap { $0 % 2 != 0 ? $1 : nil }
|
let odd = ranges.enumerated().compactMap { $0 % 2 != 0 ? $1 : nil }
|
||||||
|
|
||||||
return zip(even, odd).map { evenRange, oddRange -> NSRange in
|
return zip(even, odd).map { evenRange, oddRange -> ByteRange in
|
||||||
let location = NSMaxRange(evenRange)
|
let location = evenRange.upperBound
|
||||||
let length = oddRange.location - location
|
let length = oddRange.location - location
|
||||||
|
return ByteRange(location: location, length: length)
|
||||||
return NSRange(location: location, length: length)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import SourceKittenFramework
|
||||||
|
|
||||||
extension ColonRule {
|
extension ColonRule {
|
||||||
internal func functionCallColonViolationRanges(in file: SwiftLintFile,
|
internal func functionCallColonViolationRanges(in file: SwiftLintFile,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
dictionary: SourceKittenDictionary) -> [ByteRange] {
|
||||||
return dictionary.traverseDepthFirst { subDict in
|
return dictionary.traverseDepthFirst { subDict in
|
||||||
guard let kind = subDict.expressionKind else { return nil }
|
guard let kind = subDict.expressionKind else { return nil }
|
||||||
return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict)
|
return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict)
|
||||||
|
@ -11,15 +11,16 @@ extension ColonRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
|
internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange] {
|
dictionary: SourceKittenDictionary) -> [ByteRange] {
|
||||||
guard kind == .argument,
|
guard kind == .argument,
|
||||||
let ranges = functionCallColonRanges(dictionary: dictionary) else {
|
let ranges = functionCallColonRanges(dictionary: dictionary)
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
return ranges.filter {
|
return ranges.filter {
|
||||||
guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else {
|
guard let colon = contents.substringWithByteRange($0) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,15 +33,16 @@ extension ColonRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? {
|
private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? {
|
||||||
guard let nameOffset = dictionary.nameOffset,
|
guard let nameOffset = dictionary.nameOffset,
|
||||||
let nameLength = dictionary.nameLength, nameLength > 0,
|
let nameLength = dictionary.nameLength, nameLength > 0,
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
let bodyOffset = dictionary.bodyOffset,
|
||||||
case let location = nameOffset + nameLength,
|
case let location = nameOffset + nameLength,
|
||||||
bodyOffset > location else {
|
bodyOffset > location
|
||||||
return nil
|
else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return [NSRange(location: location, length: bodyOffset - location)]
|
return [ByteRange(location: location, length: bodyOffset - location)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ internal extension ColonRule {
|
||||||
|
|
||||||
return isValidMatch(syntaxTokens: syntaxTokens, file: file)
|
return isValidMatch(syntaxTokens: syntaxTokens, file: file)
|
||||||
}.compactMap { match, syntaxTokens in
|
}.compactMap { match, syntaxTokens in
|
||||||
let identifierRange = contents
|
let firstSyntaxTokenByteRange = ByteRange(location: syntaxTokens[0].offset, length: 0)
|
||||||
.byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0)
|
let identifierRange = contents.byteRangeToNSRange(firstSyntaxTokenByteRange)
|
||||||
return identifierRange.map { NSUnionRange($0, match.range) }
|
return identifierRange.map { NSUnionRange($0, match.range) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ internal extension ColonRule {
|
||||||
case (.identifier, .keyword),
|
case (.identifier, .keyword),
|
||||||
(.typeidentifier, .keyword):
|
(.typeidentifier, .keyword):
|
||||||
validKinds = file.isTypeLike(token: syntaxTokens[1])
|
validKinds = file.isTypeLike(token: syntaxTokens[1])
|
||||||
//Exclude explicit "Self" type because of static variables
|
// Exclude explicit "Self" type because of static variables
|
||||||
if syntaxKinds[0] == .identifier,
|
if syntaxKinds[0] == .identifier,
|
||||||
file.contents(for: syntaxTokens[1]) == "Self" {
|
file.contents(for: syntaxTokens[1]) == "Self" {
|
||||||
validKinds = false
|
validKinds = false
|
||||||
|
|
|
@ -79,17 +79,11 @@ public struct ColonRule: CorrectableRule, ConfigurationProviderRule {
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
let dictViolations: [RangeWithKind] = dictionaryColonViolationRanges(in: file,
|
let dictViolations: [RangeWithKind] = dictionaryColonViolationRanges(in: file,
|
||||||
dictionary: dictionary).compactMap {
|
dictionary: dictionary).compactMap {
|
||||||
guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else {
|
return contents.byteRangeToNSRange($0).map { (range: $0, kind: .dictionary) }
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return (range: range, kind: .dictionary)
|
|
||||||
}
|
}
|
||||||
let functionViolations: [RangeWithKind] = functionCallColonViolationRanges(in: file,
|
let functionViolations: [RangeWithKind] = functionCallColonViolationRanges(in: file,
|
||||||
dictionary: dictionary).compactMap {
|
dictionary: dictionary).compactMap {
|
||||||
guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else {
|
return contents.byteRangeToNSRange($0).map { (range: $0, kind: .functionCall) }
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return (range: range, kind: .functionCall)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (violations + dictViolations + functionViolations).sorted {
|
return (violations + dictViolations + functionViolations).sorted {
|
||||||
|
|
|
@ -77,10 +77,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
|
||||||
|
|
||||||
let callsRanges = dictionary.substructure.compactMap { dict -> NSRange? in
|
let callsRanges = dictionary.substructure.compactMap { dict -> NSRange? in
|
||||||
guard dict.expressionKind == .call,
|
guard dict.expressionKind == .call,
|
||||||
let offset = dict.offset,
|
let byteRange = dict.byteRange,
|
||||||
let length = dict.length,
|
let range = contents.byteRangeToNSRange(byteRange)
|
||||||
let range = contents.byteRangeToNSRange(start: offset, length: length) else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return range
|
return range
|
||||||
|
@ -88,10 +88,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
|
||||||
|
|
||||||
return dictionary.elements.flatMap { subDictionary -> [NSRange] in
|
return dictionary.elements.flatMap { subDictionary -> [NSRange] in
|
||||||
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
guard subDictionary.kind == "source.lang.swift.structure.elem.pattern",
|
||||||
let offset = subDictionary.offset,
|
let byteRange = subDictionary.byteRange,
|
||||||
let length = subDictionary.length,
|
let caseRange = contents.byteRangeToNSRange(byteRange)
|
||||||
let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else {
|
else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let emptyArgumentRegex = regex("\\.\\S+\\s*(\\([,\\s_]*\\))")
|
let emptyArgumentRegex = regex("\\.\\S+\\s*(\\([,\\s_]*\\))")
|
||||||
|
@ -107,8 +107,8 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat
|
||||||
// avoid matches in "(_, _) where"
|
// avoid matches in "(_, _) where"
|
||||||
if let whereByteRange = contents.NSRangeToByteRange(start: whereRange.location,
|
if let whereByteRange = contents.NSRangeToByteRange(start: whereRange.location,
|
||||||
length: whereRange.length),
|
length: whereRange.length),
|
||||||
case let length = whereByteRange.location - offset,
|
case let length = whereByteRange.location - byteRange.location,
|
||||||
case let byteRange = NSRange(location: offset, length: length),
|
case let byteRange = ByteRange(location: byteRange.location, length: length),
|
||||||
Set(file.syntaxMap.kinds(inByteRange: byteRange)) == [.keyword] {
|
Set(file.syntaxMap.kinds(inByteRange: byteRange)) == [.keyword] {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,12 +82,14 @@ public struct EmptyParenthesesWithTrailingClosureRule: SubstitutionCorrectableAS
|
||||||
|
|
||||||
let rangeStart = nameOffset + nameLength
|
let rangeStart = nameOffset + nameLength
|
||||||
let rangeLength = (offset + length) - (nameOffset + nameLength)
|
let rangeLength = (offset + length) - (nameOffset + nameLength)
|
||||||
|
let byteRange = ByteRange(location: rangeStart, length: rangeLength)
|
||||||
let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex
|
let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex
|
||||||
|
|
||||||
guard let range = file.stringView.byteRangeToNSRange(start: rangeStart, length: rangeLength),
|
guard let range = file.stringView.byteRangeToNSRange(byteRange),
|
||||||
let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range,
|
||||||
match.location == range.location else {
|
match.location == range.location
|
||||||
return []
|
else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return [match]
|
return [match]
|
||||||
|
|
|
@ -140,12 +140,12 @@ public struct ExplicitSelfRule: CorrectableRule, ConfigurationProviderRule, Anal
|
||||||
let contents = file.stringView
|
let contents = file.stringView
|
||||||
|
|
||||||
return cursorsMissingExplicitSelf.compactMap { cursorInfo in
|
return cursorsMissingExplicitSelf.compactMap { cursorInfo in
|
||||||
guard let byteOffset = cursorInfo["swiftlint.offset"] as? Int64 else {
|
guard let byteOffset = (cursorInfo["swiftlint.offset"] as? Int64).flatMap(ByteCount.init) else {
|
||||||
queuedPrintError("couldn't convert offsets")
|
queuedPrintError("couldn't convert offsets")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents.byteRangeToNSRange(start: Int(byteOffset), length: 0)
|
return contents.byteRangeToNSRange(ByteRange(location: byteOffset, length: 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,29 +156,29 @@ private let kindsToFind: Set = [
|
||||||
]
|
]
|
||||||
|
|
||||||
private extension SwiftLintFile {
|
private extension SwiftLintFile {
|
||||||
func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [Int]) throws
|
func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [ByteCount]) throws
|
||||||
-> [[String: SourceKitRepresentable]] {
|
-> [[String: SourceKitRepresentable]] {
|
||||||
return try byteOffsets.compactMap { offset in
|
return try byteOffsets.compactMap { offset in
|
||||||
if stringView.substringWithByteRange(start: offset - 1, length: 1)! == "." { return nil }
|
if stringView.substringWithByteRange(ByteRange(location: offset - 1, length: 1))! == "." { return nil }
|
||||||
var cursorInfo = try Request.cursorInfo(file: self.path!, offset: Int64(offset),
|
var cursorInfo = try Request.cursorInfo(file: self.path!, offset: offset,
|
||||||
arguments: compilerArguments).sendIfNotDisabled()
|
arguments: compilerArguments).sendIfNotDisabled()
|
||||||
cursorInfo["swiftlint.offset"] = Int64(offset)
|
cursorInfo["swiftlint.offset"] = Int64(offset.value)
|
||||||
return cursorInfo
|
return cursorInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension StringView {
|
private extension StringView {
|
||||||
func byteOffset(forLine line: Int, column: Int) -> Int {
|
func byteOffset(forLine line: Int, column: Int) -> ByteCount {
|
||||||
var byteOffset = 0
|
var byteOffset = ByteCount(0)
|
||||||
for line in lines[..<(line - 1)] {
|
for line in lines[..<(line - 1)] {
|
||||||
byteOffset += line.byteRange.length
|
byteOffset += line.byteRange.length
|
||||||
}
|
}
|
||||||
return byteOffset + column - 1
|
return byteOffset + ByteCount(column - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recursiveByteOffsets(_ dict: [String: Any]) -> [Int] {
|
func recursiveByteOffsets(_ dict: [String: Any]) -> [ByteCount] {
|
||||||
let cur: [Int]
|
let cur: [ByteCount]
|
||||||
if let line = dict["key.line"] as? Int64,
|
if let line = dict["key.line"] as? Int64,
|
||||||
let column = dict["key.column"] as? Int64,
|
let column = dict["key.column"] as? Int64,
|
||||||
let kindString = dict["key.kind"] as? String,
|
let kindString = dict["key.kind"] as? String,
|
||||||
|
@ -194,7 +194,7 @@ private extension StringView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [Int] {
|
private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [ByteCount] {
|
||||||
let absoluteFile = file.path!.bridge().absolutePathRepresentation()
|
let absoluteFile = file.path!.bridge().absolutePathRepresentation()
|
||||||
let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled()
|
let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled()
|
||||||
let binaryOffsets = file.stringView.recursiveByteOffsets(index)
|
let binaryOffsets = file.stringView.recursiveByteOffsets(index)
|
||||||
|
|
|
@ -63,7 +63,8 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule {
|
||||||
if let firstToken = firstToken, let lastToken = lastToken {
|
if let firstToken = firstToken, let lastToken = lastToken {
|
||||||
let start = firstToken.offset
|
let start = firstToken.offset
|
||||||
let length = lastToken.offset + lastToken.length - firstToken.offset
|
let length = lastToken.offset + lastToken.length - firstToken.offset
|
||||||
guard let range = file.stringView.byteRangeToNSRange(start: start, length: length) else {
|
let byteRange = ByteRange(location: start, length: length)
|
||||||
|
guard let range = file.stringView.byteRangeToNSRange(byteRange) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,36 +76,32 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule {
|
||||||
if let regex = requiredRegex,
|
if let regex = requiredRegex,
|
||||||
case let matches = regex.matches(in: file.contents, options: [], range: range),
|
case let matches = regex.matches(in: file.contents, options: [], range: range),
|
||||||
matches.isEmpty {
|
matches.isEmpty {
|
||||||
violationsOffsets.append(start)
|
violationsOffsets.append(file.stringView.location(fromByteOffset: start))
|
||||||
}
|
}
|
||||||
} else if requiredRegex != nil {
|
} else if requiredRegex != nil {
|
||||||
let location = firstNonCommentToken.map {
|
let location = firstNonCommentToken.map {
|
||||||
Location(file: file, byteOffset: $0.offset)
|
Location(file: file, byteOffset: $0.offset)
|
||||||
} ?? Location(file: file.path, line: 1)
|
} ?? Location(file: file.path, line: 1)
|
||||||
return [
|
return [makeViolation(at: location)]
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
|
||||||
severity: configuration.severityConfiguration.severity,
|
|
||||||
location: location,
|
|
||||||
reason: type(of: self).reason)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return violationsOffsets.map {
|
return violationsOffsets.map { makeViolation(at: Location(file: file, characterOffset: $0)) }
|
||||||
StyleViolation(ruleDescription: type(of: self).description,
|
|
||||||
severity: configuration.severityConfiguration.severity,
|
|
||||||
location: Location(file: file, characterOffset: $0),
|
|
||||||
reason: type(of: self).reason)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isSwiftLintCommand(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool {
|
private func isSwiftLintCommand(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool {
|
||||||
guard let range = file.stringView.byteRangeToNSRange(start: token.offset,
|
guard let range = file.stringView.byteRangeToNSRange(token.range) else {
|
||||||
length: token.length) else {
|
return false
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !file.commands(in: range).isEmpty
|
return !file.commands(in: range).isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func makeViolation(at location: Location) -> StyleViolation {
|
||||||
|
return StyleViolation(ruleDescription: type(of: self).description,
|
||||||
|
severity: configuration.severityConfiguration.severity,
|
||||||
|
location: location,
|
||||||
|
reason: type(of: self).reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SyntaxKind {
|
private extension SyntaxKind {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
|
public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
|
||||||
private typealias FileTypeOffset = (fileType: FileType, offset: Int)
|
private typealias FileTypeOffset = (fileType: FileType, offset: ByteCount)
|
||||||
|
|
||||||
public var configuration = FileTypesOrderConfiguration()
|
public var configuration = FileTypesOrderConfiguration()
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func validateName(dictionary: SourceKittenDictionary,
|
private func validateName(dictionary: SourceKittenDictionary,
|
||||||
kind: SwiftDeclarationKind) -> (name: String, offset: Int)? {
|
kind: SwiftDeclarationKind) -> (name: String, offset: ByteCount)? {
|
||||||
guard var name = dictionary.name,
|
guard var name = dictionary.name,
|
||||||
let offset = dictionary.offset,
|
let offset = dictionary.offset,
|
||||||
kinds.contains(kind),
|
kinds.contains(kind),
|
||||||
|
|
|
@ -189,7 +189,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
let violatingLocations = getTokens.compactMap { token -> (Int, SwiftDeclarationKind?)? in
|
let violatingLocations = getTokens.compactMap { token -> (ByteCount, SwiftDeclarationKind?)? in
|
||||||
// the last element is the deepest structure
|
// the last element is the deepest structure
|
||||||
guard let dict = declarations(forByteOffset: token.offset,
|
guard let dict = declarations(forByteOffset: token.offset,
|
||||||
structureDictionary: file.structureDictionary).last else {
|
structureDictionary: file.structureDictionary).last else {
|
||||||
|
@ -220,7 +220,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ImplicitGetterRule {
|
private extension ImplicitGetterRule {
|
||||||
func declarations(forByteOffset byteOffset: Int,
|
func declarations(forByteOffset byteOffset: ByteCount,
|
||||||
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] {
|
||||||
var results = [SourceKittenDictionary]()
|
var results = [SourceKittenDictionary]()
|
||||||
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
|
let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter])
|
||||||
|
@ -230,11 +230,10 @@ private extension ImplicitGetterRule {
|
||||||
// Only accepts declarations which contains a body and contains the
|
// Only accepts declarations which contains a body and contains the
|
||||||
// searched byteOffset
|
// searched byteOffset
|
||||||
guard let kind = dictionary.declarationKind,
|
guard let kind = dictionary.declarationKind,
|
||||||
let bodyOffset = dictionary.bodyOffset,
|
let byteRange = dictionary.byteRange,
|
||||||
let bodyLength = dictionary.bodyLength,
|
byteRange.contains(byteOffset)
|
||||||
case let byteRange = NSRange(location: bodyOffset, length: bodyLength),
|
else {
|
||||||
NSLocationInRange(byteOffset, byteRange) else {
|
return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentKind != .protocol && allowedKinds.contains(kind) {
|
if parentKind != .protocol && allowedKinds.contains(kind) {
|
||||||
|
|
|
@ -35,11 +35,10 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, SubstitutionCorrect
|
||||||
return file.matchesAndSyntaxKinds(matching: pattern).compactMap { result, kinds in
|
return file.matchesAndSyntaxKinds(matching: pattern).compactMap { result, kinds in
|
||||||
let range = result.range
|
let range = result.range
|
||||||
guard kinds == [.keyword, .keyword] || kinds == [.keyword],
|
guard kinds == [.keyword, .keyword] || kinds == [.keyword],
|
||||||
let byteRange = contents.NSRangeToByteRange(start: range.location,
|
let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length),
|
||||||
length: range.length),
|
|
||||||
let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind
|
let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isKindIncluded(_ kind: ImplicitReturnConfiguration.ReturnKind) -> Bool {
|
func isKindIncluded(_ kind: ImplicitReturnConfiguration.ReturnKind) -> Bool {
|
||||||
|
|
|
@ -119,7 +119,8 @@ public struct LetVarWhitespaceRule: ConfigurationProviderRule, OptInRule, Automa
|
||||||
|
|
||||||
private func lineOffsets(file: SwiftLintFile, statement: SourceKittenDictionary) -> (Int, Int)? {
|
private func lineOffsets(file: SwiftLintFile, statement: SourceKittenDictionary) -> (Int, Int)? {
|
||||||
guard let offset = statement.offset,
|
guard let offset = statement.offset,
|
||||||
let length = statement.length else {
|
let length = statement.length
|
||||||
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let startLine = file.line(byteOffset: offset)
|
let startLine = file.line(byteOffset: offset)
|
||||||
|
@ -230,7 +231,7 @@ private extension SwiftDeclarationKind {
|
||||||
|
|
||||||
private extension SwiftLintFile {
|
private extension SwiftLintFile {
|
||||||
// Zero based line number for specified byte offset
|
// Zero based line number for specified byte offset
|
||||||
func line(byteOffset: Int) -> Int {
|
func line(byteOffset: ByteCount) -> Int {
|
||||||
let lineIndex = lines.firstIndexAssumingSorted { line in
|
let lineIndex = lines.firstIndexAssumingSorted { line in
|
||||||
return line.byteRange.location > byteOffset
|
return line.byteRange.location > byteOffset
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue