From fe5baca7cd62953c1b9e27413443093dda9d4e2f Mon Sep 17 00:00:00 2001 From: JP Simard Date: Thu, 16 Jan 2020 15:18:37 -0800 Subject: [PATCH] Migrate to use SourceKitten's new ByteCount/ByteRange types (#3037) New APIs were introduced in SourceKitten to allow for a more typesafe distinction between integers meaning NSString-based distances and byte-based distances. * https://github.com/jpsim/SourceKitten/pull/639 * https://github.com/jpsim/SourceKitten/pull/642 This PR migrates SwiftLint's use of those APIs. --- CHANGELOG.md | 7 +- Cartfile | 2 +- Cartfile.resolved | 2 +- Carthage/Checkouts/SourceKitten | 2 +- Package.resolved | 4 +- Package.swift | 2 +- .../Extensions/Dictionary+SwiftLint.swift | 48 ++++++---- .../SourceKittenDictionary+Swiftlint.swift | 25 +++--- .../Extensions/SwiftLintFile+Regex.swift | 25 +++--- .../Helpers/NamespaceCollector.swift | 2 +- .../SwiftLintFramework/Models/Location.swift | 4 +- .../Models/SwiftLintSyntaxMap.swift | 7 +- .../Models/SwiftLintSyntaxToken.swift | 9 +- .../Models/SwiftVersion.swift | 3 +- .../Protocols/CallPairRule.swift | 20 ++--- .../Rules/Idiomatic/ConvenienceTypeRule.swift | 6 +- .../DiscouragedOptionalCollectionRule.swift | 7 +- .../Idiomatic/DuplicateImportsRule.swift | 7 +- .../Rules/Idiomatic/ExplicitACLRule.swift | 8 +- .../Idiomatic/ExplicitEnumRawValueRule.swift | 2 +- .../Rules/Idiomatic/ExplicitInitRule.swift | 4 +- .../Idiomatic/ExplicitTopLevelACLRule.swift | 6 +- .../Idiomatic/ExplicitTypeInterfaceRule.swift | 23 +++-- .../ExtensionAccessModifierRule.swift | 33 +++---- .../Idiomatic/FatalErrorMessageRule.swift | 9 +- .../Rules/Idiomatic/ForWhereRule.swift | 13 ++- .../Rules/Idiomatic/ForceUnwrappingRule.swift | 10 +-- .../FunctionDefaultParameterAtEndRule.swift | 6 +- .../Rules/Idiomatic/GenericTypeNameRule.swift | 35 ++++---- .../JoinedDefaultParameterRule.swift | 17 ++-- .../Idiomatic/LegacyConstructorRule.swift | 12 ++- .../Idiomatic/NoFallthroughOnlyRule.swift | 10 +-- .../Idiomatic/NoGroupingExtensionRule.swift | 16 ++-- .../Rules/Idiomatic/ObjectLiteralRule.swift | 8 +- .../PatternMatchingKeywordsRule.swift | 8 +- .../PrivateOverFilePrivateRule.swift | 7 +- .../RedundantObjcAttributeRule.swift | 8 +- .../RedundantOptionalInitializationRule.swift | 8 +- .../RedundantStringEnumValueRule.swift | 16 ++-- .../Idiomatic/RedundantVoidReturnRule.swift | 3 +- .../Rules/Idiomatic/TypeNameRule.swift | 2 +- .../Idiomatic/UnavailableFunctionRule.swift | 8 +- .../Idiomatic/UnneededBreakInSwitchRule.swift | 8 +- .../Idiomatic/UnusedEnumeratedRule.swift | 7 +- .../Rules/Idiomatic/XCTFailMessageRule.swift | 10 +-- .../Idiomatic/XCTSpecificMatcherRule.swift | 10 +-- .../Rules/Lint/AnyObjectProtocolRule.swift | 11 ++- .../Rules/Lint/ArrayInitRule.swift | 43 ++++----- .../Lint/ClassDelegateProtocolRule.swift | 6 +- .../Rules/Lint/CompilerProtocolInitRule.swift | 7 +- .../Rules/Lint/DeploymentTargetRule.swift | 18 ++-- ...cardedNotificationCenterObserverRule.swift | 6 +- .../Rules/Lint/DuplicateEnumCasesRule.swift | 5 +- .../Rules/Lint/DynamicInlineRule.swift | 4 +- .../Rules/Lint/IdenticalOperandsRule.swift | 10 +-- .../Rules/Lint/InertDeferRule.swift | 4 +- .../Rules/Lint/LowerACLThanParentRule.swift | 6 +- .../Rules/Lint/MarkRule.swift | 4 +- .../Rules/Lint/MissingDocsRule.swift | 5 +- .../Rules/Lint/NSLocalizedStringKeyRule.swift | 8 +- .../NotificationCenterDetachmentRule.swift | 20 ++--- .../Rules/Lint/OrphanedDocCommentRule.swift | 4 +- .../Rules/Lint/OverrideInExtensionRule.swift | 7 +- .../Rules/Lint/QuickDiscouragedCallRule.swift | 8 +- ...RawValueForCamelCasedCodableEnumRule.swift | 8 +- .../Rules/Lint/RequiredEnumCaseRule.swift | 2 +- .../Rules/Lint/StrongIBOutletRule.swift | 5 +- .../Lint/UnownedVariableCaptureRule.swift | 27 +++--- .../Rules/Lint/UnusedCaptureListRule.swift | 8 +- .../Lint/UnusedClosureParameterRule.swift | 82 +++++++++-------- .../Lint/UnusedControlFlowLabelRule.swift | 24 +++-- .../Rules/Lint/UnusedDeclarationRule.swift | 18 ++-- .../Rules/Lint/UnusedImportRule.swift | 8 +- .../Rules/Lint/UnusedSetterValueRule.swift | 41 +++++---- .../Rules/Lint/WeakDelegateRule.swift | 12 ++- .../Rules/Lint/YodaConditionRule.swift | 16 +--- .../Metrics/CyclomaticComplexityRule.swift | 5 +- .../Metrics/FunctionParameterCountRule.swift | 29 +++--- .../Rules/Metrics/LargeTupleRule.swift | 15 ++-- .../Rules/Performance/FirstWhereRule.swift | 5 +- .../Rules/Performance/LastWhereRule.swift | 5 +- .../Rules/Performance/ReduceIntoRule.swift | 9 +- .../Rules/Style/AttributesRule.swift | 13 +-- .../Style/ClosureEndIndentationRule.swift | 89 ++++++++++--------- .../Style/ClosureParameterPositionRule.swift | 13 +-- .../Rules/Style/ClosureSpacingRule.swift | 2 +- .../Rules/Style/CollectionAlignmentRule.swift | 7 +- .../Rules/Style/ColonRule+Dictionary.swift | 28 +++--- .../Rules/Style/ColonRule+FunctionCall.swift | 21 ++--- .../Rules/Style/ColonRule+Type.swift | 6 +- .../Rules/Style/ColonRule.swift | 10 +-- .../Rules/Style/EmptyEnumArgumentsRule.swift | 20 ++--- ...tyParenthesesWithTrailingClosureRule.swift | 8 +- .../Rules/Style/ExplicitSelfRule.swift | 24 ++--- .../Rules/Style/FileHeaderRule.swift | 31 +++---- .../Rules/Style/FileTypesOrderRule.swift | 2 +- .../Rules/Style/IdentifierNameRule.swift | 2 +- .../Rules/Style/ImplicitGetterRule.swift | 14 ++- .../Rules/Style/ImplicitReturnRule.swift | 7 +- .../Rules/Style/LetVarWhitespaceRule.swift | 5 +- .../LiteralExpressionEndIdentationRule.swift | 24 +++-- .../Rules/Style/ModifierOrderRule.swift | 23 ++--- .../MultilineArgumentsBracketsRule.swift | 11 ++- .../Rules/Style/MultilineArgumentsRule.swift | 35 ++++---- .../Style/MultilineFunctionChainsRule.swift | 38 ++++---- .../Style/MultilineLiteralBracketsRule.swift | 12 ++- .../MultilineParametersBracketsRule.swift | 21 ++--- .../Rules/Style/MultilineParametersRule.swift | 43 ++++----- ...tipleClosuresWithTrailingClosureRule.swift | 10 +-- .../Rules/Style/NoSpaceInMethodCallRule.swift | 7 +- .../Rules/Style/NumberSeparatorRule.swift | 6 +- .../Style/OptionalEnumCaseMatchingRule.swift | 16 ++-- .../Style/RedundantDiscardableLetRule.swift | 14 ++- .../Style/ReturnArrowWhitespaceRule.swift | 2 +- .../Rules/Style/ShorthandOperatorRule.swift | 2 +- .../Rules/Style/SwitchCaseOnNewlineRule.swift | 5 +- .../Rules/Style/TrailingClosureRule.swift | 38 ++++---- .../Rules/Style/TrailingCommaRule.swift | 77 ++++++++-------- .../Rules/Style/TypeContentsOrderRule.swift | 4 +- .../Style/UnusedOptionalBindingRule.swift | 8 +- ...VerticalParameterAlignmentOnCallRule.swift | 13 +-- .../VerticalParameterAlignmentRule.swift | 10 ++- SwiftLintFramework.podspec | 2 +- 123 files changed, 829 insertions(+), 878 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d47790b..f89382288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ #### Breaking -* None. +* Replace all uses of `Int`/`Int64`/`NSRange` representing byte offsets + to use newly introduced `ByteCount` and `ByteRange` values instead. + This will minimize the risk of accidentally using a byte-based offset + in character-based contexts. + [Paul Taykalo](https://github.com/PaulTaykalo) + [JP Simard](https://github.com/jpsim) #### Experimental diff --git a/Cartfile b/Cartfile index 8f8c7e778..4133a398e 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ -github "jpsim/SourceKitten" ~> 0.28.0 +github "jpsim/SourceKitten" ~> 0.29.0 github "scottrhoyt/SwiftyTextTable" ~> 0.9.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index ee4dae87b..3b30eaea3 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,6 +1,6 @@ github "Carthage/Commandant" "0.17.0" github "drmohundro/SWXMLHash" "5.0.1" -github "jpsim/SourceKitten" "0.28.0" +github "jpsim/SourceKitten" "0.29.0" github "jpsim/Yams" "2.0.0" github "jspahrsummers/xcconfigs" "0.12" github "scottrhoyt/SwiftyTextTable" "0.9.0" diff --git a/Carthage/Checkouts/SourceKitten b/Carthage/Checkouts/SourceKitten index 97b5848e5..77a4dbbb4 160000 --- a/Carthage/Checkouts/SourceKitten +++ b/Carthage/Checkouts/SourceKitten @@ -1 +1 @@ -Subproject commit 97b5848e5692150d75b5cf0b81d7ebef5f4d5071 +Subproject commit 77a4dbbb477a8110eb8765e3c44c70fb4929098f diff --git a/Package.resolved b/Package.resolved index 065b35d23..ad29737ff 100644 --- a/Package.resolved +++ b/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/jpsim/SourceKitten.git", "state": { "branch": null, - "revision": "97b5848e5692150d75b5cf0b81d7ebef5f4d5071", - "version": "0.28.0" + "revision": "77a4dbbb477a8110eb8765e3c44c70fb4929098f", + "version": "0.29.0" } }, { diff --git a/Package.swift b/Package.swift index 0acda269d..99a64d209 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/Carthage/Commandant.git", .upToNextMinor(from: "0.17.0")), - .package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.28.0"), + .package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.29.0")), .package(url: "https://github.com/jpsim/Yams.git", from: "2.0.0"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.9.0"), ] + (addCryptoSwift ? [.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMinor(from: "1.0.0"))] : []), diff --git a/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift index d2a5906ba..9fdcdc1c0 100644 --- a/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift @@ -38,13 +38,19 @@ public struct SourceKittenDictionary { } /// Body length - var bodyLength: Int? { - return (value["key.bodylength"] as? Int64).flatMap({ Int($0) }) + var bodyLength: ByteCount? { + return (value["key.bodylength"] as? Int64).map(ByteCount.init) } /// Body offset. - var bodyOffset: Int? { - return (value["key.bodyoffset"] as? Int64).flatMap({ Int($0) }) + var bodyOffset: ByteCount? { + return (value["key.bodyoffset"] as? Int64).map(ByteCount.init) + } + + /// Body byte range. + var bodyByteRange: ByteRange? { + guard let offset = bodyOffset, let length = bodyLength else { return nil } + return ByteRange(location: offset, length: length) } /// Kind. @@ -53,8 +59,8 @@ public struct SourceKittenDictionary { } /// Length. - var length: Int? { - return (value["key.length"] as? Int64).flatMap({ Int($0) }) + var length: ByteCount? { + return (value["key.length"] as? Int64).map(ByteCount.init) } /// Name. var name: String? { @@ -62,24 +68,30 @@ public struct SourceKittenDictionary { } /// Name length. - var nameLength: Int? { - return (value["key.namelength"] as? Int64).flatMap({ Int($0) }) + var nameLength: ByteCount? { + return (value["key.namelength"] as? Int64).map(ByteCount.init) } /// Name offset. - var nameOffset: Int? { - return (value["key.nameoffset"] as? Int64).flatMap({ Int($0) }) + var nameOffset: ByteCount? { + return (value["key.nameoffset"] as? Int64).map(ByteCount.init) + } + + /// Byte range of name. + var nameByteRange: ByteRange? { + guard let offset = nameOffset, let length = nameLength else { return nil } + return ByteRange(location: offset, length: length) } /// Offset. - var offset: Int? { - return (value["key.offset"] as? Int64).flatMap({ Int($0) }) + var offset: ByteCount? { + return (value["key.offset"] as? Int64).map(ByteCount.init) } /// Returns byte range starting from `offset` with `length` bytes - var byteRange: NSRange? { + var byteRange: ByteRange? { guard let offset = offset, let length = length else { return nil } - return NSRange(location: offset, length: length) + return ByteRange(location: offset, length: length) } /// Setter accessibility. @@ -93,13 +105,13 @@ public struct SourceKittenDictionary { } /// Documentation offset. - var docOffset: Int? { - return (value["key.docoffset"] as? Int64).flatMap({ Int($0) }) + var docOffset: ByteCount? { + return (value["key.docoffset"] as? Int64).flatMap(ByteCount.init) } /// Documentation length. - var docLength: Int? { - return (value["key.doclength"] as? Int64).flatMap({ Int($0) }) + var docLength: ByteCount? { + return (value["key.doclength"] as? Int64).flatMap(ByteCount.init) } /// The attribute for this dictionary, as returned by SourceKit. diff --git a/Source/SwiftLintFramework/Extensions/SourceKittenDictionary+Swiftlint.swift b/Source/SwiftLintFramework/Extensions/SourceKittenDictionary+Swiftlint.swift index 8863f84f6..2fd028b65 100644 --- a/Source/SwiftLintFramework/Extensions/SourceKittenDictionary+Swiftlint.swift +++ b/Source/SwiftLintFramework/Extensions/SourceKittenDictionary+Swiftlint.swift @@ -1,4 +1,4 @@ -import Foundation +import SourceKittenFramework extension SourceKittenDictionary { /// Returns array of tuples containing "key.kind" and "byteRange" from Structure @@ -7,20 +7,19 @@ extension SourceKittenDictionary { /// - parameter byteOffset: Int? /// /// - returns: The kinds and byte ranges. - internal func kinds(forByteOffset byteOffset: Int? = nil) - -> [(kind: String, byteRange: NSRange)] { - var results = [(kind: String, byteRange: NSRange)]() + internal func kinds(forByteOffset byteOffset: ByteCount? = nil) + -> [(kind: String, byteRange: ByteRange)] { + var results = [(kind: String, byteRange: ByteRange)]() func parse(_ dictionary: SourceKittenDictionary) { - guard let offset = dictionary.offset, - let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }) else { - return + guard let range = dictionary.byteRange else { + return } - if let byteOffset = byteOffset, !NSLocationInRange(byteOffset, byteRange) { + if let byteOffset = byteOffset, !range.contains(byteOffset) { return } if let kind = dictionary.kind { - results.append((kind: kind, byteRange: byteRange)) + results.append((kind: kind, byteRange: range)) } dictionary.substructure.forEach(parse) } @@ -28,14 +27,12 @@ extension SourceKittenDictionary { return results } - internal func structures(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] { + internal func structures(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] { var results = [SourceKittenDictionary]() func parse(_ dictionary: SourceKittenDictionary) { - guard let offset = dictionary.offset, - let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }), - NSLocationInRange(byteOffset, byteRange) else { - return + guard let byteRange = dictionary.byteRange, byteRange.contains(byteOffset) else { + return } results.append(dictionary) diff --git a/Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift b/Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift index 98815b346..bc800bde4 100644 --- a/Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift +++ b/Source/SwiftLintFramework/Extensions/SwiftLintFile+Regex.swift @@ -105,13 +105,11 @@ extension SwiftLintFile { internal func matchesAndTokens(matching pattern: String, range: NSRange? = nil) -> [(NSTextCheckingResult, [SwiftLintSyntaxToken])] { let contents = stringView - let range = range ?? stringView.range + let range = range ?? contents.range let syntax = syntaxMap - return regex(pattern).matches(in: contents, options: [], range: range).map { match in - let matchByteRange = contents.NSRangeToByteRange(start: match.range.location, - length: match.range.length) ?? match.range - let tokensInRange = syntax.tokens(inByteRange: matchByteRange) - return (match, tokensInRange) + return regex(pattern).matches(in: contents, options: [], range: range).compactMap { match in + let matchByteRange = contents.NSRangeToByteRange(start: match.range.location, length: match.range.length) + return matchByteRange.map { (match, syntax.tokens(inByteRange: $0)) } } } @@ -143,12 +141,11 @@ extension SwiftLintFile { var maybeLine = lineIterator.next() var maybeStructure = structureIterator.next() while let line = maybeLine, let structure = maybeStructure { - if NSLocationInRange(structure.byteRange.location, line.byteRange), + if line.byteRange.contains(structure.byteRange.location), let swiftDeclarationKind = SwiftDeclarationKind(rawValue: structure.kind) { results[line.index].append(swiftDeclarationKind) } - let lineEnd = NSMaxRange(line.byteRange) - if structure.byteRange.location >= lineEnd { + if structure.byteRange.location >= line.byteRange.upperBound { maybeLine = lineIterator.next() } else { maybeStructure = structureIterator.next() @@ -168,12 +165,12 @@ extension SwiftLintFile { var maybeToken = tokenGenerator.next() while let line = maybeLine, let token = maybeToken { let tokenRange = token.range - if NSLocationInRange(token.offset, line.byteRange) || - NSLocationInRange(line.byteRange.location, tokenRange) { + if line.byteRange.contains(token.offset) || + tokenRange.contains(line.byteRange.location) { results[line.index].append(token) } - let tokenEnd = NSMaxRange(tokenRange) - let lineEnd = NSMaxRange(line.byteRange) + let tokenEnd = tokenRange.upperBound + let lineEnd = line.byteRange.upperBound if tokenEnd < lineEnd { maybeToken = tokenGenerator.next() } else if tokenEnd > lineEnd { @@ -337,6 +334,6 @@ extension SwiftLintFile { } internal func contents(for token: SwiftLintSyntaxToken) -> String? { - return stringView.substringWithByteRange(start: token.offset, length: token.length) + return stringView.substringWithByteRange(token.range) } } diff --git a/Source/SwiftLintFramework/Helpers/NamespaceCollector.swift b/Source/SwiftLintFramework/Helpers/NamespaceCollector.swift index f119c167b..654ff1837 100644 --- a/Source/SwiftLintFramework/Helpers/NamespaceCollector.swift +++ b/Source/SwiftLintFramework/Helpers/NamespaceCollector.swift @@ -4,7 +4,7 @@ struct NamespaceCollector { struct Element { let name: String let kind: SwiftDeclarationKind - let offset: Int + let offset: ByteCount let dictionary: SourceKittenDictionary init?(dictionary: SourceKittenDictionary, namespace: [String]) { diff --git a/Source/SwiftLintFramework/Models/Location.swift b/Source/SwiftLintFramework/Models/Location.swift index 4a2021231..6ef880972 100644 --- a/Source/SwiftLintFramework/Models/Location.swift +++ b/Source/SwiftLintFramework/Models/Location.swift @@ -37,11 +37,11 @@ public struct Location: CustomStringConvertible, Comparable, Codable { } /// Creates a `Location` based on a `SwiftLintFile` and a byte-offset into the file. - /// Fails if tthe specified offset was not a valid location in the file. + /// Fails if the specified offset was not a valid location in the file. /// /// - parameter file: The file for this location. /// - parameter offset: The offset in bytes into the file for this location. - public init(file: SwiftLintFile, byteOffset offset: Int) { + public init(file: SwiftLintFile, byteOffset offset: ByteCount) { self.file = file.path if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) { line = lineAndCharacter.line diff --git a/Source/SwiftLintFramework/Models/SwiftLintSyntaxMap.swift b/Source/SwiftLintFramework/Models/SwiftLintSyntaxMap.swift index 0efd8d4e4..31f2e89f3 100644 --- a/Source/SwiftLintFramework/Models/SwiftLintSyntaxMap.swift +++ b/Source/SwiftLintFramework/Models/SwiftLintSyntaxMap.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework /// Represents a Swift file's syntax information. @@ -22,7 +21,7 @@ public struct SwiftLintSyntaxMap { /// - parameter byteRange: Byte-based NSRange. /// /// - returns: The array of syntax tokens intersecting with byte range. - internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] { + internal func tokens(inByteRange byteRange: ByteRange) -> [SwiftLintSyntaxToken] { func intersect(_ token: SwiftLintSyntaxToken) -> Bool { return token.range.intersects(byteRange) } @@ -46,10 +45,10 @@ public struct SwiftLintSyntaxMap { /// Returns the syntax kinds in the specified byte range. /// - /// - parameter byteRange: Byte-based NSRange. + /// - parameter byteRange: Byte range. /// /// - returns: The syntax kinds in the specified byte range. - internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] { + internal func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] { return tokens(inByteRange: byteRange).compactMap { $0.kind } } } diff --git a/Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift b/Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift index d8a333972..99e490de2 100644 --- a/Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift +++ b/Source/SwiftLintFramework/Models/SwiftLintSyntaxToken.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework /// A SwiftLint-aware Swift syntax token. @@ -18,17 +17,17 @@ public struct SwiftLintSyntaxToken { } /// The byte range in a source file for this token. - public var range: NSRange { - return NSRange(location: value.offset, length: value.length) + public var range: ByteRange { + return value.range } /// The starting byte offset in a source file for this token. - public var offset: Int { + public var offset: ByteCount { return value.offset } /// The length in bytes for this token. - public var length: Int { + public var length: ByteCount { return value.length } } diff --git a/Source/SwiftLintFramework/Models/SwiftVersion.swift b/Source/SwiftLintFramework/Models/SwiftVersion.swift index 0cb6193f6..544282467 100644 --- a/Source/SwiftLintFramework/Models/SwiftVersion.swift +++ b/Source/SwiftLintFramework/Models/SwiftVersion.swift @@ -111,7 +111,8 @@ public extension SwiftVersion { let decl = file.structureDictionary.kinds() .first(where: { $0.kind == SwiftDeclarationKind.varGlobal.rawValue }), let token = file.syntaxMap.tokens(inByteRange: decl.byteRange).first(where: { $0.kind == .string }) { - return .init(rawValue: file.contents.substring(from: token.offset + 1, length: token.length - 2)) + let offsetRange = ByteRange(location: token.offset + 1, length: token.length - 2) + return .init(rawValue: file.stringView.substringWithByteRange(offsetRange)!) } return .three diff --git a/Source/SwiftLintFramework/Protocols/CallPairRule.swift b/Source/SwiftLintFramework/Protocols/CallPairRule.swift index c6d83ef2e..e033079e2 100644 --- a/Source/SwiftLintFramework/Protocols/CallPairRule.swift +++ b/Source/SwiftLintFramework/Protocols/CallPairRule.swift @@ -36,7 +36,7 @@ extension CallPairRule { let stringView = file.stringView let dictionary = file.structureDictionary - let violatingLocations: [Int] = firstRanges.compactMap { range in + let violatingLocations: [ByteCount] = firstRanges.compactMap { range in guard let bodyByteRange = stringView.NSRangeToByteRange(start: range.location, length: range.length), case let firstLocation = range.location + range.length - 1, let firstByteRange = stringView.NSRangeToByteRange(start: firstLocation, length: 1) else { @@ -63,18 +63,14 @@ extension CallPairRule { } } - private func methodCall(forByteOffset byteOffset: Int, excludingOffset: Int, + private func methodCall(forByteOffset byteOffset: ByteCount, excludingOffset: ByteCount, dictionary: SourceKittenDictionary, - predicate: (SourceKittenDictionary) -> Bool) -> Int? { - if dictionary.expressionKind == .call, - let bodyOffset = dictionary.offset, - let bodyLength = dictionary.length, - let offset = dictionary.offset { - let byteRange = NSRange(location: bodyOffset, length: bodyLength) - - if NSLocationInRange(byteOffset, byteRange) && - !NSLocationInRange(excludingOffset, byteRange) && predicate(dictionary) { - return offset + predicate: (SourceKittenDictionary) -> Bool) -> ByteCount? { + if dictionary.expressionKind == .call, let byteRange = dictionary.byteRange { + if byteRange.contains(byteOffset) && + !byteRange.contains(excludingOffset) && + predicate(dictionary) { + return dictionary.offset } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ConvenienceTypeRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ConvenienceTypeRule.swift index d69f5acd4..b2cfc2cc8 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ConvenienceTypeRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ConvenienceTypeRule.swift @@ -97,9 +97,9 @@ public struct ConvenienceTypeRule: ASTRule, OptInRule, ConfigurationProviderRule private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { return dictionary.swiftAttributes.contains { dict -> Bool in guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available, - let offset = dict.offset, let length = dict.length, - let contents = file.stringView.substringWithByteRange(start: offset, length: length) else { - return false + let contents = dict.byteRange.flatMap(file.stringView.substringWithByteRange) + else { + return false } return contents.contains("unavailable") diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift index dba011b28..61f9fde8c 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/DiscouragedOptionalCollectionRule.swift @@ -31,7 +31,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati private func variableViolations(file: SwiftLintFile, kind: SwiftDeclarationKind, - dictionary: SourceKittenDictionary) -> [Int] { + dictionary: SourceKittenDictionary) -> [ByteCount] { guard SwiftDeclarationKind.variableKinds.contains(kind), let offset = dictionary.offset, @@ -42,7 +42,7 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati private func functionViolations(file: SwiftLintFile, kind: SwiftDeclarationKind, - dictionary: SourceKittenDictionary) -> [Int] { + dictionary: SourceKittenDictionary) -> [ByteCount] { guard SwiftDeclarationKind.functionKinds.contains(kind), let nameOffset = dictionary.nameOffset, @@ -51,8 +51,9 @@ public struct DiscouragedOptionalCollectionRule: ASTRule, OptInRule, Configurati let offset = dictionary.offset, case let start = nameOffset + nameLength, case let end = dictionary.bodyOffset ?? offset + length, + case let byteRange = ByteRange(location: start, length: end - start), case let contents = file.stringView, - let range = file.stringView.byteRangeToNSRange(start: start, length: end - start), + let range = file.stringView.byteRangeToNSRange(byteRange), let match = file.match(pattern: "->\\s*(.*?)\\{", excludingSyntaxKinds: excludingKinds, range: range).first else { return [] } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/DuplicateImportsRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/DuplicateImportsRule.swift index b54bf83c4..ad5db13ad 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/DuplicateImportsRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/DuplicateImportsRule.swift @@ -22,19 +22,18 @@ public struct DuplicateImportsRule: ConfigurationProviderRule, AutomaticTestable triggeringExamples: DuplicateImportsRuleExamples.triggeringExamples ) - private func rangesInConditionalCompilation(file: SwiftLintFile) -> [NSRange] { + private func rangesInConditionalCompilation(file: SwiftLintFile) -> [ByteRange] { let contents = file.stringView let ranges = file.syntaxMap.tokens .filter { $0.kind == .buildconfigKeyword } .map { $0.range } .filter { range in - let keyword = contents.substringWithByteRange(start: range.location, length: range.length) - return ["#if", "#endif"].contains(keyword) + return ["#if", "#endif"].contains(contents.substringWithByteRange(range)) } return stride(from: 0, to: ranges.count, by: 2).reduce(into: []) { result, rangeIndex in - result.append(NSUnionRange(ranges[rangeIndex], ranges[rangeIndex + 1])) + result.append(ranges[rangeIndex].union(with: ranges[rangeIndex + 1])) } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitACLRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitACLRule.swift index 333d9823b..741eb6466 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitACLRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitACLRule.swift @@ -53,7 +53,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe ] ) - private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [NSRange] { + private func findAllExplicitInternalTokens(in file: SwiftLintFile) -> [ByteRange] { let contents = file.stringView return file.match(pattern: "internal", with: [.attributeBuiltin]).compactMap { contents.NSRangeToByteRange(start: $0.location, length: $0.length) @@ -61,7 +61,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe } private func offsetOfElements(from elements: [SourceKittenElement], in file: SwiftLintFile, - thatAreNotInRanges ranges: [NSRange]) -> [Int] { + thatAreNotInRanges ranges: [ByteRange]) -> [ByteCount] { let extensionKinds: Set = [.extension, .extensionClass, .extensionEnum, .extensionProtocol, .extensionStruct] @@ -83,7 +83,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe // the "internal" token correspond to the type if there're only // attributeBuiltin (`final` for example) tokens between them let length = typeOffset - previousInternalByteRange.location - let range = NSRange(location: previousInternalByteRange.location, length: length) + let range = ByteRange(location: previousInternalByteRange.location, length: length) let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin] return internalDoesntBelongToType ? typeOffset : nil @@ -109,7 +109,7 @@ public struct ExplicitACLRule: OptInRule, ConfigurationProviderRule, AutomaticTe } } - private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { + private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? { let firstPartition = ranges.prefix(while: { typeOffset > $0.location }) return firstPartition.last } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitEnumRawValueRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitEnumRawValueRule.swift index 68c64aaf6..a3bedb78d 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitEnumRawValueRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitEnumRawValueRule.swift @@ -100,7 +100,7 @@ public struct ExplicitEnumRawValueRule: ASTRule, OptInRule, ConfigurationProvide } } - private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] { + private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] { let locs = substructureElements(of: dictionary, matching: .enumcase) .compactMap { substructureElements(of: $0, matching: .enumelement) } .flatMap(enumElementsMissingInitExpr) diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitInitRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitInitRule.swift index 154a437bf..4fa8ae029 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitInitRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitInitRule.swift @@ -71,14 +71,14 @@ public struct ExplicitInitRule: SubstitutionCorrectableASTRule, ConfigurationPro && initializerWithType.numberOfMatches(in: name, options: [], range: range) != 0 } - let length = ".init".utf8.count + let length = ByteCount(".init".utf8.count) guard kind == .call, let name = dictionary.name, isExpected(name), let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, let range = file.stringView - .byteRangeToNSRange(start: nameOffset + nameLength - length, length: length) + .byteRangeToNSRange(ByteRange(location: nameOffset + nameLength - length, length: length)) else { return [] } return [range] } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTopLevelACLRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTopLevelACLRule.swift index 351191705..d1f3a4d11 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTopLevelACLRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTopLevelACLRule.swift @@ -37,7 +37,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut // find all top-level types marked as internal (either explictly or implictly) let dictionary = file.structureDictionary - let internalTypesOffsets = dictionary.substructure.compactMap { element -> Int? in + let internalTypesOffsets = dictionary.substructure.compactMap { element -> ByteCount? in // ignore extensions guard let kind = element.declarationKind, !extensionKinds.contains(kind) else { @@ -72,7 +72,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut // the "internal" token correspond to the type if there're only // attributeBuiltin (`final` for example) tokens between them let length = typeOffset - previousInternalByteRange.location - let range = NSRange(location: previousInternalByteRange.location, length: length) + let range = ByteRange(location: previousInternalByteRange.location, length: length) let internalDoesntBelongToType = Set(file.syntaxMap.kinds(inByteRange: range)) != [.attributeBuiltin] return internalDoesntBelongToType @@ -85,7 +85,7 @@ public struct ExplicitTopLevelACLRule: OptInRule, ConfigurationProviderRule, Aut } } - private func lastInternalByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { + private func lastInternalByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? { let firstPartition = ranges.prefix(while: { typeOffset > $0.location }) return firstPartition.last } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift index 50d4e4e99..6806300cf 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExplicitTypeInterfaceRule.swift @@ -108,7 +108,8 @@ private extension SourceKittenDictionary { guard let nameOffset = nameOffset, let nameLength = nameLength, - let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0) + case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0), + let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange) else { return false } @@ -125,7 +126,8 @@ private extension SourceKittenDictionary { guard let nameOffset = nameOffset, let nameLength = nameLength, - let afterNameRange = file.stringView.byteRangeToNSRange(start: nameOffset + nameLength, length: 0) + case let afterNameByteRange = ByteRange(location: nameOffset + nameLength, length: 0), + let afterNameRange = file.stringView.byteRangeToNSRange(afterNameByteRange) else { return false } @@ -137,11 +139,11 @@ private extension SourceKittenDictionary { return typeAssignment.firstMatch(in: contentAfterName, options: [], range: contentAfterName.fullNSRange) != nil } - var caseStatementPatternRanges: [NSRange] { + var caseStatementPatternRanges: [ByteRange] { return ranges(with: StatementKind.case.rawValue, for: "source.lang.swift.structure.elem.pattern") } - var caseExpressionRanges: [NSRange] { + var caseExpressionRanges: [ByteRange] { return ranges(with: SwiftExpressionKind.tuple.rawValue, for: "source.lang.swift.structure.elem.expr") } @@ -152,30 +154,27 @@ private extension SourceKittenDictionary { return statements.contains(statement) } - func ranges(with parentKind: String, for elementKind: String) -> [NSRange] { + func ranges(with parentKind: String, for elementKind: String) -> [ByteRange] { guard parentKind == kind else { return [] } return elements .filter { elementKind == $0.kind } - .compactMap { - guard let location = $0.offset, let length = $0.length else { return nil } - return NSRange(location: location, length: length) - } + .compactMap { $0.byteRange } } } private extension SwiftLintFile { - var captureGroupByteRanges: [NSRange] { + var captureGroupByteRanges: [ByteRange] { return match(pattern: "\\{\\s*\\[(\\s*\\w+\\s+\\w+,*)+\\]", excludingSyntaxKinds: SyntaxKind.commentKinds) .compactMap { stringView.NSRangeToByteRange(start: $0.location, length: $0.length) } } } -private extension Collection where Element == NSRange { - func contains(_ index: Int) -> Bool { +private extension Collection where Element == ByteRange { + func contains(_ index: ByteCount) -> Bool { return contains { $0.contains(index) } } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ExtensionAccessModifierRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ExtensionAccessModifierRule.swift index dd6a50bca..382120d73 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ExtensionAccessModifierRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ExtensionAccessModifierRule.swift @@ -79,20 +79,23 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { guard kind == .extension, let offset = dictionary.offset, - dictionary.inheritedTypes.isEmpty else { - return [] + dictionary.inheritedTypes.isEmpty + else { + return [] } - let declarations = dictionary.substructure.compactMap { entry -> (acl: AccessControlLevel, offset: Int)? in - guard entry.declarationKind != nil, - let acl = entry.accessibility, - let offset = entry.offset else { - return nil + let declarations = dictionary.substructure + .compactMap { entry -> (acl: AccessControlLevel, offset: ByteCount)? in + guard entry.declarationKind != nil, + let acl = entry.accessibility, + let offset = entry.offset + else { + return nil + } + + return (acl: acl, offset: offset) } - return (acl: acl, offset: offset) - } - let declarationsACLs = declarations.map { $0.acl }.unique let allowedACLs: Set = [.internal, .private, .open] guard declarationsACLs.count == 1, !allowedACLs.contains(declarationsACLs[0]) else { @@ -115,11 +118,11 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O } private func declarationsViolations(file: SwiftLintFile, acl: AccessControlLevel, - declarationOffsets: [Int], + declarationOffsets: [ByteCount], dictionary: SourceKittenDictionary) -> [StyleViolation] { - guard let offset = dictionary.offset, let length = dictionary.length, + guard let byteRange = dictionary.byteRange, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { + let range = contents.byteRangeToNSRange(byteRange) else { return [] } @@ -138,7 +141,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O // the ACL token correspond to the type if there're only // attributeBuiltin (`final` for example) tokens between them let length = typeOffset - previousInternalByteRange.location - let range = NSRange(location: previousInternalByteRange.location, length: length) + let range = ByteRange(location: previousInternalByteRange.location, length: length) let internalBelongsToType = Set(file.syntaxMap.kinds(inByteRange: range)) == [.attributeBuiltin] return internalBelongsToType @@ -151,7 +154,7 @@ public struct ExtensionAccessModifierRule: ASTRule, ConfigurationProviderRule, O } } - private func lastACLByteRange(before typeOffset: Int, in ranges: [NSRange]) -> NSRange? { + private func lastACLByteRange(before typeOffset: ByteCount, in ranges: [ByteRange]) -> ByteRange? { let firstPartition = ranges.partitioned(by: { $0.location > typeOffset }).first return firstPartition.last } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/FatalErrorMessageRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/FatalErrorMessageRule.swift index 6c998ace2..e4ee5a0e0 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/FatalErrorMessageRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/FatalErrorMessageRule.swift @@ -53,16 +53,15 @@ public struct FatalErrorMessageRule: ASTRule, ConfigurationProviderRule, OptInRu } private func hasEmptyBody(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { - guard let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength else { - return false + guard let bodyRange = dictionary.bodyByteRange else { + return false } - if bodyLength == 0 { + if bodyRange.length == 0 { return true } - let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) + let body = file.stringView.substringWithByteRange(bodyRange) return body == "\"\"" } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ForWhereRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ForWhereRule.swift index 74433b2af..5cc1df681 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ForWhereRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ForWhereRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -133,9 +132,9 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl return false } - let beforeIfRange = NSRange(location: offset, length: ifOffset - offset) + let beforeIfRange = ByteRange(location: offset, length: ifOffset - offset) let ifFinalPosition = ifOffset + ifLength - let afterIfRange = NSRange(location: ifFinalPosition, length: offset + length - ifFinalPosition) + let afterIfRange = ByteRange(location: ifFinalPosition, length: offset + length - ifFinalPosition) let allKinds = file.syntaxMap.kinds(inByteRange: beforeIfRange) + file.syntaxMap.kinds(inByteRange: afterIfRange) @@ -148,13 +147,11 @@ public struct ForWhereRule: ASTRule, ConfigurationProviderRule, AutomaticTestabl private func isComplexCondition(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { let kind = "source.lang.swift.structure.elem.condition_expr" - let contents = file.stringView return dictionary.elements.contains { element in guard element.kind == kind, - let offset = element.offset, - let length = element.length, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { - return false + let range = element.byteRange.flatMap(file.stringView.byteRangeToNSRange) + else { + return false } let containsKeyword = !file.match(pattern: "\\blet|var|case\\b", with: [.keyword], range: range).isEmpty diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ForceUnwrappingRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ForceUnwrappingRule.swift index 76237cb38..23e6b9786 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ForceUnwrappingRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ForceUnwrappingRule.swift @@ -152,7 +152,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat } // check if first captured range is comment, string, typeidentifier, or a keyword that is not `self`. - private func isFirstRangeExcludedToken(byteRange: NSRange, syntaxMap: SwiftLintSyntaxMap, + private func isFirstRangeExcludedToken(byteRange: ByteRange, syntaxMap: SwiftLintSyntaxMap, file: SwiftLintFile) -> Bool { let tokens = syntaxMap.tokens(inByteRange: byteRange) return tokens.contains { token in @@ -166,7 +166,7 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat } // check deepest kind matching range in structure is a typeAnnotation - private func isTypeAnnotation(in file: SwiftLintFile, byteRange: NSRange) -> Bool { + private func isTypeAnnotation(in file: SwiftLintFile, byteRange: ByteRange) -> Bool { let kinds = file.structureDictionary.kinds(forByteOffset: byteRange.location) guard let lastItem = kinds.last, let lastKind = SwiftDeclarationKind(rawValue: lastItem.kind), @@ -175,9 +175,9 @@ public struct ForceUnwrappingRule: OptInRule, ConfigurationProviderRule, Automat } // range is in some "source.lang.swift.decl.var.*" - let byteOffset = lastItem.byteRange.location - let byteLength = byteRange.location - byteOffset - if let varDeclarationString = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength), + let varRange = ByteRange(location: lastItem.byteRange.location, + length: byteRange.location - lastItem.byteRange.location) + if let varDeclarationString = file.stringView.substringWithByteRange(varRange), varDeclarationString.contains("=") { // if declarations contains "=", range is not type annotation return false diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift index 2c7be58cf..b9face616 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/FunctionDefaultParameterAtEndRule.swift @@ -97,10 +97,8 @@ public struct FunctionDefaultParameterAtEndRule: ASTRule, ConfigurationProviderR } private func isDefaultParameter(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { - let contents = file.stringView - guard let offset = dictionary.offset, let length = dictionary.length, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { - return false + guard let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange) else { + return false } return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/GenericTypeNameRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/GenericTypeNameRule.swift index fd9f0c900..7fbe7db5f 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/GenericTypeNameRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/GenericTypeNameRule.swift @@ -71,15 +71,16 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule { } else { guard kind == .genericTypeParam, let name = dictionary.name, - let offset = dictionary.offset else { - return [] + let offset = dictionary.offset + else { + return [] } return validate(name: name, file: file, offset: offset) } } - private func validate(name: String, file: SwiftLintFile, offset: Int) -> [StyleViolation] { + private func validate(name: String, file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] { guard !configuration.excluded.contains(name) else { return [] } @@ -122,7 +123,7 @@ extension GenericTypeNameRule { private func validateGenericTypeAliases(in file: SwiftLintFile) -> [StyleViolation] { let pattern = "typealias\\s+\\w+?\\s*" + type(of: self).genericTypePattern + "\\s*=" - return file.match(pattern: pattern).flatMap { range, tokens -> [(String, Int)] in + return file.match(pattern: pattern).flatMap { range, tokens -> [(String, ByteCount)] in guard tokens.first == .keyword, Set(tokens.dropFirst()) == [.identifier], let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [], @@ -136,7 +137,7 @@ extension GenericTypeNameRule { } private func genericTypesForType(in file: SwiftLintFile, kind: SwiftDeclarationKind, - dictionary: SourceKittenDictionary) -> [(String, Int)] { + dictionary: SourceKittenDictionary) -> [(String, ByteCount)] { guard SwiftDeclarationKind.typeKinds.contains(kind), let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, @@ -144,7 +145,8 @@ extension GenericTypeNameRule { case let contents = file.stringView, case let start = nameOffset + nameLength, case let length = bodyOffset - start, - let range = file.stringView.byteRangeToNSRange(start: start, length: length), + case let byteRange = ByteRange(location: start, length: length), + let range = file.stringView.byteRangeToNSRange(byteRange), let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [], range: range)?.range(at: 1) else { return [] @@ -155,16 +157,18 @@ extension GenericTypeNameRule { } private func genericTypesForFunction(in file: SwiftLintFile, kind: SwiftDeclarationKind, - dictionary: SourceKittenDictionary) -> [(String, Int)] { + dictionary: SourceKittenDictionary) -> [(String, ByteCount)] { guard SwiftDeclarationKind.functionKinds.contains(kind), let offset = dictionary.nameOffset, let length = dictionary.nameLength, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: offset, length: length), + case let byteRange = ByteRange(location: offset, length: length), + let range = contents.byteRangeToNSRange(byteRange), let match = type(of: self).genericTypeRegex.firstMatch(in: file.contents, options: [], range: range)?.range(at: 1), - match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file) else { - return [] + match.location < minParameterOffset(parameters: dictionary.enclosedVarParameters, file: file) + else { + return [] } let genericConstraint = contents.substring(with: match) @@ -174,7 +178,7 @@ extension GenericTypeNameRule { private func minParameterOffset(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> Int { let offsets = parameters.compactMap { param -> Int? in return param.offset.flatMap { - file.stringView.byteRangeToNSRange(start: $0, length: 0)?.location + file.stringView.byteRangeToNSRange(ByteRange(location: $0, length: 0))?.location } } @@ -182,7 +186,7 @@ extension GenericTypeNameRule { } private func extractTypes(fromGenericConstraint constraint: String, offset: Int, - file: SwiftLintFile) -> [(String, Int)] { + file: SwiftLintFile) -> [(String, ByteCount)] { guard let beforeWhere = constraint.components(separatedBy: "where").first else { return [] } @@ -196,7 +200,7 @@ extension GenericTypeNameRule { } let contents = file.stringView - return namesAndRanges.compactMap { name, range -> (String, Int)? in + return namesAndRanges.compactMap { name, range -> (String, ByteCount)? in guard let byteRange = contents.NSRangeToByteRange(start: range.location + offset, length: range.length), file.syntaxMap.kinds(inByteRange: byteRange) == [.identifier] else { @@ -228,8 +232,9 @@ private extension String { let bridged = bridge() let range = NSRange(location: 0, length: bridged.length) guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range), - NSEqualRanges(range, match.range) else { - return (self, range) + NSEqualRanges(range, match.range) + else { + return (self, range) } let trimmedRange = match.range(at: 1) diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/JoinedDefaultParameterRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/JoinedDefaultParameterRule.swift index 7159be384..a5eb554f7 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/JoinedDefaultParameterRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/JoinedDefaultParameterRule.swift @@ -70,23 +70,18 @@ public struct JoinedDefaultParameterRule: SubstitutionCorrectableASTRule, Config guard // is this single argument called 'separator'? let argument = dictionary.enclosedArguments.first, - let offset = argument.offset, - let length = argument.length, - argument.name == "separator" + let argumentByteRange = argument.byteRange, + argument.name == "separator", + let argumentNSRange = file.stringView.byteRangeToNSRange(argumentByteRange) else { return [] } guard // is this single argument the default parameter? - let bodyOffset = argument.bodyOffset, - let bodyLength = argument.bodyLength, - let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength), + let bodyRange = argument.bodyByteRange, + let body = file.stringView.substringWithByteRange(bodyRange), body == "\"\"" else { return [] } - guard - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) - else { return [] } - - return [range] + return [argumentNSRange] } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/LegacyConstructorRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/LegacyConstructorRule.swift index 09b6ac968..c62bb1fd3 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/LegacyConstructorRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/LegacyConstructorRule.swift @@ -181,8 +181,8 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv var adjustedLocations = [Int]() for dictionary in violatingDictionaries.reversed() { - guard let offset = dictionary.offset, let length = dictionary.length, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length), + guard let byteRange = dictionary.byteRange, + let range = file.stringView.byteRangeToNSRange(byteRange), let name = dictionary.name, let correctedName = type(of: self).constructorsToCorrectedNames[name], file.ruleEnabled(violatingRanges: [range], for: self) == [range], @@ -213,13 +213,11 @@ public struct LegacyConstructorRule: ASTRule, CorrectableRule, ConfigurationProv private func argumentsContents(file: SwiftLintFile, arguments: [SourceKittenDictionary]) -> [String] { let contents = file.stringView return arguments.compactMap { argument -> String? in - guard argument.name == nil, - let offset = argument.offset, - let length = argument.length else { - return nil + guard argument.name == nil, let byteRange = argument.byteRange else { + return nil } - return contents.substringWithByteRange(start: offset, length: length) + return contents.substringWithByteRange(byteRange) } } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/NoFallthroughOnlyRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/NoFallthroughOnlyRule.swift index 9a053ff96..d77165acf 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/NoFallthroughOnlyRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/NoFallthroughOnlyRule.swift @@ -19,10 +19,9 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat kind: StatementKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { guard kind == .case, - let length = dictionary.length, - let offset = dictionary.offset, + let byteRange = dictionary.byteRange, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: offset, length: length), + let range = contents.byteRangeToNSRange(byteRange), let colonLocation = findCaseColon(text: file.stringView.nsString, range: range) else { return [] @@ -40,7 +39,7 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat let nsRange = nonCommentCaseBody[0].0 if contents.substring(with: nsRange) == "fallthrough" && nonCommentCaseBody[0].1 == [.keyword] && - !isNextTokenUnknownAttribute(afterOffset: offset + length, file: file) { + !isNextTokenUnknownAttribute(afterOffset: byteRange.upperBound, file: file) { return [StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, location: Location(file: file, characterOffset: nsRange.location))] @@ -49,13 +48,12 @@ public struct NoFallthroughOnlyRule: ASTRule, ConfigurationProviderRule, Automat return [] } - private func isNextTokenUnknownAttribute(afterOffset offset: Int, file: SwiftLintFile) -> Bool { + private func isNextTokenUnknownAttribute(afterOffset offset: ByteCount, file: SwiftLintFile) -> Bool { let nextNonCommentToken = file.syntaxMap.tokens .first { token in guard let kind = token.kind, !kind.isCommentLike else { return false } - return token.offset > offset } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift index 68f234bbc..123542689 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift @@ -45,18 +45,14 @@ public struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule, Aut } private func hasWhereClause(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { - let contents = file.stringView - guard let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, - let bodyOffset = dictionary.bodyOffset else { - return false - } - - let rangeStart = nameOffset + nameLength - let rangeLength = bodyOffset - rangeStart - - guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) else { + let bodyOffset = dictionary.bodyOffset, + case let contents = file.stringView, + case let rangeStart = nameOffset + nameLength, + case let rangeLength = bodyOffset - rangeStart, + let range = contents.byteRangeToNSRange(ByteRange(location: rangeStart, length: rangeLength)) + else { return false } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/ObjectLiteralRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/ObjectLiteralRule.swift index 8938bcd86..8680e3ce9 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/ObjectLiteralRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/ObjectLiteralRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct ObjectLiteralRule: ASTRule, ConfigurationProviderRule, OptInRule { @@ -94,11 +93,6 @@ public struct ObjectLiteralRule: ASTRule, ConfigurationProviderRule, OptInRule { } private func kinds(forArgument argument: SourceKittenDictionary, file: SwiftLintFile) -> Set { - guard let offset = argument.bodyOffset, let length = argument.bodyLength else { - return [] - } - - let range = NSRange(location: offset, length: length) - return Set(file.syntaxMap.kinds(inByteRange: range)) + return argument.bodyByteRange.map { Set(file.syntaxMap.kinds(inByteRange: $0)) } ?? [] } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/PatternMatchingKeywordsRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/PatternMatchingKeywordsRule.swift index 1eda5109b..d2d016815 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/PatternMatchingKeywordsRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/PatternMatchingKeywordsRule.swift @@ -43,10 +43,10 @@ public struct PatternMatchingKeywordsRule: ASTRule, ConfigurationProviderRule, O let contents = file.stringView return dictionary.elements.flatMap { subDictionary -> [StyleViolation] in guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", - let offset = subDictionary.offset, - let length = subDictionary.length, - let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else { - return [] + let caseByteRange = subDictionary.byteRange, + let caseRange = contents.byteRangeToNSRange(caseByteRange) + else { + return [] } let letMatches = file.match(pattern: "\\blet\\b", with: [.keyword], range: caseRange) diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/PrivateOverFilePrivateRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/PrivateOverFilePrivateRule.swift index 1aaebfa3d..04b7d45cf 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/PrivateOverFilePrivateRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/PrivateOverFilePrivateRule.swift @@ -81,10 +81,11 @@ public struct PrivateOverFilePrivateRule: ConfigurationProviderRule, Substitutio let parts = syntaxTokens.prefix { offset > $0.offset } guard let lastKind = parts.last, lastKind.kind == .attributeBuiltin, - let aclName = contents.substringWithByteRange(start: lastKind.offset, length: lastKind.length), + let aclName = contents.substringWithByteRange(lastKind.range), AccessControlLevel(description: aclName) == .fileprivate, - let range = contents.byteRangeToNSRange(start: lastKind.offset, length: lastKind.length) else { - return nil + let range = contents.byteRangeToNSRange(lastKind.range) + else { + return nil } return range diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantObjcAttributeRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantObjcAttributeRule.swift index ac0a063e5..fa02e055d 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantObjcAttributeRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantObjcAttributeRule.swift @@ -41,10 +41,10 @@ public struct RedundantObjcAttributeRule: SubstitutionCorrectableRule, Configura parentStructure: SourceKittenDictionary?) -> [NSRange] { let objcAttribute = dictionary.swiftAttributes .first(where: { $0.attribute == SwiftDeclarationAttributeKind.objc.rawValue }) - guard let objcOffset = objcAttribute?.offset, - let objcLength = objcAttribute?.length, - let range = file.stringView.byteRangeToNSRange(start: objcOffset, length: objcLength), - !dictionary.isObjcAndIBDesignableDeclaredExtension else { + guard let objcByteRange = objcAttribute?.byteRange, + let range = file.stringView.byteRangeToNSRange(objcByteRange), + !dictionary.isObjcAndIBDesignableDeclaredExtension + else { return [] } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantOptionalInitializationRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantOptionalInitializationRule.swift index 606883c14..a2cc6f0c7 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantOptionalInitializationRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantOptionalInitializationRule.swift @@ -127,9 +127,9 @@ public struct RedundantOptionalInitializationRule: SubstitutionCorrectableASTRul let contents = file.stringView if let bodyOffset = dictionary.bodyOffset { - return contents.byteRangeToNSRange(start: offset, length: bodyOffset - offset) + return contents.byteRangeToNSRange(ByteRange(location: offset, length: bodyOffset - offset)) } else { - return contents.byteRangeToNSRange(start: offset, length: length) + return contents.byteRangeToNSRange(ByteRange(location: offset, length: length)) } } @@ -148,9 +148,9 @@ extension SourceKittenDictionary { } private func isVariable(file: SwiftLintFile) -> Bool { - guard let start = offset, let length = length, + guard let byteRange = byteRange, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: start, length: length), + let range = contents.byteRangeToNSRange(byteRange), !file.match(pattern: "\\Avar\\b", with: [.keyword], range: range).isEmpty else { return false } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantStringEnumValueRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantStringEnumValueRule.swift index 6e8984e46..b66836e47 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantStringEnumValueRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantStringEnumValueRule.swift @@ -90,9 +90,9 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule, } } - private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] { + private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] { var caseCount = 0 - var violations = [Int]() + var violations = [ByteCount]() for enumCase in children(of: dictionary, matching: .enumcase) { caseCount += enumElementsCount(dictionary: enumCase) @@ -112,8 +112,8 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule, }).count } - private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] { - return children(of: dictionary, matching: .enumelement).flatMap { element -> [Int] in + private func violatingOffsetsForEnumCase(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] { + return children(of: dictionary, matching: .enumelement).flatMap { element -> [ByteCount] in guard let name = element.name else { return [] } @@ -122,18 +122,18 @@ public struct RedundantStringEnumValueRule: ASTRule, ConfigurationProviderRule, } private func violatingOffsetsForEnumElement(dictionary: SourceKittenDictionary, name: String, - file: SwiftLintFile) -> [Int] { + file: SwiftLintFile) -> [ByteCount] { let enumInits = filterEnumInits(dictionary: dictionary) - return enumInits.compactMap { dictionary -> Int? in + return enumInits.compactMap { dictionary -> ByteCount? in guard let offset = dictionary.offset, let length = dictionary.length else { return nil } // the string would be quoted if offset and length were used directly - let enumCaseName = file.stringView - .substringWithByteRange(start: offset + 1, length: length - 2) ?? "" + let rangeWithoutQuotes = ByteRange(location: offset + 1, length: length - 2) + let enumCaseName = file.stringView.substringWithByteRange(rangeWithoutQuotes) ?? "" guard enumCaseName == name else { return nil } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantVoidReturnRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantVoidReturnRule.swift index af39b2d97..417e2050b 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/RedundantVoidReturnRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/RedundantVoidReturnRule.swift @@ -81,7 +81,8 @@ public struct RedundantVoidReturnRule: ConfigurationProviderRule, SubstitutionCo case let start = nameOffset + nameLength, case let end = dictionary.bodyOffset ?? offset + length, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: start, length: end - start), + case let byteRange = ByteRange(location: start, length: end - start), + let range = contents.byteRangeToNSRange(byteRange), file.match(pattern: "->", excludingSyntaxKinds: excludingKinds, range: range).count == 1, let match = file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, range: range).first else { return [] diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/TypeNameRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/TypeNameRule.swift index fb90052fe..b47750034 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/TypeNameRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/TypeNameRule.swift @@ -61,7 +61,7 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule { } private func validate(name: String, dictionary: SourceKittenDictionary = SourceKittenDictionary([:]), - file: SwiftLintFile, offset: Int) -> [StyleViolation] { + file: SwiftLintFile, offset: ByteCount) -> [StyleViolation] { guard !configuration.excluded.contains(name) else { return [] } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableFunctionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableFunctionRule.swift index 59a4a717f..4c2234949 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableFunctionRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/UnavailableFunctionRule.swift @@ -65,8 +65,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn guard let offset = dictionary.offset, containsFatalError, !isFunctionUnavailable(file: file, dictionary: dictionary), - let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: bodyOffset, length: bodyLength), + let bodyRange = dictionary.bodyByteRange, + let range = file.stringView.byteRangeToNSRange(bodyRange), file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else { return [] } @@ -81,8 +81,8 @@ public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptIn private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { return dictionary.swiftAttributes.contains { dict -> Bool in guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available, - let offset = dict.offset, let length = dict.length, - let contents = file.stringView.substringWithByteRange(start: offset, length: length) else { + let byteRange = dict.byteRange, + let contents = file.stringView.substringWithByteRange(byteRange) else { return false } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/UnneededBreakInSwitchRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/UnneededBreakInSwitchRule.swift index 6ffd4f8c1..7304c3318 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/UnneededBreakInSwitchRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/UnneededBreakInSwitchRule.swift @@ -38,13 +38,11 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length), let innerStructure = file.structureDictionary.structures(forByteOffset: byteRange.location).last, innerStructure.statementKind == .case, - let caseOffset = innerStructure.offset, - let caseLength = innerStructure.length, + let caseRange = innerStructure.byteRange, let lastPatternEnd = patternEnd(dictionary: innerStructure) else { return nil } - let caseRange = NSRange(location: caseOffset, length: caseLength) let tokens = file.syntaxMap.tokens(inByteRange: caseRange).filter { token in guard let kind = token.kind, token.offset > lastPatternEnd else { @@ -73,8 +71,8 @@ public struct UnneededBreakInSwitchRule: ConfigurationProviderRule, AutomaticTes } } - private func patternEnd(dictionary: SourceKittenDictionary) -> Int? { - let patternEnds = dictionary.elements.compactMap { subDictionary -> Int? in + private func patternEnd(dictionary: SourceKittenDictionary) -> ByteCount? { + let patternEnds = dictionary.elements.compactMap { subDictionary -> ByteCount? in guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", let offset = subDictionary.offset, let length = subDictionary.length else { diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/UnusedEnumeratedRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/UnusedEnumeratedRule.swift index f08bae866..0889add70 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/UnusedEnumeratedRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/UnusedEnumeratedRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -44,7 +43,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati return [] } - let offset: Int + let offset: ByteCount let reason: String if firstTokenIsUnderscore { offset = tokens[0].offset @@ -83,7 +82,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati return false } - private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> NSRange? { + private func byteRangeForVariables(dictionary: SourceKittenDictionary) -> ByteRange? { let expectedKind = "source.lang.swift.structure.elem.id" for subDict in dictionary.elements where subDict.kind == expectedKind { guard let offset = subDict.offset, @@ -91,7 +90,7 @@ public struct UnusedEnumeratedRule: ASTRule, ConfigurationProviderRule, Automati continue } - return NSRange(location: offset, length: length) + return ByteRange(location: offset, length: length) } return nil diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/XCTFailMessageRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/XCTFailMessageRule.swift index aca010e29..55c5f73c0 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/XCTFailMessageRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/XCTFailMessageRule.swift @@ -54,13 +54,13 @@ public struct XCTFailMessageRule: ASTRule, ConfigurationProviderRule, AutomaticT } private func hasEmptyMessage(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { - guard - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength else { return false } + guard let bodyRange = dictionary.bodyByteRange else { + return false + } - guard bodyLength > 0 else { return true } + guard bodyRange.length > 0 else { return true } - let body = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) + let body = file.stringView.substringWithByteRange(bodyRange) return body == "\"\"" } } diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/XCTSpecificMatcherRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/XCTSpecificMatcherRule.swift index ad82bed36..f6b447b65 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/XCTSpecificMatcherRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/XCTSpecificMatcherRule.swift @@ -46,15 +46,7 @@ public struct XCTSpecificMatcherRule: ASTRule, OptInRule, ConfigurationProviderR return firstOffset < secondOffset } .prefix(2) - .compactMap { argument -> String? in - guard - let argOffset = argument.offset, - let argLength = argument.length, - let body = file.stringView.substringWithByteRange(start: argOffset, length: argLength) - else { return nil } - - return body - } + .compactMap { $0.byteRange.flatMap(file.stringView.substringWithByteRange) } .sorted { arg1, _ -> Bool in return protectedArguments.contains(arg1) } diff --git a/Source/SwiftLintFramework/Rules/Lint/AnyObjectProtocolRule.swift b/Source/SwiftLintFramework/Rules/Lint/AnyObjectProtocolRule.swift index 1e18ae607..842423a2c 100644 --- a/Source/SwiftLintFramework/Rules/Lint/AnyObjectProtocolRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/AnyObjectProtocolRule.swift @@ -61,15 +61,14 @@ public struct AnyObjectProtocolRule: SubstitutionCorrectableASTRule, OptInRule, return dictionary.elements.compactMap { subDict -> NSRange? in guard - let offset = subDict.offset, - let length = subDict.length, - let content = file.stringView.substringWithByteRange(start: offset, length: length), + let byteRange = subDict.byteRange, + let content = file.stringView.substringWithByteRange(byteRange), content == "class" - else { - return nil + else { + return nil } - return file.stringView.byteRangeToNSRange(start: offset, length: length) + return file.stringView.byteRangeToNSRange(byteRange) } } } diff --git a/Source/SwiftLintFramework/Rules/Lint/ArrayInitRule.swift b/Source/SwiftLintFramework/Rules/Lint/ArrayInitRule.swift index 8b80efce2..a560de057 100644 --- a/Source/SwiftLintFramework/Rules/Lint/ArrayInitRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/ArrayInitRule.swift @@ -49,14 +49,14 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto guard kind == .call, let name = dictionary.name, name.hasSuffix(".map"), let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, + let bodyRange = dictionary.bodyByteRange, let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, let offset = dictionary.offset else { return [] } - let range = NSRange(location: bodyOffset, length: bodyLength) - let tokens = file.syntaxMap.tokens(inByteRange: range).filter { token in + let tokens = file.syntaxMap.tokens(inByteRange: bodyRange).filter { token in guard let kind = token.kind else { return false } @@ -84,37 +84,39 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto } private func isClosureParameter(firstToken: SwiftLintSyntaxToken, - nameEndPosition: Int, + nameEndPosition: ByteCount, file: SwiftLintFile) -> Bool { let length = firstToken.offset - nameEndPosition guard length > 0, case let contents = file.stringView, - let byteRange = contents.byteRangeToNSRange(start: nameEndPosition, length: length) else { - return false + case let byteRange = ByteRange(location: nameEndPosition, length: length), + let nsRange = contents.byteRangeToNSRange(byteRange) + else { + return false } let pattern = regex("\\A\\s*\\(?\\s*\\{") - return pattern.firstMatch(in: file.contents, options: .anchored, range: byteRange) != nil + return pattern.firstMatch(in: file.contents, options: .anchored, range: nsRange) != nil } private func containsTrailingContent(lastToken: SwiftLintSyntaxToken, - bodyEndPosition: Int, + bodyEndPosition: ByteCount, file: SwiftLintFile) -> Bool { let lastTokenEnd = lastToken.offset + lastToken.length let remainingLength = bodyEndPosition - lastTokenEnd - let remainingRange = NSRange(location: lastTokenEnd, length: remainingLength) + let remainingRange = ByteRange(location: lastTokenEnd, length: remainingLength) return containsContent(inByteRange: remainingRange, file: file) } private func containsLeadingContent(tokens: [SwiftLintSyntaxToken], - bodyStartPosition: Int, + bodyStartPosition: ByteCount, file: SwiftLintFile) -> Bool { let inTokenPosition = tokens.firstIndex(where: { token in token.kind == .keyword && file.contents(for: token) == "in" }) let firstToken: SwiftLintSyntaxToken - let start: Int + let start: ByteCount if let position = inTokenPosition { let index = tokens.index(after: position) firstToken = tokens[index] @@ -126,25 +128,26 @@ public struct ArrayInitRule: ASTRule, ConfigurationProviderRule, OptInRule, Auto } let length = firstToken.offset - start - let remainingRange = NSRange(location: start, length: length) + let remainingRange = ByteRange(location: start, length: length) return containsContent(inByteRange: remainingRange, file: file) } - private func containsContent(inByteRange byteRange: NSRange, file: SwiftLintFile) -> Bool { - let nsstring = file.stringView + private func containsContent(inByteRange byteRange: ByteRange, file: SwiftLintFile) -> Bool { + let stringView = file.stringView let remainingTokens = file.syntaxMap.tokens(inByteRange: byteRange) - let ranges = NSMutableIndexSet(indexesIn: byteRange) + guard let nsRange = stringView.byteRangeToNSRange(byteRange) else { + return false + } - for token in remainingTokens { - ranges.remove(in: token.range) + let ranges = NSMutableIndexSet(indexesIn: nsRange) + + for tokenNSRange in remainingTokens.compactMap({ stringView.byteRangeToNSRange($0.range) }) { + ranges.remove(in: tokenNSRange) } var containsContent = false ranges.enumerateRanges(options: []) { range, stop in - guard let substring = nsstring.substringWithByteRange(start: range.location, length: range.length) else { - return - } - + let substring = stringView.substring(with: range) let processedSubstring = substring .trimmingCharacters(in: CharacterSet(charactersIn: "{}")) .trimmingCharacters(in: .whitespacesAndNewlines) diff --git a/Source/SwiftLintFramework/Rules/Lint/ClassDelegateProtocolRule.swift b/Source/SwiftLintFramework/Rules/Lint/ClassDelegateProtocolRule.swift index 074e40693..b5d63749d 100644 --- a/Source/SwiftLintFramework/Rules/Lint/ClassDelegateProtocolRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/ClassDelegateProtocolRule.swift @@ -65,8 +65,10 @@ public struct ClassDelegateProtocolRule: ASTRule, ConfigurationProviderRule, Aut let bodyOffset = dictionary.bodyOffset, case let contents = file.stringView, case let start = nameOffset + nameLength, - let range = contents.byteRangeToNSRange(start: start, length: bodyOffset - start), - !isClassProtocol(file: file, range: range) else { + case let byteRange = ByteRange(location: start, length: bodyOffset - start), + let range = contents.byteRangeToNSRange(byteRange), + !isClassProtocol(file: file, range: range) + else { return [] } diff --git a/Source/SwiftLintFramework/Rules/Lint/CompilerProtocolInitRule.swift b/Source/SwiftLintFramework/Rules/Lint/CompilerProtocolInitRule.swift index c156ca0e9..d60fb9598 100644 --- a/Source/SwiftLintFramework/Rules/Lint/CompilerProtocolInitRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/CompilerProtocolInitRule.swift @@ -52,10 +52,9 @@ public struct CompilerProtocolInitRule: ASTRule, ConfigurationProviderRule { guard compilerProtocol.initCallNames.contains(name), case let arguments = dictionary.enclosedArguments.compactMap({ $0.name }), compilerProtocol.match(arguments: arguments), - let offset = dictionary.offset, - let length = dictionary.length, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else { - continue + let range = dictionary.byteRange.flatMap(file.stringView.byteRangeToNSRange) + else { + continue } return [(compilerProtocol, range)] diff --git a/Source/SwiftLintFramework/Rules/Lint/DeploymentTargetRule.swift b/Source/SwiftLintFramework/Rules/Lint/DeploymentTargetRule.swift index 4429029e6..8a1388bd7 100644 --- a/Source/SwiftLintFramework/Rules/Lint/DeploymentTargetRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/DeploymentTargetRule.swift @@ -53,9 +53,9 @@ public struct DeploymentTargetRule: ConfigurationProviderRule { return file.rangesAndTokens(matching: pattern).flatMap { range, tokens -> [StyleViolation] in guard let availabilityToken = tokens.first, availabilityToken.kind == .keyword, - let tokenRange = file.stringView.byteRangeToNSRange(start: availabilityToken.offset, - length: availabilityToken.length) else { - return [] + let tokenRange = file.stringView.byteRangeToNSRange(availabilityToken.range) + else { + return [] } let rangeToSearch = NSRange(location: tokenRange.upperBound, length: range.length - tokenRange.length) @@ -83,17 +83,19 @@ public struct DeploymentTargetRule: ConfigurationProviderRule { let contents = file.stringView return attributes.flatMap { dictionary -> [StyleViolation] in - guard let offset = dictionary.offset, let length = dictionary.length, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { - return [] + guard let byteRange = dictionary.byteRange, + let range = contents.byteRangeToNSRange(byteRange) + else { + return [] } - return validate(range: range, file: file, violationType: "attribute", byteOffsetToReport: offset) + return validate(range: range, file: file, violationType: "attribute", + byteOffsetToReport: byteRange.location) }.unique } private func validate(range: NSRange, file: SwiftLintFile, violationType: String, - byteOffsetToReport: Int) -> [StyleViolation] { + byteOffsetToReport: ByteCount) -> [StyleViolation] { let platformToConfiguredMinVersion = self.platformToConfiguredMinVersion let allPlatforms = "(?:" + platformToConfiguredMinVersion.keys.joined(separator: "|") + ")" let pattern = "\(allPlatforms) [\\d\\.]+" diff --git a/Source/SwiftLintFramework/Rules/Lint/DiscardedNotificationCenterObserverRule.swift b/Source/SwiftLintFramework/Rules/Lint/DiscardedNotificationCenterObserverRule.swift index 99ae59a09..b654d7273 100644 --- a/Source/SwiftLintFramework/Rules/Lint/DiscardedNotificationCenterObserverRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/DiscardedNotificationCenterObserverRule.swift @@ -48,7 +48,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro } private func violationOffsets(in file: SwiftLintFile, dictionary: SourceKittenDictionary, - kind: SwiftExpressionKind) -> [Int] { + kind: SwiftExpressionKind) -> [ByteCount] { guard kind == .call, let name = dictionary.name, name.hasSuffix(".addObserver"), @@ -57,7 +57,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro argumentsNames == ["forName", "object", "queue"] || argumentsNames == ["forName", "object", "queue", "using"], let offset = dictionary.offset, - let range = file.stringView.byteRangeToNSRange(start: 0, length: offset) else { + let range = file.stringView.byteRangeToNSRange(ByteRange(location: 0, length: offset)) else { return [] } @@ -83,7 +83,7 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro } private extension SourceKittenDictionary { - func functions(forByteOffset byteOffset: Int) -> [SourceKittenDictionary] { + func functions(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] { return structures(forByteOffset: byteOffset) .filter { $0.declarationKind.map(SwiftDeclarationKind.functionKinds.contains) == true } } diff --git a/Source/SwiftLintFramework/Rules/Lint/DuplicateEnumCasesRule.swift b/Source/SwiftLintFramework/Rules/Lint/DuplicateEnumCasesRule.swift index 4ed8aad9e..f28f9b217 100644 --- a/Source/SwiftLintFramework/Rules/Lint/DuplicateEnumCasesRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/DuplicateEnumCasesRule.swift @@ -47,11 +47,12 @@ public struct DuplicateEnumCasesRule: ConfigurationProviderRule, ASTRule, Automa .compactMap { substructureElements(of: $0, matching: .enumelement) } .flatMap { $0 } - var elementsByName: [String: [Int]] = [:] + var elementsByName: [String: [ByteCount]] = [:] for element in enumElements { guard let name = element.name, let nameWithoutParameters = name.split(separator: "(").first, - let offset = element.offset else { + let offset = element.offset + else { continue } diff --git a/Source/SwiftLintFramework/Rules/Lint/DynamicInlineRule.swift b/Source/SwiftLintFramework/Rules/Lint/DynamicInlineRule.swift index 2c31679bb..9b049c0b8 100644 --- a/Source/SwiftLintFramework/Rules/Lint/DynamicInlineRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/DynamicInlineRule.swift @@ -34,9 +34,7 @@ public struct DynamicInlineRule: ASTRule, ConfigurationProviderRule, AutomaticTe case let attributes = dictionary.enclosedSwiftAttributes, attributes.contains(.dynamic), attributes.contains(.inline), - let funcByteOffset = dictionary.offset, - let funcOffset = file.stringView - .byteRangeToNSRange(start: funcByteOffset, length: 0)?.location, + let funcOffset = dictionary.offset.flatMap(file.stringView.location), case let inlinePattern = regex("@inline"), case let range = NSRange(location: 0, length: funcOffset), let inlineMatch = inlinePattern.matches(in: file.contents, options: [], range: range) diff --git a/Source/SwiftLintFramework/Rules/Lint/IdenticalOperandsRule.swift b/Source/SwiftLintFramework/Rules/Lint/IdenticalOperandsRule.swift index 214a2e67e..b3fe01e63 100644 --- a/Source/SwiftLintFramework/Rules/Lint/IdenticalOperandsRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/IdenticalOperandsRule.swift @@ -131,8 +131,7 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom } } - let violationRange = file.stringView.byteRangeToNSRange(start: leftmostToken.offset, - length: leftmostToken.length) + let violationRange = file.stringView.byteRangeToNSRange(leftmostToken.range) return violationRange } @@ -175,12 +174,13 @@ public struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule, Autom private extension StringView { func subStringWithSyntaxToken(_ syntaxToken: SwiftLintSyntaxToken) -> String? { - return substringWithByteRange(start: syntaxToken.offset, length: syntaxToken.length) + return substringWithByteRange(syntaxToken.range) } func subStringBetweenTokens(_ startToken: SwiftLintSyntaxToken, _ endToken: SwiftLintSyntaxToken) -> String? { - return substringWithByteRange(start: startToken.offset + startToken.length, - length: endToken.offset - startToken.offset - startToken.length) + let byteRange = ByteRange(location: startToken.range.upperBound, + length: endToken.offset - startToken.range.upperBound) + return substringWithByteRange(byteRange) } func isDotOrOptionalChainingBetweenTokens(_ startToken: SwiftLintSyntaxToken, diff --git a/Source/SwiftLintFramework/Rules/Lint/InertDeferRule.swift b/Source/SwiftLintFramework/Rules/Lint/InertDeferRule.swift index 5730f3ddd..2be5615fd 100644 --- a/Source/SwiftLintFramework/Rules/Lint/InertDeferRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/InertDeferRule.swift @@ -63,7 +63,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule { case let outerKindIndex = kinds.index(before: brace.offset), case let outerKind = kinds[outerKindIndex], case let braceEnd = brace.element.byteRange.upperBound, - case let tokensRange = NSRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd), + case let tokensRange = ByteRange(location: braceEnd, length: outerKind.byteRange.upperBound - braceEnd), case let tokens = file.syntaxMap.tokens(inByteRange: tokensRange), !tokens.contains(where: isNotComment) else { return nil @@ -76,7 +76,7 @@ public struct InertDeferRule: ConfigurationProviderRule, AutomaticTestableRule { } } -private func isBrace(offset: Int, element: (kind: String, byteRange: NSRange)) -> Bool { +private func isBrace(offset: Int, element: (kind: String, byteRange: ByteRange)) -> Bool { return StatementKind(rawValue: element.kind) == .brace } diff --git a/Source/SwiftLintFramework/Rules/Lint/LowerACLThanParentRule.swift b/Source/SwiftLintFramework/Rules/Lint/LowerACLThanParentRule.swift index aed0a84c2..b5ad9c460 100644 --- a/Source/SwiftLintFramework/Rules/Lint/LowerACLThanParentRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/LowerACLThanParentRule.swift @@ -41,14 +41,14 @@ public struct LowerACLThanParentRule: OptInRule, ConfigurationProviderRule, Auto } private func validateACL(isHigherThan parentAccessibility: AccessControlLevel, - in substructure: SourceKittenDictionary) -> [Int] { - return substructure.substructure.flatMap { element -> [Int] in + in substructure: SourceKittenDictionary) -> [ByteCount] { + return substructure.substructure.flatMap { element -> [ByteCount] in guard let elementKind = element.declarationKind, elementKind.isRelevantDeclaration else { return [] } - var violationOffset: Int? + var violationOffset: ByteCount? let accessibility = element.accessibility ?? .internal // Swift 5 infers members of private types with no explicit ACL attribute to be `internal`. let isInferredACL = accessibility == .internal && !element.enclosedSwiftAttributes.contains(.internal) diff --git a/Source/SwiftLintFramework/Rules/Lint/MarkRule.swift b/Source/SwiftLintFramework/Rules/Lint/MarkRule.swift index 2333c1ab9..58c86f764 100644 --- a/Source/SwiftLintFramework/Rules/Lint/MarkRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/MarkRule.swift @@ -189,8 +189,8 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule { } return !syntaxTokens.isEmpty && SyntaxKind.commentKinds.contains(syntaxKind) }.compactMap { range, syntaxTokens in - let identifierRange = file.stringView - .byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0) + let byteRange = ByteRange(location: syntaxTokens[0].offset, length: 0) + let identifierRange = file.stringView.byteRangeToNSRange(byteRange) return identifierRange.map { NSUnionRange($0, range) } } } diff --git a/Source/SwiftLintFramework/Rules/Lint/MissingDocsRule.swift b/Source/SwiftLintFramework/Rules/Lint/MissingDocsRule.swift index f097880ff..912776859 100644 --- a/Source/SwiftLintFramework/Rules/Lint/MissingDocsRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/MissingDocsRule.swift @@ -2,7 +2,7 @@ import SourceKittenFramework private extension SwiftLintFile { func missingDocOffsets(in dictionary: SourceKittenDictionary, - acls: [AccessControlLevel]) -> [(Int, AccessControlLevel)] { + acls: [AccessControlLevel]) -> [(ByteCount, AccessControlLevel)] { if dictionary.enclosedSwiftAttributes.contains(.override) || !dictionary.inheritedTypes.isEmpty { return [] @@ -77,8 +77,7 @@ public struct MissingDocsRule: OptInRule, ConfigurationProviderRule, AutomaticTe public func validate(file: SwiftLintFile) -> [StyleViolation] { let acls = configuration.parameters.map { $0.value } let dict = file.structureDictionary - return file.missingDocOffsets(in: dict, - acls: acls).map { (offset: Int, acl: AccessControlLevel) in + return file.missingDocOffsets(in: dict, acls: acls).map { offset, acl in StyleViolation(ruleDescription: type(of: self).description, severity: configuration.parameters.first { $0.value == acl }?.severity ?? .warning, location: Location(file: file, byteOffset: offset), diff --git a/Source/SwiftLintFramework/Rules/Lint/NSLocalizedStringKeyRule.swift b/Source/SwiftLintFramework/Rules/Lint/NSLocalizedStringKeyRule.swift index 77423f80d..90a4b1423 100644 --- a/Source/SwiftLintFramework/Rules/Lint/NSLocalizedStringKeyRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/NSLocalizedStringKeyRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -28,9 +27,8 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide dictionary.name == "NSLocalizedString", let firstArgument = dictionary.enclosedArguments.first, firstArgument.name == nil, - let offset = firstArgument.offset, - let length = firstArgument.length, - case let kinds = file.syntaxMap.kinds(inByteRange: NSRange(location: offset, length: length)), + let byteRange = firstArgument.byteRange, + case let kinds = file.syntaxMap.kinds(inByteRange: byteRange), !kinds.allSatisfy({ $0 == .string }) else { return [] } @@ -38,7 +36,7 @@ public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProvide return [ StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, - location: Location(file: file, byteOffset: offset)) + location: Location(file: file, byteOffset: byteRange.location)) ] } } diff --git a/Source/SwiftLintFramework/Rules/Lint/NotificationCenterDetachmentRule.swift b/Source/SwiftLintFramework/Rules/Lint/NotificationCenterDetachmentRule.swift index 50080ef26..ed234d8af 100644 --- a/Source/SwiftLintFramework/Rules/Lint/NotificationCenterDetachmentRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/NotificationCenterDetachmentRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -29,8 +28,8 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu } private func violationOffsets(file: SwiftLintFile, - dictionary: SourceKittenDictionary) -> [Int] { - return dictionary.substructure.flatMap { subDict -> [Int] in + dictionary: SourceKittenDictionary) -> [ByteCount] { + return dictionary.substructure.flatMap { subDict -> [ByteCount] in // complete detachment is allowed on `deinit` if subDict.declarationKind == .functionMethodInstance, subDict.name == "deinit" { @@ -51,16 +50,11 @@ public struct NotificationCenterDetachmentRule: ASTRule, ConfigurationProviderRu private var methodName = "NotificationCenter.default.removeObserver" private func parameterIsSelf(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { - guard let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength else { - return false - } - - let range = NSRange(location: bodyOffset, length: bodyLength) - let tokens = file.syntaxMap.tokens(inByteRange: range) - let types = tokens.kinds - - guard types == [.keyword], let token = tokens.first else { + guard let bodyRange = dictionary.bodyByteRange, + case let tokens = file.syntaxMap.tokens(inByteRange: bodyRange), + tokens.kinds == [.keyword], + let token = tokens.first + else { return false } diff --git a/Source/SwiftLintFramework/Rules/Lint/OrphanedDocCommentRule.swift b/Source/SwiftLintFramework/Rules/Lint/OrphanedDocCommentRule.swift index 8500d4033..a80b99fa4 100644 --- a/Source/SwiftLintFramework/Rules/Lint/OrphanedDocCommentRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/OrphanedDocCommentRule.swift @@ -55,12 +55,12 @@ public struct OrphanedDocCommentRule: ConfigurationProviderRule { return token.kind == .docComment || token.kind == .docCommentField } - let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [NSRange]? in + let docummentedDeclsRanges = file.structureDictionary.traverseDepthFirst { dictionary -> [ByteRange]? in guard let docOffset = dictionary.docOffset, let docLength = dictionary.docLength else { return nil } - return [NSRange(location: docOffset, length: docLength)] + return [ByteRange(location: docOffset, length: docLength)] }.sorted { $0.location < $1.location } return docStringsTokens diff --git a/Source/SwiftLintFramework/Rules/Lint/OverrideInExtensionRule.swift b/Source/SwiftLintFramework/Rules/Lint/OverrideInExtensionRule.swift index ce2e8199d..ce7068f90 100644 --- a/Source/SwiftLintFramework/Rules/Lint/OverrideInExtensionRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/OverrideInExtensionRule.swift @@ -40,11 +40,12 @@ public struct OverrideInExtensionRule: ConfigurationProviderRule, OptInRule, Aut return elements .filter { $0.kind == .extension && !susceptibleNames.contains($0.name) } .flatMap { element in - return element.dictionary.substructure.compactMap { element -> Int? in + return element.dictionary.substructure.compactMap { element -> ByteCount? in guard element.declarationKind != nil, element.enclosedSwiftAttributes.contains(.override), - let offset = element.offset else { - return nil + let offset = element.offset + else { + return nil } return offset diff --git a/Source/SwiftLintFramework/Rules/Lint/QuickDiscouragedCallRule.swift b/Source/SwiftLintFramework/Rules/Lint/QuickDiscouragedCallRule.swift index 054cad7d2..9f6e5e394 100644 --- a/Source/SwiftLintFramework/Rules/Lint/QuickDiscouragedCallRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/QuickDiscouragedCallRule.swift @@ -60,8 +60,8 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au } } - private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [Int] { - return substructure.flatMap { dictionary -> [Int] in + private func violationOffsets(in substructure: [SourceKittenDictionary]) -> [ByteCount] { + return substructure.flatMap { dictionary -> [ByteCount] in let substructure = dictionary.substructure.flatMap { dict -> [SourceKittenDictionary] in if dict.expressionKind == .closure { return dict.substructure @@ -74,7 +74,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au } } - private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [Int] { + private func toViolationOffsets(dictionary: SourceKittenDictionary) -> [ByteCount] { guard dictionary.kind != nil, let offset = dictionary.offset @@ -90,7 +90,7 @@ public struct QuickDiscouragedCallRule: OptInRule, ConfigurationProviderRule, Au return dictionary.substructure.compactMap(toViolationOffset) } - private func toViolationOffset(dictionary: SourceKittenDictionary) -> Int? { + private func toViolationOffset(dictionary: SourceKittenDictionary) -> ByteCount? { guard let name = dictionary.name, let offset = dictionary.offset, diff --git a/Source/SwiftLintFramework/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift b/Source/SwiftLintFramework/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift index 988d08421..b7c7ce590 100644 --- a/Source/SwiftLintFramework/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/RawValueForCamelCasedCodableEnumRule.swift @@ -1,7 +1,7 @@ import SourceKittenFramework public struct RawValueForCamelCasedCodableEnumRule: ASTRule, OptInRule, ConfigurationProviderRule, -AutomaticTestableRule { + AutomaticTestableRule { public var configuration = SeverityConfiguration(.warning) public init() {} @@ -104,13 +104,11 @@ AutomaticTestableRule { } } - private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [Int] { - let locs = substructureElements(of: dictionary, matching: .enumcase) + private func violatingOffsetsForEnum(dictionary: SourceKittenDictionary) -> [ByteCount] { + return substructureElements(of: dictionary, matching: .enumcase) .compactMap { substructureElements(of: $0, matching: .enumelement) } .flatMap(camelCasedEnumCasesMissingRawValue) .compactMap { $0.offset } - - return locs } private func substructureElements(of dict: SourceKittenDictionary, diff --git a/Source/SwiftLintFramework/Rules/Lint/RequiredEnumCaseRule.swift b/Source/SwiftLintFramework/Rules/Lint/RequiredEnumCaseRule.swift index 616af9ef0..402aef238 100644 --- a/Source/SwiftLintFramework/Rules/Lint/RequiredEnumCaseRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/RequiredEnumCaseRule.swift @@ -89,7 +89,7 @@ public struct RequiredEnumCaseRule: ASTRule, OptInRule, ConfigurationProviderRul /// /// - returns: Location of where the enum declaration starts. static func location(from dictionary: SourceKittenDictionary, in file: SwiftLintFile) -> Location { - return Location(file: file, characterOffset: dictionary.offset ?? 0) + return Location(file: file, byteOffset: dictionary.offset ?? 0) } /// Determines the names of cases found in the enum. diff --git a/Source/SwiftLintFramework/Rules/Lint/StrongIBOutletRule.swift b/Source/SwiftLintFramework/Rules/Lint/StrongIBOutletRule.swift index 2c113baf9..45570b986 100644 --- a/Source/SwiftLintFramework/Rules/Lint/StrongIBOutletRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/StrongIBOutletRule.swift @@ -27,8 +27,9 @@ public struct StrongIBOutletRule: ConfigurationProviderRule, ASTRule, OptInRule, case let attributes = dictionary.enclosedSwiftAttributes, attributes.contains(.iboutlet), attributes.contains(.weak), - let offset = dictionary.offset else { - return [] + let offset = dictionary.offset + else { + return [] } return [ diff --git a/Source/SwiftLintFramework/Rules/Lint/UnownedVariableCaptureRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnownedVariableCaptureRule.swift index d0c177f45..d73d3e689 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnownedVariableCaptureRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnownedVariableCaptureRule.swift @@ -29,26 +29,28 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi public func validate(file: SwiftLintFile, kind: SwiftExpressionKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { - guard kind == .closure, let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, + guard kind == .closure, let bodyRange = dictionary.bodyByteRange, case let contents = file.stringView, - let closureRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength), + let closureRange = contents.byteRangeToNSRange(bodyRange), let inTokenRange = file.match(pattern: "\\bin\\b", with: [.keyword], range: closureRange).first, let inTokenByteRange = contents.NSRangeToByteRange(start: inTokenRange.location, - length: inTokenRange.length) else { - return [] + length: inTokenRange.length) + else { + return [] } - let length = inTokenByteRange.location - bodyOffset - let variables = localVariableDeclarations(inByteRange: NSRange(location: bodyOffset, length: length), + let length = inTokenByteRange.location - bodyRange.location + let variables = localVariableDeclarations(inByteRange: ByteRange(location: bodyRange.location, length: length), structureDictionary: file.structureDictionary) let unownedVariableOffsets = variables.compactMap { dictionary in return dictionary.swiftAttributes.first { attributeDict in guard attributeDict.attribute.flatMap(SwiftDeclarationAttributeKind.init) == .weak, - let offset = attributeDict.offset, let length = attributeDict.length else { - return false + let attributeByteRange = attributeDict.byteRange + else { + return false } - return contents.substringWithByteRange(start: offset, length: length) == "unowned" + return contents.substringWithByteRange(attributeByteRange) == "unowned" }?.offset } @@ -59,13 +61,14 @@ public struct UnownedVariableCaptureRule: ASTRule, OptInRule, ConfigurationProvi } } - private func localVariableDeclarations(inByteRange byteRange: NSRange, + private func localVariableDeclarations(inByteRange byteRange: ByteRange, structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { return structureDictionary.traverseBreadthFirst { dictionary in guard dictionary.declarationKind == .varLocal, let variableByteRange = dictionary.byteRange, - byteRange.intersects(variableByteRange) else { - return nil + byteRange.intersects(variableByteRange) + else { + return nil } return [dictionary] } diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift index 03962cb36..6c9b8f8e5 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift @@ -80,12 +80,14 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat guard kind == .closure, let offset = dictionary.offset, let length = dictionary.length, - let closureRange = contents.byteRangeToNSRange(start: offset, length: length) + let closureByteRange = dictionary.byteRange, + let closureRange = contents.byteRangeToNSRange(closureByteRange) else { return [] } let firstSubstructureOffset = dictionary.substructure.first?.offset ?? (offset + length) let captureListSearchLength = firstSubstructureOffset - offset - guard let captureListSearchRange = contents.byteRangeToNSRange(start: offset, length: captureListSearchLength), + let captureListSearchByteRange = ByteRange(location: offset, length: captureListSearchLength) + guard let captureListSearchRange = contents.byteRangeToNSRange(captureListSearchByteRange), let match = captureListRegex.firstMatch(in: file.contents, options: [], range: captureListSearchRange) else { return [] } @@ -131,7 +133,7 @@ public struct UnusedCaptureListRule: ASTRule, ConfigurationProviderRule, Automat } } - private func identifierStrings(in file: SwiftLintFile, byteRange: NSRange) -> Set { + private func identifierStrings(in file: SwiftLintFile, byteRange: ByteRange) -> Set { let identifiers = file.syntaxMap .tokens(inByteRange: byteRange) .compactMap { token -> String? in diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift index 3b633aa7b..f767c4b69 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedClosureParameterRule.swift @@ -127,54 +127,62 @@ public struct UnusedClosureParameterRule: SubstitutionCorrectableASTRule, Config let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, let bodyLength = dictionary.bodyLength, - bodyLength > 0 else { - return [] + bodyLength > 0 + else { + return [] } let rangeStart = nameOffset + nameLength let rangeLength = (offset + length) - (nameOffset + nameLength) + let byteRange = ByteRange(location: rangeStart, length: rangeLength) let parameters = dictionary.enclosedVarParameters let contents = file.stringView return parameters.compactMap { param -> (NSRange, String)? in - guard let paramOffset = param.offset, - let name = param.name, - name != "_", - let regex = try? NSRegularExpression(pattern: name, - options: [.ignoreMetacharacters]), - let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) + self.rangeAndName(parameter: param, contents: contents, byteRange: byteRange, file: file) + } + } + + private func rangeAndName(parameter: SourceKittenDictionary, contents: StringView, byteRange: ByteRange, + file: SwiftLintFile) -> (range: NSRange, name: String)? { + guard let paramOffset = parameter.offset, + let name = parameter.name, + name != "_", + let regex = try? NSRegularExpression(pattern: name, + options: [.ignoreMetacharacters]), + let range = contents.byteRangeToNSRange(byteRange) + else { + return nil + } + + let paramLength = ByteCount(name.lengthOfBytes(using: .utf8)) + + let matches = regex.matches(in: file.contents, options: [], range: range).ranges() + for range in matches { + guard let byteRange = contents.NSRangeToByteRange(start: range.location, + length: range.length), + // if it's the parameter declaration itself, we should skip + byteRange.location > paramOffset, + case let tokens = file.syntaxMap.tokens(inByteRange: byteRange) else { + continue + } + + let token = tokens.first(where: { token -> Bool in + return (token.kind == .identifier + || (token.kind == .keyword && name == "self")) && + token.offset == byteRange.location && + token.length == byteRange.length + }) + + // found a usage, there's no violation! + guard token == nil else { return nil } - - let paramLength = name.lengthOfBytes(using: .utf8) - - let matches = regex.matches(in: file.contents, options: [], range: range).ranges() - for range in matches { - guard let byteRange = contents.NSRangeToByteRange(start: range.location, - length: range.length), - // if it's the parameter declaration itself, we should skip - byteRange.location > paramOffset, - case let tokens = file.syntaxMap.tokens(inByteRange: byteRange) else { - continue - } - - let token = tokens.first(where: { token -> Bool in - return (token.kind == .identifier - || (token.kind == .keyword && name == "self")) && - token.offset == byteRange.location && - token.length == byteRange.length - }) - - // found a usage, there's no violation! - guard token == nil else { - return nil - } - } - if let range = contents.byteRangeToNSRange(start: paramOffset, length: paramLength) { - return (range, name) - } - return nil + } + let violationByteRange = ByteRange(location: paramOffset, length: paramLength) + return contents.byteRangeToNSRange(violationByteRange).map { range in + return (range, name) } } diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedControlFlowLabelRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedControlFlowLabelRule.swift index 015c5efd1..e5970d580 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedControlFlowLabelRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedControlFlowLabelRule.swift @@ -103,9 +103,9 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config let contentsNSString = file.stringView if let byteRange = contentsNSString.NSRangeToByteRange(start: violationRange.location, length: violationRange.length), - let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }), - let nextTokenLocation = contentsNSString.byteRangeToNSRange(start: nextToken.offset, length: 0) { - rangeToRemove.length = nextTokenLocation.location - violationRange.location + let nextToken = file.syntaxMap.tokens.first(where: { $0.offset > byteRange.location }) { + let nextTokenLocation = contentsNSString.location(fromByteOffset: nextToken.offset) + rangeToRemove.length = nextTokenLocation - violationRange.location } return (rangeToRemove, "") @@ -114,22 +114,18 @@ public struct UnusedControlFlowLabelRule: SubstitutionCorrectableASTRule, Config public func violationRanges(in file: SwiftLintFile, kind: StatementKind, dictionary: SourceKittenDictionary) -> [NSRange] { guard type(of: self).kinds.contains(kind), - let offset = dictionary.offset, let length = dictionary.length, - case let byteRange = NSRange(location: offset, length: length), + let byteRange = dictionary.byteRange, case let tokens = file.syntaxMap.tokens(inByteRange: byteRange), let firstToken = tokens.first, firstToken.kind == .identifier, let tokenContent = file.contents(for: firstToken), case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { - return [] - } - - let pattern = "(?:break|continue)\\s+\(tokenContent)\\b" - guard file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty, - let violationRange = contents.byteRangeToNSRange(start: firstToken.offset, - length: firstToken.length) else { - return [] + let range = contents.byteRangeToNSRange(byteRange), + case let pattern = "(?:break|continue)\\s+\(tokenContent)\\b", + file.match(pattern: pattern, with: [.keyword, .identifier], range: range).isEmpty, + let violationRange = contents.byteRangeToNSRange(firstToken.range) + else { + return [] } return [violationRange] diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedDeclarationRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedDeclarationRule.swift index 7af73cfc8..1bdc14d56 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedDeclarationRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedDeclarationRule.swift @@ -4,7 +4,7 @@ import SourceKittenFramework public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProviderRule, AnalyzerRule, CollectingRule { public struct FileUSRs { var referenced: Set - var declared: [(usr: String, nameOffset: Int)] + var declared: [(usr: String, nameOffset: ByteCount)] var testCaseUSRs: Set } @@ -121,9 +121,9 @@ public struct UnusedDeclarationRule: AutomaticTestableRule, ConfigurationProvide } private func violationOffsets(in file: SwiftLintFile, compilerArguments: [String], - declaredUSRs: [(usr: String, nameOffset: Int)], + declaredUSRs: [(usr: String, nameOffset: ByteCount)], allReferencedUSRs: Set, - allTestCaseUSRs: Set) -> [Int] { + allTestCaseUSRs: Set) -> [ByteCount] { // Unused declarations are: // 1. all declarations // 2. minus all references @@ -160,7 +160,7 @@ private extension SwiftLintFile { return nil } - let offset = Int64(token.offset) + let offset = token.offset let request = Request.cursorInfo(file: path, offset: offset, arguments: compilerArguments) guard var cursorInfo = try? request.sendIfNotDisabled() else { return nil @@ -169,14 +169,14 @@ private extension SwiftLintFile { if let acl = editorOpen.aclAtOffset(offset) { cursorInfo["key.accessibility"] = acl.rawValue } - cursorInfo["swiftlint.offset"] = offset + cursorInfo["swiftlint.offset"] = Int64(offset.value) return cursorInfo } .map(SourceKittenDictionary.init) } static func declaredUSRs(allCursorInfo: [SourceKittenDictionary], includePublicAndOpen: Bool) - -> [(usr: String, nameOffset: Int)] { + -> [(usr: String, nameOffset: ByteCount)] { return allCursorInfo.compactMap { cursorInfo in return declaredUSRAndOffset(cursorInfo: cursorInfo, includePublicAndOpen: includePublicAndOpen) } @@ -191,7 +191,7 @@ private extension SwiftLintFile { } private static func declaredUSRAndOffset(cursorInfo: SourceKittenDictionary, includePublicAndOpen: Bool) - -> (usr: String, nameOffset: Int)? { + -> (usr: String, nameOffset: ByteCount)? { if let offset = cursorInfo.swiftlintOffset, let usr = cursorInfo.usr, let kind = cursorInfo.declarationKind, @@ -231,7 +231,7 @@ private extension SwiftLintFile { return nil } - return (usr, Int(offset)) + return (usr, ByteCount(offset)) } return nil @@ -276,7 +276,7 @@ private extension SourceKittenDictionary { return value["key.annotated_decl"] as? String } - func aclAtOffset(_ offset: Int64) -> AccessControlLevel? { + func aclAtOffset(_ offset: ByteCount) -> AccessControlLevel? { if let nameOffset = nameOffset, nameOffset == offset, let acl = accessibility { diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedImportRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedImportRule.swift index 89778eb05..086bfe214 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedImportRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedImportRule.swift @@ -203,7 +203,7 @@ private extension SwiftLintFile { if syntaxKindsToSkip.contains(tokenKind) { continue } - let cursorInfoRequest = Request.cursorInfo(file: path!, offset: Int64(token.offset), + let cursorInfoRequest = Request.cursorInfo(file: path!, offset: token.offset, arguments: compilerArguments) guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else { queuedPrintError("Could not get cursor info") @@ -252,7 +252,7 @@ private extension SwiftLintFile { } // Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request - func operatorImports(arguments: [String], processedTokenOffsets: Set) -> Set { + func operatorImports(arguments: [String], processedTokenOffsets: Set) -> Set { guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled()) .map(SourceKittenDictionary.init) else { queuedPrintError("Could not get index") @@ -271,9 +271,9 @@ private extension SwiftLintFile { let offset = lineOffset + column - 1 // Filter already processed tokens such as static methods that are not operators - guard !processedTokenOffsets.contains(Int(offset)) else { continue } + guard !processedTokenOffsets.contains(ByteCount(offset)) else { continue } - let cursorInfoRequest = Request.cursorInfo(file: path!, offset: offset, arguments: arguments) + let cursorInfoRequest = Request.cursorInfo(file: path!, offset: ByteCount(offset), arguments: arguments) guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()) .map(SourceKittenDictionary.init) else { queuedPrintError("Could not get cursor info") diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedSetterValueRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedSetterValueRule.swift index f49573dc7..e4bd8f525 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedSetterValueRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedSetterValueRule.swift @@ -101,43 +101,43 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl public func validate(file: SwiftLintFile) -> [StyleViolation] { let setTokens = file.rangesAndTokens(matching: "\\bset\\b").keywordTokens() - let violatingLocations = setTokens.compactMap { setToken -> Int? in + let violatingLocations = setTokens.compactMap { setToken -> ByteCount? in // the last element is the deepest structure guard let dict = declarations(forByteOffset: setToken.offset, structureDictionary: file.structureDictionary).last, - let bodyOffset = dict.bodyOffset, let bodyLength = dict.bodyLength, + let bodyByteRange = dict.bodyByteRange, case let contents = file.stringView, - let propertyRange = contents.byteRangeToNSRange(start: bodyOffset, length: bodyLength), - let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict) else { - return nil + let propertyRange = contents.byteRangeToNSRange(bodyByteRange), + let getToken = findGetToken(in: propertyRange, file: file, propertyStructure: dict) + else { + return nil } let argument = findNamedArgument(after: setToken, file: file) - let propertyEndOffset = bodyOffset + bodyLength - let setterByteRange: NSRange + let propertyEndOffset = bodyByteRange.upperBound + let setterByteRange: ByteRange if setToken.offset > getToken.offset { // get {} set {} - let startOfBody: Int + let startOfBody: ByteCount if let argumentToken = argument?.token { startOfBody = argumentToken.offset + argumentToken.length } else { startOfBody = setToken.offset } - setterByteRange = NSRange(location: startOfBody, - length: propertyEndOffset - startOfBody) + setterByteRange = ByteRange(location: startOfBody, + length: propertyEndOffset - startOfBody) } else { // set {} get {} - let startOfBody: Int + let startOfBody: ByteCount if let argumentToken = argument?.token { startOfBody = argumentToken.offset + argumentToken.length } else { startOfBody = setToken.offset } - setterByteRange = NSRange(location: startOfBody, - length: getToken.offset - startOfBody) + setterByteRange = ByteRange(location: startOfBody, + length: getToken.offset - startOfBody) } - guard let setterRange = contents.byteRangeToNSRange(start: setterByteRange.location, - length: setterByteRange.length) else { + guard let setterRange = contents.byteRangeToNSRange(setterByteRange) else { return nil } @@ -188,7 +188,7 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl }) } - private func declarations(forByteOffset byteOffset: Int, + private func declarations(forByteOffset byteOffset: ByteCount, structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { var results = [SourceKittenDictionary]() let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter]) @@ -197,11 +197,10 @@ public struct UnusedSetterValueRule: ConfigurationProviderRule, AutomaticTestabl // Only accepts declarations which contains a body and contains the // searched byteOffset guard let kind = dictionary.declarationKind, - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength, - case let byteRange = NSRange(location: bodyOffset, length: bodyLength), - NSLocationInRange(byteOffset, byteRange) else { - return + let byteRange = dictionary.bodyByteRange, + byteRange.contains(byteOffset) + else { + return } if parentKind != .protocol && allowedKinds.contains(kind) { diff --git a/Source/SwiftLintFramework/Rules/Lint/WeakDelegateRule.swift b/Source/SwiftLintFramework/Rules/Lint/WeakDelegateRule.swift index 41cbaef22..84a750e54 100644 --- a/Source/SwiftLintFramework/Rules/Lint/WeakDelegateRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/WeakDelegateRule.swift @@ -74,7 +74,10 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu guard !isComputed else { return [] } guard let offset = dictionary.offset, - let range = file.stringView.byteRangeToNSRange(start: offset, length: 3) else { return [] } + let range = file.stringView.byteRangeToNSRange(ByteRange(location: offset, length: 3)) + else { + return [] + } return [range] } @@ -83,13 +86,14 @@ public struct WeakDelegateRule: ASTRule, SubstitutionCorrectableASTRule, Configu return (violationRange, "weak var") } - private func protocolDeclarations(forByteOffset byteOffset: Int, + private func protocolDeclarations(forByteOffset byteOffset: ByteCount, structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { return structureDictionary.traverseBreadthFirst { dictionary in guard dictionary.declarationKind == .protocol, let byteRange = dictionary.byteRange, - NSLocationInRange(byteOffset, byteRange) else { - return nil + byteRange.contains(byteOffset) + else { + return nil } return [dictionary] } diff --git a/Source/SwiftLintFramework/Rules/Lint/YodaConditionRule.swift b/Source/SwiftLintFramework/Rules/Lint/YodaConditionRule.swift index 0e82d79b3..04df94c61 100644 --- a/Source/SwiftLintFramework/Rules/Lint/YodaConditionRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/YodaConditionRule.swift @@ -61,11 +61,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule, public func validate(file: SwiftLintFile, kind: StatementKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { - guard observedStatements.contains(kind), - let offset = dictionary.offset, - let length = dictionary.length - else { - return [] + guard observedStatements.contains(kind), let offset = dictionary.offset else { + return [] } let matches = file.lines.filter({ $0.byteRange.contains(offset) }).reduce(into: []) { matches, line in @@ -75,15 +72,8 @@ public struct YodaConditionRule: ASTRule, OptInRule, ConfigurationProviderRule, } return matches.map { _ -> StyleViolation in - let characterOffset = startOffset(of: offset, with: length, in: file) - let location = Location(file: file, characterOffset: characterOffset) return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, - location: location) + location: Location(file: file, byteOffset: offset)) } } - - private func startOffset(of offset: Int, with length: Int, in file: SwiftLintFile) -> Int { - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) - return range?.location ?? offset - } } diff --git a/Source/SwiftLintFramework/Rules/Metrics/CyclomaticComplexityRule.swift b/Source/SwiftLintFramework/Rules/Metrics/CyclomaticComplexityRule.swift index 1ffd7b781..27d1815c8 100644 --- a/Source/SwiftLintFramework/Rules/Metrics/CyclomaticComplexityRule.swift +++ b/Source/SwiftLintFramework/Rules/Metrics/CyclomaticComplexityRule.swift @@ -89,10 +89,9 @@ public struct CyclomaticComplexityRule: ASTRule, ConfigurationProviderRule { private func reduceSwitchComplexity(initialComplexity complexity: Int, file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Int { - let bodyOffset = dictionary.bodyOffset ?? 0 - let bodyLength = dictionary.bodyLength ?? 0 + let bodyRange = dictionary.bodyByteRange ?? ByteRange(location: 0, length: 0) - let contents = file.stringView.substringWithByteRange(start: bodyOffset, length: bodyLength) ?? "" + let contents = file.stringView.substringWithByteRange(bodyRange) ?? "" let fallthroughCount = contents.components(separatedBy: "fallthrough").count - 1 return complexity - fallthroughCount diff --git a/Source/SwiftLintFramework/Rules/Metrics/FunctionParameterCountRule.swift b/Source/SwiftLintFramework/Rules/Metrics/FunctionParameterCountRule.swift index db96655f2..80f28ee92 100644 --- a/Source/SwiftLintFramework/Rules/Metrics/FunctionParameterCountRule.swift +++ b/Source/SwiftLintFramework/Rules/Metrics/FunctionParameterCountRule.swift @@ -40,10 +40,8 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { return [] } - let nameOffset = dictionary.nameOffset ?? 0 - let length = dictionary.nameLength ?? 0 - - if functionIsInitializer(file: file, byteOffset: nameOffset, byteLength: length) { + let nameRange = ByteRange(location: dictionary.nameOffset ?? 0, length: dictionary.nameLength ?? 0) + if functionIsInitializer(file: file, byteRange: nameRange) { return [] } @@ -53,8 +51,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <) - let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, offset: nameOffset, - length: length) + let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, range: nameRange) if allParameterCount < minThreshold! { return [] } @@ -62,7 +59,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { var parameterCount = allParameterCount if configuration.ignoresDefaultParameters { - parameterCount -= defaultFunctionParameterCount(file: file, byteOffset: nameOffset, byteLength: length) + parameterCount -= defaultFunctionParameterCount(file: file, byteRange: nameRange) } for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value { @@ -78,16 +75,14 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { return [] } - private func allFunctionParameterCount(structure: [SourceKittenDictionary], - offset: Int, length: Int) -> Int { + private func allFunctionParameterCount(structure: [SourceKittenDictionary], range: ByteRange) -> Int { var parameterCount = 0 for subDict in structure { - guard subDict.kind != nil, - let parameterOffset = subDict.offset else { - continue + guard subDict.kind != nil, let parameterOffset = subDict.offset else { + continue } - guard offset..<(offset + length) ~= parameterOffset else { + guard range.contains(parameterOffset) else { return parameterCount } @@ -98,15 +93,15 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { return parameterCount } - private func defaultFunctionParameterCount(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Int { - let substring = file.stringView.substringWithByteRange(start: byteOffset, length: byteLength)! + private func defaultFunctionParameterCount(file: SwiftLintFile, byteRange: ByteRange) -> Int { + let substring = file.stringView.substringWithByteRange(byteRange)! let equals = substring.filter { $0 == "=" } return equals.count } - private func functionIsInitializer(file: SwiftLintFile, byteOffset: Int, byteLength: Int) -> Bool { + private func functionIsInitializer(file: SwiftLintFile, byteRange: ByteRange) -> Bool { guard let name = file.stringView - .substringWithByteRange(start: byteOffset, length: byteLength), + .substringWithByteRange(byteRange), name.hasPrefix("init"), let funcName = name.components(separatedBy: CharacterSet(charactersIn: "<(")).first else { return false diff --git a/Source/SwiftLintFramework/Rules/Metrics/LargeTupleRule.swift b/Source/SwiftLintFramework/Rules/Metrics/LargeTupleRule.swift index 6e793ad80..2a8716171 100644 --- a/Source/SwiftLintFramework/Rules/Metrics/LargeTupleRule.swift +++ b/Source/SwiftLintFramework/Rules/Metrics/LargeTupleRule.swift @@ -73,7 +73,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta } private func violationOffsetsForTypes(in file: SwiftLintFile, dictionary: SourceKittenDictionary, - kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] { + kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] { let kinds = SwiftDeclarationKind.variableKinds.subtracting([.varLocal]) guard kinds.contains(kind), let type = dictionary.typeName, @@ -86,12 +86,11 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta } private func violationOffsetsForFunctions(in file: SwiftLintFile, dictionary: SourceKittenDictionary, - kind: SwiftDeclarationKind) -> [(offset: Int, size: Int)] { + kind: SwiftDeclarationKind) -> [(offset: ByteCount, size: Int)] { let contents = file.stringView guard SwiftDeclarationKind.functionKinds.contains(kind), let returnRange = returnRangeForFunction(dictionary: dictionary), - let returnSubstring = contents.substringWithByteRange(start: returnRange.location, - length: returnRange.length) else { + let returnSubstring = contents.substringWithByteRange(returnRange) else { return [] } @@ -99,13 +98,13 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta return offsets.sorted { $0.offset < $1.offset } } - private func violationOffsets(for text: String, initialOffset: Int = 0) -> [(offset: Int, size: Int)] { + private func violationOffsets(for text: String, initialOffset: ByteCount = 0) -> [(offset: ByteCount, size: Int)] { guard let ranges = try? parenthesesRanges(in: text) else { return [] } var text = text.bridge() - var offsets = [(offset: Int, size: Int)]() + var offsets = [(offset: ByteCount, size: Int)]() for (range, kind) in ranges { let substring = text.substring(with: range) @@ -124,7 +123,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta return offsets } - private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> NSRange? { + private func returnRangeForFunction(dictionary: SourceKittenDictionary) -> ByteRange? { guard let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, let length = dictionary.length, @@ -139,7 +138,7 @@ public struct LargeTupleRule: ASTRule, ConfigurationProviderRule, AutomaticTesta return nil } - return NSRange(location: start, length: end - start) + return ByteRange(location: start, length: end - start) } private func parenthesesRanges(in text: String) throws -> [(NSRange, RangeKind)] { diff --git a/Source/SwiftLintFramework/Rules/Performance/FirstWhereRule.swift b/Source/SwiftLintFramework/Rules/Performance/FirstWhereRule.swift index 78c347b13..7c0d8efda 100644 --- a/Source/SwiftLintFramework/Rules/Performance/FirstWhereRule.swift +++ b/Source/SwiftLintFramework/Rules/Performance/FirstWhereRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct FirstWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -47,11 +46,11 @@ public struct FirstWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule return true // has a substructure, like a closure } - guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else { + guard let bodyRange = dictionary.bodyByteRange else { return true } - let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength)) + let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange) return !syntaxKinds.contains(.string) } } diff --git a/Source/SwiftLintFramework/Rules/Performance/LastWhereRule.swift b/Source/SwiftLintFramework/Rules/Performance/LastWhereRule.swift index 3b0959e25..2b6dad8a0 100644 --- a/Source/SwiftLintFramework/Rules/Performance/LastWhereRule.swift +++ b/Source/SwiftLintFramework/Rules/Performance/LastWhereRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct LastWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule { @@ -40,11 +39,11 @@ public struct LastWhereRule: CallPairRule, OptInRule, ConfigurationProviderRule, return true // has a substructure, like a closure } - guard let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength else { + guard let bodyRange = dictionary.bodyByteRange else { return true } - let syntaxKinds = file.syntaxMap.kinds(inByteRange: NSRange(location: bodyOffset, length: bodyLength)) + let syntaxKinds = file.syntaxMap.kinds(inByteRange: bodyRange) return !syntaxKinds.contains(.string) } } diff --git a/Source/SwiftLintFramework/Rules/Performance/ReduceIntoRule.swift b/Source/SwiftLintFramework/Rules/Performance/ReduceIntoRule.swift index e64aa2e37..3c5abb3c4 100644 --- a/Source/SwiftLintFramework/Rules/Performance/ReduceIntoRule.swift +++ b/Source/SwiftLintFramework/Rules/Performance/ReduceIntoRule.swift @@ -103,7 +103,8 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut kind == .call, let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, - let nameRange = file.stringView.byteRangeToNSRange(start: nameOffset, length: nameLength), + case let nameByteRange = ByteRange(location: nameOffset, length: nameLength), + let nameRange = file.stringView.byteRangeToNSRange(nameByteRange), let match = reduceExpression.firstMatch(in: file.contents, options: [], range: nameRange), dictionary.enclosedArguments.count == 2, // would otherwise equal "into" @@ -132,13 +133,11 @@ public struct ReduceIntoRule: ASTRule, ConfigurationProviderRule, OptInRule, Aut } let contents = file.stringView - guard let offset = argument.offset, - let length = argument.length, - let range = contents.byteRangeToNSRange(start: offset, length: length) + guard let byteRange = argument.byteRange, + let range = contents.byteRangeToNSRange(byteRange) else { return false } // Check for string literal - let byteRange = NSRange(location: offset, length: length) let kinds = file.syntaxMap.kinds(inByteRange: byteRange) if kinds == [.string] { return true diff --git a/Source/SwiftLintFramework/Rules/Style/AttributesRule.swift b/Source/SwiftLintFramework/Rules/Style/AttributesRule.swift index c5bbea947..e62a7ea1d 100644 --- a/Source/SwiftLintFramework/Rules/Style/AttributesRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/AttributesRule.swift @@ -138,7 +138,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule { } private func createAlwaysOnNewLineAttributes(previousAttributes: [(String, Bool)], - attributesTokens: [(String, NSRange)], + attributesTokens: [(String, ByteRange)], line: Line, file: SwiftLintFile) -> Set { let attributesTokensWithParameters: [(String, Bool)] = attributesTokens.map { let hasParameter = attributeContainsParameter(attributeRange: $1, @@ -241,16 +241,17 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule { return allTokens } - private func attributeContainsParameter(attributeRange: NSRange, + private func attributeContainsParameter(attributeRange: ByteRange, line: Line, file: SwiftLintFile) -> Bool { - let restOfLineOffset = attributeRange.location + attributeRange.length - let restOfLineLength = line.byteRange.location + line.byteRange.length - restOfLineOffset + let restOfLineOffset = attributeRange.upperBound + let restOfLineLength = line.byteRange.upperBound - restOfLineOffset let regex = AttributesRule.regularExpression let contents = file.stringView // check if after the token is a `(` with only spaces allowed between the token and `(` - guard let restOfLine = contents.substringWithByteRange(start: restOfLineOffset, length: restOfLineLength), + let restOfLineByteRange = ByteRange(location: restOfLineOffset, length: restOfLineLength) + guard let restOfLine = contents.substringWithByteRange(restOfLineByteRange), case let range = restOfLine.fullNSRange, regex.firstMatch(in: restOfLine, options: [], range: range) != nil else { return false @@ -259,7 +260,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule { return true } - private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, NSRange)? { + private func attributeName(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> (String, ByteRange)? { guard token.kind == .attributeBuiltin else { return nil } diff --git a/Source/SwiftLintFramework/Rules/Style/ClosureEndIndentationRule.swift b/Source/SwiftLintFramework/Rules/Style/ClosureEndIndentationRule.swift index 2fd03ce2c..f188c5113 100644 --- a/Source/SwiftLintFramework/Rules/Style/ClosureEndIndentationRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ClosureEndIndentationRule.swift @@ -38,8 +38,12 @@ public struct ClosureEndIndentationRule: Rule, OptInRule, ConfigurationProviderR extension ClosureEndIndentationRule: CorrectableRule { public func correct(file: SwiftLintFile) -> [Correction] { - let allViolations = violations(in: file).reversed().filter { - !file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty + let allViolations = violations(in: file).reversed().filter { violation in + guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else { + return false + } + + return !file.ruleEnabled(violatingRanges: [nsRange], for: self).isEmpty } guard !allViolations.isEmpty else { @@ -107,8 +111,8 @@ extension ClosureEndIndentationRule: CorrectableRule { extension ClosureEndIndentationRule { fileprivate struct Violation { var indentationRanges: (expected: NSRange, actual: NSRange) - var endOffset: Int - var range: NSRange + var endOffset: ByteCount + var range: ByteRange } fileprivate func violations(in file: SwiftLintFile) -> [Violation] { @@ -136,11 +140,10 @@ extension ClosureEndIndentationRule { private func hasTrailingClosure(in file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool { guard - let offset = dictionary.offset, - let length = dictionary.length, - let text = file.stringView.substringWithByteRange(start: offset, length: length) - else { - return false + let byteRange = dictionary.byteRange, + let text = file.stringView.substringWithByteRange(byteRange) + else { + return false } return !text.hasSuffix(")") @@ -156,15 +159,17 @@ extension ClosureEndIndentationRule { let nameLength = dictionary.nameLength, bodyLength > 0, case let endOffset = offset + length - 1, - contents.substringWithByteRange(start: endOffset, length: 1) == "}", + case let closingBraceByteRange = ByteRange(location: endOffset, length: 1), + contents.substringWithByteRange(closingBraceByteRange) == "}", let startOffset = startOffset(forDictionary: dictionary, file: file), let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset), let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), case let nameEndPosition = nameOffset + nameLength, let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), startLine != endLine, bodyOffsetLine != endLine, - !containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { - return nil + !containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) + else { + return nil } let range = file.lines[startLine - 1].range @@ -172,8 +177,9 @@ extension ClosureEndIndentationRule { let actual = endPosition - 1 guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, - expected != actual else { - return nil + expected != actual + else { + return nil } var expectedRange = range @@ -184,7 +190,7 @@ extension ClosureEndIndentationRule { return Violation(indentationRanges: (expected: expectedRange, actual: actualRange), endOffset: endOffset, - range: NSRange(location: offset, length: length)) + range: ByteRange(location: offset, length: length)) } private func validateArguments(in file: SwiftLintFile, @@ -216,15 +222,17 @@ extension ClosureEndIndentationRule { let nameLength = dictionary.nameLength, bodyLength > 0, case let endOffset = offset + length - 1, - contents.substringWithByteRange(start: endOffset, length: 1) == "}", + case let closingBraceByteRange = ByteRange(location: endOffset, length: 1), + contents.substringWithByteRange(closingBraceByteRange) == "}", let startOffset = dictionary.offset, let (startLine, _) = contents.lineAndCharacter(forByteOffset: startOffset), let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), case let nameEndPosition = nameOffset + nameLength, let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), startLine != endLine, bodyOffsetLine != endLine, - !isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { - return nil + !isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) + else { + return nil } let range = file.lines[startLine - 1].range @@ -232,8 +240,9 @@ extension ClosureEndIndentationRule { let actual = endPosition - 1 guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, - expected != actual else { - return nil + expected != actual + else { + return nil } var expectedRange = range @@ -244,30 +253,28 @@ extension ClosureEndIndentationRule { return Violation(indentationRanges: (expected: expectedRange, actual: actualRange), endOffset: endOffset, - range: NSRange(location: offset, length: length)) + range: ByteRange(location: offset, length: length)) } - private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Int? { - guard let nameOffset = dictionary.nameOffset, - let nameLength = dictionary.nameLength else { + private func startOffset(forDictionary dictionary: SourceKittenDictionary, file: SwiftLintFile) -> ByteCount? { + guard let nameByteRange = dictionary.nameByteRange else { return nil } let newLineRegex = regex("\n(\\s*\\}?\\.)") let contents = file.stringView - guard let range = contents.byteRangeToNSRange(start: nameOffset, length: nameLength), - let match = newLineRegex.matches(in: file.contents, options: [], - range: range).last?.range(at: 1), - let methodByteRange = contents.NSRangeToByteRange(start: match.location, - length: match.length) else { - return nameOffset + guard let range = contents.byteRangeToNSRange(nameByteRange), + let match = newLineRegex.matches(in: file.contents, options: [], range: range).last?.range(at: 1), + let methodByteRange = contents.NSRangeToByteRange(start: match.location, length: match.length) + else { + return nameByteRange.location } return methodByteRange.location } private func isSingleLineClosure(dictionary: SourceKittenDictionary, - endPosition: Int, file: SwiftLintFile) -> Bool { + endPosition: ByteCount, file: SwiftLintFile) -> Bool { let contents = file.stringView guard let start = dictionary.bodyOffset, @@ -280,7 +287,7 @@ extension ClosureEndIndentationRule { } private func containsSingleLineClosure(dictionary: SourceKittenDictionary, - endPosition: Int, file: SwiftLintFile) -> Bool { + endPosition: ByteCount, file: SwiftLintFile) -> Bool { let contents = file.stringView guard let closure = trailingClosure(dictionary: dictionary, file: file), @@ -309,12 +316,12 @@ extension ClosureEndIndentationRule { private func filterClosureArguments(_ arguments: [SourceKittenDictionary], file: SwiftLintFile) -> [SourceKittenDictionary] { return arguments.filter { argument in - guard let offset = argument.bodyOffset, - let length = argument.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length), + guard let bodyByteRange = argument.bodyByteRange, + let range = file.stringView.byteRangeToNSRange(bodyByteRange), let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range, - match.location == range.location else { - return false + match.location == range.location + else { + return false } return true @@ -330,10 +337,12 @@ extension ClosureEndIndentationRule { let firstArgumentOffset = firstArgument.offset, case let offset = nameOffset + nameLength, case let length = firstArgumentOffset - offset, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length), + case let byteRange = ByteRange(location: offset, length: length), + let range = file.stringView.byteRangeToNSRange(byteRange), let match = regex("\\(\\s*\\n\\s*").firstMatch(in: file.contents, options: [], range: range)?.range, - match.location == range.location else { - return false + match.location == range.location + else { + return false } return true diff --git a/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift b/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift index faab12998..180ad8e4c 100644 --- a/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ClosureParameterPositionRule.swift @@ -52,8 +52,9 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule, guard let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, let bodyLength = dictionary.bodyLength, - bodyLength > 0 else { - return [] + bodyLength > 0 + else { + return [] } let parameters = dictionary.enclosedVarParameters @@ -70,14 +71,16 @@ public struct ClosureParameterPositionRule: ASTRule, ConfigurationProviderRule, let rangeLength = paramOffset - rangeStart let contents = file.stringView - guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength), + let byteRange = ByteRange(location: rangeStart, length: rangeLength) + guard let range = contents.byteRangeToNSRange(byteRange), let match = regex.matches(in: file.contents, options: [], range: range).last?.range, match.location != NSNotFound, let braceOffset = contents.NSRangeToByteRange(start: match.location, length: match.length)?.location, let (braceLine, _) = contents.lineAndCharacter(forByteOffset: braceOffset), let (paramLine, _) = contents.lineAndCharacter(forByteOffset: paramOffset), - braceLine != paramLine else { - return nil + braceLine != paramLine + else { + return nil } return StyleViolation(ruleDescription: type(of: self).description, diff --git a/Source/SwiftLintFramework/Rules/Style/ClosureSpacingRule.swift b/Source/SwiftLintFramework/Rules/Style/ClosureSpacingRule.swift index 8cc136f82..91203c0d6 100644 --- a/Source/SwiftLintFramework/Rules/Style/ClosureSpacingRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ClosureSpacingRule.swift @@ -90,7 +90,7 @@ public struct ClosureSpacingRule: CorrectableRule, ConfigurationProviderRule, Op return kindsToExclude.contains(tokenKind) } let tokenRanges = tokens.compactMap { - file.stringView.byteRangeToNSRange(start: $0.offset, length: $0.length) + file.stringView.byteRangeToNSRange($0.range) } linesWithBraces.append(braces.filter({ !$0.intersects(tokenRanges) })) } diff --git a/Source/SwiftLintFramework/Rules/Style/CollectionAlignmentRule.swift b/Source/SwiftLintFramework/Rules/Style/CollectionAlignmentRule.swift index 2a5765c30..4db242928 100644 --- a/Source/SwiftLintFramework/Rules/Style/CollectionAlignmentRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/CollectionAlignmentRule.swift @@ -86,12 +86,13 @@ public struct CollectionAlignmentRule: ASTRule, ConfigurationProviderRule, OptIn } } - private func colonLocation(with file: SwiftLintFile, keyOffset: Int, keyLength: Int, - valueOffset: Int) -> Location? { + private func colonLocation(with file: SwiftLintFile, keyOffset: ByteCount, keyLength: ByteCount, + valueOffset: ByteCount) -> Location? { let contents = file.stringView let matchStart = keyOffset + keyLength let matchLength = valueOffset - matchStart - let range = contents.byteRangeToNSRange(start: matchStart, length: matchLength) + let byteRange = ByteRange(location: matchStart, length: matchLength) + let range = contents.byteRangeToNSRange(byteRange) let matches = file.match(pattern: ":", excludingSyntaxKinds: [.comment], range: range) return matches.first.map { Location(file: file, characterOffset: $0.location) } diff --git a/Source/SwiftLintFramework/Rules/Style/ColonRule+Dictionary.swift b/Source/SwiftLintFramework/Rules/Style/ColonRule+Dictionary.swift index 5c7654e51..0c447e4c2 100644 --- a/Source/SwiftLintFramework/Rules/Style/ColonRule+Dictionary.swift +++ b/Source/SwiftLintFramework/Rules/Style/ColonRule+Dictionary.swift @@ -1,14 +1,13 @@ -import Foundation import SourceKittenFramework extension ColonRule { internal func dictionaryColonViolationRanges(in file: SwiftLintFile, - dictionary: SourceKittenDictionary) -> [NSRange] { + dictionary: SourceKittenDictionary) -> [ByteRange] { guard configuration.applyToDictionaries else { return [] } - let ranges: [NSRange] = dictionary.traverseDepthFirst { subDict in + let ranges: [ByteRange] = dictionary.traverseDepthFirst { subDict in guard let kind = subDict.expressionKind else { return nil } return dictionaryColonViolationRanges(in: file, kind: kind, dictionary: subDict) } @@ -17,7 +16,7 @@ extension ColonRule { } internal func dictionaryColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind, - dictionary: SourceKittenDictionary) -> [NSRange] { + dictionary: SourceKittenDictionary) -> [ByteRange] { guard kind == .dictionary, let ranges = dictionaryColonRanges(dictionary: dictionary) else { return [] @@ -25,7 +24,7 @@ extension ColonRule { let contents = file.stringView return ranges.filter { - guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else { + guard let colon = contents.substringWithByteRange($0) else { return false } @@ -38,31 +37,28 @@ extension ColonRule { } } - private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? { + private func dictionaryColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? { let elements = dictionary.elements guard elements.count % 2 == 0 else { return nil } let expectedKind = "source.lang.swift.structure.elem.expr" - let ranges: [NSRange] = elements.compactMap { subDict in - guard subDict.kind == expectedKind, - let offset = subDict.offset, - let length = subDict.length else { - return nil + let ranges: [ByteRange] = elements.compactMap { subDict in + guard subDict.kind == expectedKind else { + return nil } - return NSRange(location: offset, length: length) + return subDict.byteRange } let even = ranges.enumerated().compactMap { $0 % 2 == 0 ? $1 : nil } let odd = ranges.enumerated().compactMap { $0 % 2 != 0 ? $1 : nil } - return zip(even, odd).map { evenRange, oddRange -> NSRange in - let location = NSMaxRange(evenRange) + return zip(even, odd).map { evenRange, oddRange -> ByteRange in + let location = evenRange.upperBound let length = oddRange.location - location - - return NSRange(location: location, length: length) + return ByteRange(location: location, length: length) } } } diff --git a/Source/SwiftLintFramework/Rules/Style/ColonRule+FunctionCall.swift b/Source/SwiftLintFramework/Rules/Style/ColonRule+FunctionCall.swift index a018383ba..1410236a0 100644 --- a/Source/SwiftLintFramework/Rules/Style/ColonRule+FunctionCall.swift +++ b/Source/SwiftLintFramework/Rules/Style/ColonRule+FunctionCall.swift @@ -1,9 +1,8 @@ -import Foundation import SourceKittenFramework extension ColonRule { internal func functionCallColonViolationRanges(in file: SwiftLintFile, - dictionary: SourceKittenDictionary) -> [NSRange] { + dictionary: SourceKittenDictionary) -> [ByteRange] { return dictionary.traverseDepthFirst { subDict in guard let kind = subDict.expressionKind else { return nil } return functionCallColonViolationRanges(in: file, kind: kind, dictionary: subDict) @@ -11,15 +10,16 @@ extension ColonRule { } internal func functionCallColonViolationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind, - dictionary: SourceKittenDictionary) -> [NSRange] { + dictionary: SourceKittenDictionary) -> [ByteRange] { guard kind == .argument, - let ranges = functionCallColonRanges(dictionary: dictionary) else { - return [] + let ranges = functionCallColonRanges(dictionary: dictionary) + else { + return [] } let contents = file.stringView return ranges.filter { - guard let colon = contents.substringWithByteRange(start: $0.location, length: $0.length) else { + guard let colon = contents.substringWithByteRange($0) else { return false } @@ -32,15 +32,16 @@ extension ColonRule { } } - private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [NSRange]? { + private func functionCallColonRanges(dictionary: SourceKittenDictionary) -> [ByteRange]? { guard let nameOffset = dictionary.nameOffset, let nameLength = dictionary.nameLength, nameLength > 0, let bodyOffset = dictionary.bodyOffset, case let location = nameOffset + nameLength, - bodyOffset > location else { - return nil + bodyOffset > location + else { + return nil } - return [NSRange(location: location, length: bodyOffset - location)] + return [ByteRange(location: location, length: bodyOffset - location)] } } diff --git a/Source/SwiftLintFramework/Rules/Style/ColonRule+Type.swift b/Source/SwiftLintFramework/Rules/Style/ColonRule+Type.swift index 9e99c2a95..4598415f4 100644 --- a/Source/SwiftLintFramework/Rules/Style/ColonRule+Type.swift +++ b/Source/SwiftLintFramework/Rules/Style/ColonRule+Type.swift @@ -32,8 +32,8 @@ internal extension ColonRule { return isValidMatch(syntaxTokens: syntaxTokens, file: file) }.compactMap { match, syntaxTokens in - let identifierRange = contents - .byteRangeToNSRange(start: syntaxTokens[0].offset, length: 0) + let firstSyntaxTokenByteRange = ByteRange(location: syntaxTokens[0].offset, length: 0) + let identifierRange = contents.byteRangeToNSRange(firstSyntaxTokenByteRange) return identifierRange.map { NSUnionRange($0, match.range) } } } @@ -53,7 +53,7 @@ internal extension ColonRule { case (.identifier, .keyword), (.typeidentifier, .keyword): validKinds = file.isTypeLike(token: syntaxTokens[1]) - //Exclude explicit "Self" type because of static variables + // Exclude explicit "Self" type because of static variables if syntaxKinds[0] == .identifier, file.contents(for: syntaxTokens[1]) == "Self" { validKinds = false diff --git a/Source/SwiftLintFramework/Rules/Style/ColonRule.swift b/Source/SwiftLintFramework/Rules/Style/ColonRule.swift index 68d194dfb..b3ded7d13 100644 --- a/Source/SwiftLintFramework/Rules/Style/ColonRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ColonRule.swift @@ -79,17 +79,11 @@ public struct ColonRule: CorrectableRule, ConfigurationProviderRule { let contents = file.stringView let dictViolations: [RangeWithKind] = dictionaryColonViolationRanges(in: file, dictionary: dictionary).compactMap { - guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else { - return nil - } - return (range: range, kind: .dictionary) + return contents.byteRangeToNSRange($0).map { (range: $0, kind: .dictionary) } } let functionViolations: [RangeWithKind] = functionCallColonViolationRanges(in: file, dictionary: dictionary).compactMap { - guard let range = contents.byteRangeToNSRange(start: $0.location, length: $0.length) else { - return nil - } - return (range: range, kind: .functionCall) + return contents.byteRangeToNSRange($0).map { (range: $0, kind: .functionCall) } } return (violations + dictViolations + functionViolations).sorted { diff --git a/Source/SwiftLintFramework/Rules/Style/EmptyEnumArgumentsRule.swift b/Source/SwiftLintFramework/Rules/Style/EmptyEnumArgumentsRule.swift index c4a3ab6f4..0bda13a81 100644 --- a/Source/SwiftLintFramework/Rules/Style/EmptyEnumArgumentsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/EmptyEnumArgumentsRule.swift @@ -77,10 +77,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat let callsRanges = dictionary.substructure.compactMap { dict -> NSRange? in guard dict.expressionKind == .call, - let offset = dict.offset, - let length = dict.length, - let range = contents.byteRangeToNSRange(start: offset, length: length) else { - return nil + let byteRange = dict.byteRange, + let range = contents.byteRangeToNSRange(byteRange) + else { + return nil } return range @@ -88,10 +88,10 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat return dictionary.elements.flatMap { subDictionary -> [NSRange] in guard subDictionary.kind == "source.lang.swift.structure.elem.pattern", - let offset = subDictionary.offset, - let length = subDictionary.length, - let caseRange = contents.byteRangeToNSRange(start: offset, length: length) else { - return [] + let byteRange = subDictionary.byteRange, + let caseRange = contents.byteRangeToNSRange(byteRange) + else { + return [] } let emptyArgumentRegex = regex("\\.\\S+\\s*(\\([,\\s_]*\\))") @@ -107,8 +107,8 @@ public struct EmptyEnumArgumentsRule: SubstitutionCorrectableASTRule, Configurat // avoid matches in "(_, _) where" if let whereByteRange = contents.NSRangeToByteRange(start: whereRange.location, length: whereRange.length), - case let length = whereByteRange.location - offset, - case let byteRange = NSRange(location: offset, length: length), + case let length = whereByteRange.location - byteRange.location, + case let byteRange = ByteRange(location: byteRange.location, length: length), Set(file.syntaxMap.kinds(inByteRange: byteRange)) == [.keyword] { return nil } diff --git a/Source/SwiftLintFramework/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift b/Source/SwiftLintFramework/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift index 6086e39a0..b7380174f 100644 --- a/Source/SwiftLintFramework/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift @@ -82,12 +82,14 @@ public struct EmptyParenthesesWithTrailingClosureRule: SubstitutionCorrectableAS let rangeStart = nameOffset + nameLength let rangeLength = (offset + length) - (nameOffset + nameLength) + let byteRange = ByteRange(location: rangeStart, length: rangeLength) let regex = EmptyParenthesesWithTrailingClosureRule.emptyParenthesesRegex - guard let range = file.stringView.byteRangeToNSRange(start: rangeStart, length: rangeLength), + guard let range = file.stringView.byteRangeToNSRange(byteRange), let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, - match.location == range.location else { - return [] + match.location == range.location + else { + return [] } return [match] diff --git a/Source/SwiftLintFramework/Rules/Style/ExplicitSelfRule.swift b/Source/SwiftLintFramework/Rules/Style/ExplicitSelfRule.swift index 9d1919cc3..898982807 100644 --- a/Source/SwiftLintFramework/Rules/Style/ExplicitSelfRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ExplicitSelfRule.swift @@ -140,12 +140,12 @@ public struct ExplicitSelfRule: CorrectableRule, ConfigurationProviderRule, Anal let contents = file.stringView return cursorsMissingExplicitSelf.compactMap { cursorInfo in - guard let byteOffset = cursorInfo["swiftlint.offset"] as? Int64 else { + guard let byteOffset = (cursorInfo["swiftlint.offset"] as? Int64).flatMap(ByteCount.init) else { queuedPrintError("couldn't convert offsets") return nil } - return contents.byteRangeToNSRange(start: Int(byteOffset), length: 0) + return contents.byteRangeToNSRange(ByteRange(location: byteOffset, length: 0)) } } } @@ -156,29 +156,29 @@ private let kindsToFind: Set = [ ] private extension SwiftLintFile { - func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [Int]) throws + func allCursorInfo(compilerArguments: [String], atByteOffsets byteOffsets: [ByteCount]) throws -> [[String: SourceKitRepresentable]] { return try byteOffsets.compactMap { offset in - if stringView.substringWithByteRange(start: offset - 1, length: 1)! == "." { return nil } - var cursorInfo = try Request.cursorInfo(file: self.path!, offset: Int64(offset), + if stringView.substringWithByteRange(ByteRange(location: offset - 1, length: 1))! == "." { return nil } + var cursorInfo = try Request.cursorInfo(file: self.path!, offset: offset, arguments: compilerArguments).sendIfNotDisabled() - cursorInfo["swiftlint.offset"] = Int64(offset) + cursorInfo["swiftlint.offset"] = Int64(offset.value) return cursorInfo } } } private extension StringView { - func byteOffset(forLine line: Int, column: Int) -> Int { - var byteOffset = 0 + func byteOffset(forLine line: Int, column: Int) -> ByteCount { + var byteOffset = ByteCount(0) for line in lines[..<(line - 1)] { byteOffset += line.byteRange.length } - return byteOffset + column - 1 + return byteOffset + ByteCount(column - 1) } - func recursiveByteOffsets(_ dict: [String: Any]) -> [Int] { - let cur: [Int] + func recursiveByteOffsets(_ dict: [String: Any]) -> [ByteCount] { + let cur: [ByteCount] if let line = dict["key.line"] as? Int64, let column = dict["key.column"] as? Int64, let kindString = dict["key.kind"] as? String, @@ -194,7 +194,7 @@ private extension StringView { } } -private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [Int] { +private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [ByteCount] { let absoluteFile = file.path!.bridge().absolutePathRepresentation() let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled() let binaryOffsets = file.stringView.recursiveByteOffsets(index) diff --git a/Source/SwiftLintFramework/Rules/Style/FileHeaderRule.swift b/Source/SwiftLintFramework/Rules/Style/FileHeaderRule.swift index db464f5b5..756b76c28 100644 --- a/Source/SwiftLintFramework/Rules/Style/FileHeaderRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/FileHeaderRule.swift @@ -63,7 +63,8 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule { if let firstToken = firstToken, let lastToken = lastToken { let start = firstToken.offset let length = lastToken.offset + lastToken.length - firstToken.offset - guard let range = file.stringView.byteRangeToNSRange(start: start, length: length) else { + let byteRange = ByteRange(location: start, length: length) + guard let range = file.stringView.byteRangeToNSRange(byteRange) else { return [] } @@ -75,36 +76,32 @@ public struct FileHeaderRule: ConfigurationProviderRule, OptInRule { if let regex = requiredRegex, case let matches = regex.matches(in: file.contents, options: [], range: range), matches.isEmpty { - violationsOffsets.append(start) + violationsOffsets.append(file.stringView.location(fromByteOffset: start)) } } else if requiredRegex != nil { let location = firstNonCommentToken.map { Location(file: file, byteOffset: $0.offset) } ?? Location(file: file.path, line: 1) - return [ - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severityConfiguration.severity, - location: location, - reason: type(of: self).reason) - ] + return [makeViolation(at: location)] } - return violationsOffsets.map { - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severityConfiguration.severity, - location: Location(file: file, characterOffset: $0), - reason: type(of: self).reason) - } + return violationsOffsets.map { makeViolation(at: Location(file: file, characterOffset: $0)) } } private func isSwiftLintCommand(token: SwiftLintSyntaxToken, file: SwiftLintFile) -> Bool { - guard let range = file.stringView.byteRangeToNSRange(start: token.offset, - length: token.length) else { - return false + guard let range = file.stringView.byteRangeToNSRange(token.range) else { + return false } return !file.commands(in: range).isEmpty } + + private func makeViolation(at location: Location) -> StyleViolation { + return StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severityConfiguration.severity, + location: location, + reason: type(of: self).reason) + } } private extension SyntaxKind { diff --git a/Source/SwiftLintFramework/Rules/Style/FileTypesOrderRule.swift b/Source/SwiftLintFramework/Rules/Style/FileTypesOrderRule.swift index 19d5637b9..2fb776be3 100644 --- a/Source/SwiftLintFramework/Rules/Style/FileTypesOrderRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/FileTypesOrderRule.swift @@ -2,7 +2,7 @@ import Foundation import SourceKittenFramework public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule { - private typealias FileTypeOffset = (fileType: FileType, offset: Int) + private typealias FileTypeOffset = (fileType: FileType, offset: ByteCount) public var configuration = FileTypesOrderConfiguration() diff --git a/Source/SwiftLintFramework/Rules/Style/IdentifierNameRule.swift b/Source/SwiftLintFramework/Rules/Style/IdentifierNameRule.swift index 825f07b7a..607268e97 100644 --- a/Source/SwiftLintFramework/Rules/Style/IdentifierNameRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/IdentifierNameRule.swift @@ -80,7 +80,7 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule { } private func validateName(dictionary: SourceKittenDictionary, - kind: SwiftDeclarationKind) -> (name: String, offset: Int)? { + kind: SwiftDeclarationKind) -> (name: String, offset: ByteCount)? { guard var name = dictionary.name, let offset = dictionary.offset, kinds.contains(kind), diff --git a/Source/SwiftLintFramework/Rules/Style/ImplicitGetterRule.swift b/Source/SwiftLintFramework/Rules/Style/ImplicitGetterRule.swift index 7647eb792..d668ed74e 100644 --- a/Source/SwiftLintFramework/Rules/Style/ImplicitGetterRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ImplicitGetterRule.swift @@ -1,4 +1,3 @@ -import Foundation import SourceKittenFramework public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRule { @@ -189,7 +188,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu return token } - let violatingLocations = getTokens.compactMap { token -> (Int, SwiftDeclarationKind?)? in + let violatingLocations = getTokens.compactMap { token -> (ByteCount, SwiftDeclarationKind?)? in // the last element is the deepest structure guard let dict = declarations(forByteOffset: token.offset, structureDictionary: file.structureDictionary).last else { @@ -220,7 +219,7 @@ public struct ImplicitGetterRule: ConfigurationProviderRule, AutomaticTestableRu } private extension ImplicitGetterRule { - func declarations(forByteOffset byteOffset: Int, + func declarations(forByteOffset byteOffset: ByteCount, structureDictionary: SourceKittenDictionary) -> [SourceKittenDictionary] { var results = [SourceKittenDictionary]() let allowedKinds = SwiftDeclarationKind.variableKinds.subtracting([.varParameter]) @@ -230,11 +229,10 @@ private extension ImplicitGetterRule { // Only accepts declarations which contains a body and contains the // searched byteOffset guard let kind = dictionary.declarationKind, - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength, - case let byteRange = NSRange(location: bodyOffset, length: bodyLength), - NSLocationInRange(byteOffset, byteRange) else { - return + let byteRange = dictionary.byteRange, + byteRange.contains(byteOffset) + else { + return } if parentKind != .protocol && allowedKinds.contains(kind) { diff --git a/Source/SwiftLintFramework/Rules/Style/ImplicitReturnRule.swift b/Source/SwiftLintFramework/Rules/Style/ImplicitReturnRule.swift index e3c3148b9..0c0ca76b5 100644 --- a/Source/SwiftLintFramework/Rules/Style/ImplicitReturnRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ImplicitReturnRule.swift @@ -35,11 +35,10 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, SubstitutionCorrect return file.matchesAndSyntaxKinds(matching: pattern).compactMap { result, kinds in let range = result.range guard kinds == [.keyword, .keyword] || kinds == [.keyword], - let byteRange = contents.NSRangeToByteRange(start: range.location, - length: range.length), + let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length), let outerKindString = file.structureDictionary.kinds(forByteOffset: byteRange.location).last?.kind - else { - return nil + else { + return nil } func isKindIncluded(_ kind: ImplicitReturnConfiguration.ReturnKind) -> Bool { diff --git a/Source/SwiftLintFramework/Rules/Style/LetVarWhitespaceRule.swift b/Source/SwiftLintFramework/Rules/Style/LetVarWhitespaceRule.swift index 2d6c1cb82..ae97ec4ce 100644 --- a/Source/SwiftLintFramework/Rules/Style/LetVarWhitespaceRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/LetVarWhitespaceRule.swift @@ -119,7 +119,8 @@ public struct LetVarWhitespaceRule: ConfigurationProviderRule, OptInRule, Automa private func lineOffsets(file: SwiftLintFile, statement: SourceKittenDictionary) -> (Int, Int)? { guard let offset = statement.offset, - let length = statement.length else { + let length = statement.length + else { return nil } let startLine = file.line(byteOffset: offset) @@ -230,7 +231,7 @@ private extension SwiftDeclarationKind { private extension SwiftLintFile { // Zero based line number for specified byte offset - func line(byteOffset: Int) -> Int { + func line(byteOffset: ByteCount) -> Int { let lineIndex = lines.firstIndexAssumingSorted { line in return line.byteRange.location > byteOffset } diff --git a/Source/SwiftLintFramework/Rules/Style/LiteralExpressionEndIdentationRule.swift b/Source/SwiftLintFramework/Rules/Style/LiteralExpressionEndIdentationRule.swift index c084423ab..9f0241ffc 100644 --- a/Source/SwiftLintFramework/Rules/Style/LiteralExpressionEndIdentationRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/LiteralExpressionEndIdentationRule.swift @@ -111,8 +111,12 @@ public struct LiteralExpressionEndIdentationRule: Rule, ConfigurationProviderRul extension LiteralExpressionEndIdentationRule: CorrectableRule { public func correct(file: SwiftLintFile) -> [Correction] { - let allViolations = violations(in: file).reversed().filter { - !file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty + let allViolations = violations(in: file).reversed().filter { violation in + guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else { + return false + } + + return !file.ruleEnabled(violatingRanges: [nsRange], for: self).isEmpty } guard !allViolations.isEmpty else { @@ -173,8 +177,8 @@ extension LiteralExpressionEndIdentationRule: CorrectableRule { extension LiteralExpressionEndIdentationRule { fileprivate struct Violation { var indentationRanges: (expected: NSRange, actual: NSRange) - var endOffset: Int - var range: NSRange + var endOffset: ByteCount + var range: ByteRange } fileprivate func violations(in file: SwiftLintFile) -> [Violation] { @@ -205,8 +209,9 @@ extension LiteralExpressionEndIdentationRule { let (lastParamLine, _) = contents.lineAndCharacter(forByteOffset: lastParamOffset), case let endOffset = offset + length - 1, let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), - lastParamLine != endLine else { - return nil + lastParamLine != endLine + else { + return nil } let range = file.lines[startLine - 1].range @@ -214,8 +219,9 @@ extension LiteralExpressionEndIdentationRule { let actual = endPosition - 1 guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, - expected != actual else { - return nil + expected != actual + else { + return nil } var expectedRange = range @@ -226,6 +232,6 @@ extension LiteralExpressionEndIdentationRule { return Violation(indentationRanges: (expected: expectedRange, actual: actualRange), endOffset: endOffset, - range: NSRange(location: offset, length: length)) + range: ByteRange(location: offset, length: length)) } } diff --git a/Source/SwiftLintFramework/Rules/Style/ModifierOrderRule.swift b/Source/SwiftLintFramework/Rules/Style/ModifierOrderRule.swift index 72e8641b9..ae3832fc4 100644 --- a/Source/SwiftLintFramework/Rules/Style/ModifierOrderRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ModifierOrderRule.swift @@ -71,13 +71,12 @@ public struct ModifierOrderRule: ASTRule, OptInRule, ConfigurationProviderRule, .compactMap { preferred, declared -> (NSRange, NSRange)? in guard let preferredRange = originalContents.byteRangeToNSRange( - start: preferred.offset, - length: preferred.length + preferred.range ).flatMap({ file.ruleEnabled(violatingRange: $0, for: self) }), let declaredRange = originalContents.byteRangeToNSRange( - start: declared.offset, - length: declared.length - ).flatMap({ file.ruleEnabled(violatingRange: $0, for: self) }) else { + declared.range + ).flatMap({ file.ruleEnabled(violatingRange: $0, for: self) }) + else { return nil } return (preferredRange, declaredRange) @@ -174,7 +173,7 @@ private extension SourceKittenDictionary { keyword: keyword, group: .typeMethods, offset: offset, - length: keyword.count + length: ByteCount(keyword.lengthOfBytes(using: .utf8)) ) } return nil @@ -184,11 +183,12 @@ private extension SourceKittenDictionary { private func kindsAndOffsets(in declarationKinds: [SwiftDeclarationKind]) -> SourceKittenDictionary? { guard let offset = offset, let declarationKind = declarationKind, - declarationKinds.contains(declarationKind) else { - return nil + declarationKinds.contains(declarationKind) + else { + return nil } - return SourceKittenDictionary(["key.kind": declarationKind.rawValue, "key.offset": Int64(offset)]) + return SourceKittenDictionary(["key.kind": declarationKind.rawValue, "key.offset": Int64(offset.value)]) } } @@ -201,6 +201,7 @@ private extension String { private struct ModifierDescription: Equatable { let keyword: String let group: SwiftDeclarationAttributeKind.ModifierGroup - let offset: Int - let length: Int + let offset: ByteCount + let length: ByteCount + var range: ByteRange { return ByteRange(location: offset, length: length) } } diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsBracketsRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsBracketsRule.swift index 8eb40cfa7..981db3480 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsBracketsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsBracketsRule.swift @@ -80,9 +80,8 @@ public struct MultilineArgumentsBracketsRule: ASTRule, OptInRule, ConfigurationP dictionary: SourceKittenDictionary) -> [StyleViolation] { guard kind == .call, - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: bodyOffset, length: bodyLength) + let bodyRange = dictionary.bodyByteRange, + let range = file.stringView.byteRangeToNSRange(bodyRange) else { return [] } @@ -96,13 +95,13 @@ public struct MultilineArgumentsBracketsRule: ASTRule, OptInRule, ConfigurationP let expectedBodyBeginRegex = regex("\\A(?:[ \\t]*\\n|[^\\n]*(?:in|\\{)\\n)") let expectedBodyEndRegex = regex("\\n[ \\t]*\\z") - var violatingByteOffsets = [Int]() + var violatingByteOffsets = [ByteCount]() if expectedBodyBeginRegex.firstMatch(in: body, options: [], range: body.fullNSRange) == nil { - violatingByteOffsets.append(bodyOffset) + violatingByteOffsets.append(bodyRange.location) } if expectedBodyEndRegex.firstMatch(in: body, options: [], range: body.fullNSRange) == nil { - violatingByteOffsets.append(bodyOffset + bodyLength) + violatingByteOffsets.append(bodyRange.upperBound) } return violatingByteOffsets.map { byteOffset in diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsRule.swift index daf39127a..45429d19e 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineArgumentsRule.swift @@ -21,8 +21,9 @@ public struct MultilineArgumentsRule: ASTRule, OptInRule, ConfigurationProviderR guard kind == .call, case let arguments = dictionary.enclosedArguments, - arguments.count > 1 else { - return [] + arguments.count > 1 + else { + return [] } let wrappedArguments: [Argument] = arguments @@ -55,8 +56,9 @@ public struct MultilineArgumentsRule: ASTRule, OptInRule, ConfigurationProviderR file: SwiftLintFile) -> [Argument] { guard case let contents = file.stringView, let nameOffset = dictionary.nameOffset, - let (nameLine, _) = contents.lineAndCharacter(forByteOffset: nameOffset) else { - return [] + let (nameLine, _) = contents.lineAndCharacter(forByteOffset: nameOffset) + else { + return [] } var visitedLines = Set() @@ -114,8 +116,10 @@ public struct MultilineArgumentsRule: ASTRule, OptInRule, ConfigurationProviderR guard let offset = dictionary.offset, let length = dictionary.length, case let start = min(offset, offset + length - 1), - let text = file.stringView.substringWithByteRange(start: start, length: length) else { - return false + case let byteRange = ByteRange(location: start, length: length), + let text = file.stringView.substringWithByteRange(byteRange) + else { + return false } return !text.hasSuffix(")") @@ -125,38 +129,33 @@ public struct MultilineArgumentsRule: ASTRule, OptInRule, ConfigurationProviderR return { argument in let contents = file.stringView let closureMatcher = regex("^\\s*\\{") - guard let range = contents.byteRangeToNSRange(start: argument.bodyOffset, - length: argument.bodyLength), - case let matches = closureMatcher.matches(in: file.contents, - options: [], - range: range) else { + guard let range = contents.byteRangeToNSRange(argument.bodyRange) else { return false } + let matches = closureMatcher.matches(in: file.contents, options: [], range: range) return matches.count == 1 } } } private struct Argument { - let offset: Int + let offset: ByteCount let line: Int let index: Int - let bodyOffset: Int - let bodyLength: Int + let bodyRange: ByteRange init?(dictionary: SourceKittenDictionary, file: SwiftLintFile, index: Int) { guard let offset = dictionary.offset, let (line, _) = file.stringView.lineAndCharacter(forByteOffset: offset), - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength else { + let bodyRange = dictionary.bodyByteRange + else { return nil } self.offset = offset self.line = line self.index = index - self.bodyOffset = bodyOffset - self.bodyLength = bodyLength + self.bodyRange = bodyRange } } diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineFunctionChainsRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineFunctionChainsRule.swift index 01f5b571d..7ec947c75 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineFunctionChainsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineFunctionChainsRule.swift @@ -109,7 +109,7 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv dictionary: SourceKittenDictionary) -> [Int] { let ranges = callRanges(file: file, kind: kind, dictionary: dictionary) - let calls = ranges.compactMap { range -> (dotLine: Int, dotOffset: Int, range: NSRange)? in + let calls = ranges.compactMap { range -> (dotLine: Int, dotOffset: Int, range: ByteRange)? in guard let offset = callDotOffset(file: file, callRange: range), let line = file.stringView.lineAndCharacter(forCharacterOffset: offset)?.line else { @@ -134,9 +134,9 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv private static let whitespaceDotRegex = regex("\\s*\\.") - private func callDotOffset(file: SwiftLintFile, callRange: NSRange) -> Int? { + private func callDotOffset(file: SwiftLintFile, callRange: ByteRange) -> Int? { guard - let range = file.stringView.byteRangeToNSRange(start: callRange.location, length: callRange.length), + let range = file.stringView.byteRangeToNSRange(callRange), case let regex = type(of: self).whitespaceDotRegex, let match = regex.matches(in: file.contents, options: [], range: range).last?.range else { return nil @@ -146,9 +146,9 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv private static let newlineWhitespaceDotRegex = regex("\\n\\s*\\.") - private func callHasLeadingNewline(file: SwiftLintFile, callRange: NSRange) -> Bool { + private func callHasLeadingNewline(file: SwiftLintFile, callRange: ByteRange) -> Bool { guard - let range = file.stringView.byteRangeToNSRange(start: callRange.location, length: callRange.length), + let range = file.stringView.byteRangeToNSRange(callRange), case let regex = type(of: self).newlineWhitespaceDotRegex, regex.firstMatch(in: file.contents, options: [], range: range) != nil else { return false @@ -159,23 +159,25 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv private func callRanges(file: SwiftLintFile, kind: SwiftExpressionKind, dictionary: SourceKittenDictionary, - parentCallName: String? = nil) -> [NSRange] { + parentCallName: String? = nil) -> [ByteRange] { guard kind == .call, case let contents = file.stringView, let offset = dictionary.nameOffset, let length = dictionary.nameLength, - let name = contents.substringWithByteRange(start: offset, length: length) else { - return [] + case let nameByteRange = ByteRange(location: offset, length: length), + let name = contents.substringWithByteRange(nameByteRange) + else { + return [] } let subcalls = dictionary.subcalls if subcalls.isEmpty, let parentCallName = parentCallName, parentCallName.starts(with: name) { - return [NSRange(location: offset, length: length)] + return [ByteRange(location: offset, length: length)] } - return subcalls.flatMap { call -> [NSRange] in + return subcalls.flatMap { call -> [ByteRange] in // Bail out early if there's no subcall, since this means there's no chain. guard let range = subcallRange(file: file, call: call, parentName: name, parentNameOffset: offset) else { return [] @@ -188,7 +190,7 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv private func subcallRange(file: SwiftLintFile, call: SourceKittenDictionary, parentName: String, - parentNameOffset: Int) -> NSRange? { + parentNameOffset: ByteCount) -> ByteRange? { guard case let contents = file.stringView, let nameOffset = call.nameOffset, @@ -196,17 +198,19 @@ public struct MultilineFunctionChainsRule: ASTRule, OptInRule, ConfigurationProv let nameLength = call.nameLength, let bodyOffset = call.bodyOffset, let bodyLength = call.bodyLength, - let name = contents.substringWithByteRange(start: nameOffset, length: nameLength), - parentName.starts(with: name) else { - return nil + case let nameByteRange = ByteRange(location: nameOffset, length: nameLength), + let name = contents.substringWithByteRange(nameByteRange), + parentName.starts(with: name) + else { + return nil } let nameEndOffset = nameOffset + nameLength - let nameLengthDifference = parentName.utf8.count - nameLength + let nameLengthDifference = ByteCount(parentName.lengthOfBytes(using: .utf8)) - nameLength let offsetDifference = bodyOffset - nameEndOffset - return NSRange(location: nameEndOffset + offsetDifference + bodyLength, - length: nameLengthDifference - bodyLength - offsetDifference) + return ByteRange(location: nameEndOffset + offsetDifference + bodyLength, + length: nameLengthDifference - bodyLength - offsetDifference) } } diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineLiteralBracketsRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineLiteralBracketsRule.swift index c38b9196c..2292438cd 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineLiteralBracketsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineLiteralBracketsRule.swift @@ -102,14 +102,12 @@ public struct MultilineLiteralBracketsRule: ASTRule, OptInRule, ConfigurationPro dictionary: SourceKittenDictionary) -> [StyleViolation] { guard [.array, .dictionary].contains(kind), - let bodyOffset = dictionary.bodyOffset, - let bodyLength = dictionary.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: bodyOffset, length: bodyLength) + let bodyByteRange = dictionary.bodyByteRange, + let body = file.stringView.substringWithByteRange(bodyByteRange) else { return [] } - let body = file.contents.substring(from: range.location, length: range.length) let isMultiline = body.contains("\n") guard isMultiline else { return [] @@ -118,13 +116,13 @@ public struct MultilineLiteralBracketsRule: ASTRule, OptInRule, ConfigurationPro let expectedBodyBeginRegex = regex("\\A[ \\t]*\\n") let expectedBodyEndRegex = regex("\\n[ \\t]*\\z") - var violatingByteOffsets = [Int]() + var violatingByteOffsets = [ByteCount]() if expectedBodyBeginRegex.firstMatch(in: body, options: [], range: body.fullNSRange) == nil { - violatingByteOffsets.append(bodyOffset) + violatingByteOffsets.append(bodyByteRange.location) } if expectedBodyEndRegex.firstMatch(in: body, options: [], range: body.fullNSRange) == nil { - violatingByteOffsets.append(bodyOffset + bodyLength) + violatingByteOffsets.append(bodyByteRange.upperBound) } return violatingByteOffsets.map { byteOffset in diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineParametersBracketsRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineParametersBracketsRule.swift index 1ff6d1e74..625f39db0 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineParametersBracketsRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineParametersBracketsRule.swift @@ -100,7 +100,8 @@ public struct MultilineParametersBracketsRule: OptInRule, ConfigurationProviderR guard let nameOffset = substructure.nameOffset, let nameLength = substructure.nameLength, - let functionName = file.stringView.substringWithByteRange(start: nameOffset, length: nameLength) + case let nameByteRange = ByteRange(location: nameOffset, length: nameLength), + let functionName = file.stringView.substringWithByteRange(nameByteRange) else { return [] } @@ -130,14 +131,10 @@ public struct MultilineParametersBracketsRule: OptInRule, ConfigurationProviderR private func openingBracketViolation(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> StyleViolation? { guard - let firstParamByteOffset = parameters.first?.offset, - let firstParamByteLength = parameters.first?.length, - let firstParamRange = file.stringView.byteRangeToNSRange( - start: firstParamByteOffset, - length: firstParamByteLength - ) + let firstParamByteRange = parameters.first?.byteRange, + let firstParamRange = file.stringView.byteRangeToNSRange(firstParamByteRange) else { - return nil + return nil } let prefix = file.stringView.nsString.substring(to: firstParamRange.lowerBound) @@ -157,12 +154,8 @@ public struct MultilineParametersBracketsRule: OptInRule, ConfigurationProviderR private func closingBracketViolation(parameters: [SourceKittenDictionary], file: SwiftLintFile) -> StyleViolation? { guard - let lastParamByteOffset = parameters.last?.offset, - let lastParamByteLength = parameters.last?.length, - let lastParamRange = file.stringView.byteRangeToNSRange( - start: lastParamByteOffset, - length: lastParamByteLength - ) + let lastParamByteRange = parameters.last?.byteRange, + let lastParamRange = file.stringView.byteRangeToNSRange(lastParamByteRange) else { return nil } diff --git a/Source/SwiftLintFramework/Rules/Style/MultilineParametersRule.swift b/Source/SwiftLintFramework/Rules/Style/MultilineParametersRule.swift index de206c6ac..68c785e02 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultilineParametersRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultilineParametersRule.swift @@ -3,8 +3,6 @@ import SourceKittenFramework public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule { public var configuration = SeverityConfiguration(.warning) - private typealias ParameterRange = (offset: Int, length: Int) - public init() {} public static let description = RuleDescription( @@ -21,22 +19,17 @@ public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProvider dictionary: SourceKittenDictionary) -> [StyleViolation] { guard SwiftDeclarationKind.functionKinds.contains(kind), - let offset = dictionary.nameOffset, - let length = dictionary.nameLength - else { - return [] + let nameRange = dictionary.nameByteRange + else { + return [] } - let parameterRanges = dictionary.substructure.compactMap { subStructure -> ParameterRange? in - guard - let offset = subStructure.offset, - let length = subStructure.length, - subStructure.declarationKind == .varParameter - else { - return nil + let parameterRanges = dictionary.substructure.compactMap { subStructure -> ByteRange? in + guard subStructure.declarationKind == .varParameter else { + return nil } - return (offset, length) + return subStructure.byteRange } var numberOfParameters = 0 @@ -44,11 +37,11 @@ public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProvider for range in parameterRanges { guard - let (line, _) = file.stringView.lineAndCharacter(forByteOffset: range.offset), - offset..<(offset + length) ~= range.offset, - isRange(range, withinRanges: parameterRanges) - else { - continue + let line = file.stringView.lineAndCharacter(forByteOffset: range.location)?.line, + nameRange.contains(range.location), + range.intersects(parameterRanges) + else { + continue } linesWithParameters.insert(line) @@ -58,18 +51,12 @@ public struct MultilineParametersRule: ASTRule, OptInRule, ConfigurationProvider guard linesWithParameters.count > 1, numberOfParameters != linesWithParameters.count - else { - return [] + else { + return [] } return [StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, - location: Location(file: file, byteOffset: offset))] - } - - // MARK: - Private - - private func isRange(_ range: ParameterRange, withinRanges ranges: [ParameterRange]) -> Bool { - return !ranges.contains { $0 != range && ($0.offset..<($0.offset + $0.length)).contains(range.offset) } + location: Location(file: file, byteOffset: nameRange.location))] } } diff --git a/Source/SwiftLintFramework/Rules/Style/MultipleClosuresWithTrailingClosureRule.swift b/Source/SwiftLintFramework/Rules/Style/MultipleClosuresWithTrailingClosureRule.swift index 211c3a5c9..6f224206c 100644 --- a/Source/SwiftLintFramework/Rules/Style/MultipleClosuresWithTrailingClosureRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/MultipleClosuresWithTrailingClosureRule.swift @@ -67,12 +67,12 @@ private extension Array where Element == SourceKittenDictionary { func filterClosures(file: SwiftLintFile) -> [SourceKittenDictionary] { if SwiftVersion.current < .fourDotTwo { return filter { argument in - guard let offset = argument.bodyOffset, - let length = argument.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length), + guard let bodyByteRange = argument.bodyByteRange, + let range = file.stringView.byteRangeToNSRange(bodyByteRange), let match = regex("^\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range, - match.location == range.location else { - return false + match.location == range.location + else { + return false } return true diff --git a/Source/SwiftLintFramework/Rules/Style/NoSpaceInMethodCallRule.swift b/Source/SwiftLintFramework/Rules/Style/NoSpaceInMethodCallRule.swift index 7a4d26f22..879d32067 100644 --- a/Source/SwiftLintFramework/Rules/Style/NoSpaceInMethodCallRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/NoSpaceInMethodCallRule.swift @@ -69,9 +69,10 @@ public struct NoSpaceInMethodCallRule: SubstitutionCorrectableASTRule, Configura case let nameEndPosition = nameOffset + nameLength, bodyOffset != nameEndPosition + 1, case let contents = file.stringView, - let range = contents.byteRangeToNSRange(start: nameEndPosition, - length: bodyOffset - nameEndPosition - 1) else { - return [] + case let byteRange = ByteRange(location: nameEndPosition, length: bodyOffset - nameEndPosition - 1), + let range = contents.byteRangeToNSRange(byteRange) + else { + return [] } // Don't trigger if it's a single parameter trailing closure without parens diff --git a/Source/SwiftLintFramework/Rules/Style/NumberSeparatorRule.swift b/Source/SwiftLintFramework/Rules/Style/NumberSeparatorRule.swift index 2627d00c7..d20858f55 100644 --- a/Source/SwiftLintFramework/Rules/Style/NumberSeparatorRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/NumberSeparatorRule.swift @@ -60,9 +60,9 @@ public struct NumberSeparatorRule: OptInRule, CorrectableRule, ConfigurationProv guard let integerSubstring = components.first, case let (valid, expected) = isValid(number: integerSubstring, isFraction: false), !valid || !validFraction, - let range = file.stringView.byteRangeToNSRange(start: token.offset, - length: token.length) else { - return nil + let range = file.stringView.byteRangeToNSRange(token.range) + else { + return nil } var corrected = "" diff --git a/Source/SwiftLintFramework/Rules/Style/OptionalEnumCaseMatchingRule.swift b/Source/SwiftLintFramework/Rules/Style/OptionalEnumCaseMatchingRule.swift index 9f1c5ad10..7fe906a26 100644 --- a/Source/SwiftLintFramework/Rules/Style/OptionalEnumCaseMatchingRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/OptionalEnumCaseMatchingRule.swift @@ -165,26 +165,26 @@ public struct OptionalEnumCaseMatchingRule: SubstitutionCorrectableASTRule, Conf return dictionary.elements .filter { $0.kind == "source.lang.swift.structure.elem.pattern" } .flatMap { dictionary -> [NSRange] in - guard let offset = dictionary.offset, let length = dictionary.length else { + guard let byteRange = dictionary.byteRange else { return [] } - let pattern = contents.substringWithByteRange(start: offset, length: length) + let pattern = contents.substringWithByteRange(byteRange) let tupleCommaByteOffsets = pattern?.tupleCommaByteOffsets ?? [] - let tokensToCheck = (tupleCommaByteOffsets + [length]).compactMap { length in + let tokensToCheck = (tupleCommaByteOffsets + [byteRange.length]).compactMap { length in return file.syntaxMap - .tokens(inByteRange: NSRange(location: offset, length: length)) + .tokens(inByteRange: ByteRange(location: byteRange.location, length: length)) .prefix { $0.kind != .keyword || file.isTokenUnderscoreKeyword($0) } .last } return tokensToCheck.compactMap { tokenToCheck in - let questionMarkByteOffset = tokenToCheck.length + tokenToCheck.offset - guard contents.substringWithByteRange(start: questionMarkByteOffset, length: 1) == "?" else { + let questionMarkByteRange = ByteRange(location: tokenToCheck.range.upperBound, length: 1) + guard contents.substringWithByteRange(questionMarkByteRange) == "?" else { return nil } - return contents.byteRangeToNSRange(start: questionMarkByteOffset, length: 1) + return contents.byteRangeToNSRange(questionMarkByteRange) } } } @@ -203,7 +203,7 @@ private extension String { return first == "(" && last == ")" && contains(",") } - var tupleCommaByteOffsets: [Int] { + var tupleCommaByteOffsets: [ByteCount] { guard isTuple else { return [] } diff --git a/Source/SwiftLintFramework/Rules/Style/RedundantDiscardableLetRule.swift b/Source/SwiftLintFramework/Rules/Style/RedundantDiscardableLetRule.swift index 27b15ae7d..972213b24 100644 --- a/Source/SwiftLintFramework/Rules/Style/RedundantDiscardableLetRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/RedundantDiscardableLetRule.swift @@ -55,21 +55,17 @@ public struct RedundantDiscardableLetRule: SubstitutionCorrectableRule, Configur } } - private func isInBooleanCondition(byteOffset: Int, dictionary: SourceKittenDictionary) -> Bool { - guard let offset = dictionary.offset, - let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }), - NSLocationInRange(byteOffset, byteRange) else { - return false + private func isInBooleanCondition(byteOffset: ByteCount, dictionary: SourceKittenDictionary) -> Bool { + guard let byteRange = dictionary.byteRange, byteRange.contains(byteOffset) else { + return false } let kinds: Set = [.if, .guard, .while] if let kind = dictionary.statementKind, kinds.contains(kind) { let conditionKind = "source.lang.swift.structure.elem.condition_expr" for element in dictionary.elements where element.kind == conditionKind { - guard let elementOffset = element.offset, - let elementRange = element.length.map({ NSRange(location: elementOffset, length: $0) }), - NSLocationInRange(byteOffset, elementRange) else { - continue + guard let elementRange = element.byteRange, elementRange.contains(byteOffset) else { + continue } return true diff --git a/Source/SwiftLintFramework/Rules/Style/ReturnArrowWhitespaceRule.swift b/Source/SwiftLintFramework/Rules/Style/ReturnArrowWhitespaceRule.swift index e013a3cd5..e1f1c9c00 100644 --- a/Source/SwiftLintFramework/Rules/Style/ReturnArrowWhitespaceRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ReturnArrowWhitespaceRule.swift @@ -86,7 +86,7 @@ public struct ReturnArrowWhitespaceRule: CorrectableRule, ConfigurationProviderR // MARK: - Private private let pattern: String = { - //just horizontal spacing so that "func abc()->\n" can pass validation + // Just horizontal spacing so that "func abc()->\n" can pass validation let space = "[ \\f\\r\\t]" // Either 0 space characters or 2+ diff --git a/Source/SwiftLintFramework/Rules/Style/ShorthandOperatorRule.swift b/Source/SwiftLintFramework/Rules/Style/ShorthandOperatorRule.swift index 57cdc584c..f1510dcab 100644 --- a/Source/SwiftLintFramework/Rules/Style/ShorthandOperatorRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/ShorthandOperatorRule.swift @@ -70,7 +70,7 @@ public struct ShorthandOperatorRule: ConfigurationProviderRule, AutomaticTestabl return matches.compactMap { match -> StyleViolation? in // byteRanges will have the ranges of captured groups - let byteRanges: [NSRange?] = (1.. String { @@ -59,7 +58,7 @@ public struct SwitchCaseOnNewlineRule: ASTRule, ConfigurationProviderRule, OptIn let lastElementLength = lastElement.length, case let start = lastElementOffset + lastElementLength, case let rangeLength = offset + length - start, - case let byteRange = NSRange(location: start, length: rangeLength), + case let byteRange = ByteRange(location: start, length: rangeLength), let firstToken = firstNonCommentToken(inByteRange: byteRange, file: file), let (tokenLine, _) = file.stringView.lineAndCharacter(forByteOffset: firstToken.offset), let (caseEndLine, _) = file.stringView.lineAndCharacter(forByteOffset: start), @@ -74,7 +73,7 @@ public struct SwitchCaseOnNewlineRule: ASTRule, ConfigurationProviderRule, OptIn ] } - private func firstNonCommentToken(inByteRange byteRange: NSRange, file: SwiftLintFile) -> SwiftLintSyntaxToken? { + private func firstNonCommentToken(inByteRange byteRange: ByteRange, file: SwiftLintFile) -> SwiftLintSyntaxToken? { return file.syntaxMap.tokens(inByteRange: byteRange).first { token -> Bool in guard let kind = token.kind else { return false diff --git a/Source/SwiftLintFramework/Rules/Style/TrailingClosureRule.swift b/Source/SwiftLintFramework/Rules/Style/TrailingClosureRule.swift index 08098a0c2..72a471bb8 100644 --- a/Source/SwiftLintFramework/Rules/Style/TrailingClosureRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/TrailingClosureRule.swift @@ -39,8 +39,8 @@ public struct TrailingClosureRule: OptInRule, ConfigurationProviderRule { } } - private func violationOffsets(for dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [Int] { - var results = [Int]() + private func violationOffsets(for dictionary: SourceKittenDictionary, file: SwiftLintFile) -> [ByteCount] { + var results = [ByteCount]() if dictionary.expressionKind == .call, shouldBeTrailingClosure(dictionary: dictionary, file: file), @@ -50,7 +50,7 @@ public struct TrailingClosureRule: OptInRule, ConfigurationProviderRule { if let kind = dictionary.statementKind, kind != .brace { // trailing closures are not allowed in `if`, `guard`, etc - results += dictionary.substructure.flatMap { subDict -> [Int] in + results += dictionary.substructure.flatMap { subDict -> [ByteCount] in guard subDict.statementKind == .brace else { return [] } @@ -90,7 +90,8 @@ public struct TrailingClosureRule: OptInRule, ConfigurationProviderRule { let nameLength = dictionary.nameLength, case let start = nameOffset + nameLength, case let length = totalLength + offset - 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 = regex("\\s*\\(\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range, match.location == range.location { return shouldTrigger() @@ -102,12 +103,12 @@ public struct TrailingClosureRule: OptInRule, ConfigurationProviderRule { private func filterClosureArguments(_ arguments: [SourceKittenDictionary], file: SwiftLintFile) -> [SourceKittenDictionary] { return arguments.filter { argument in - guard let offset = argument.bodyOffset, - let length = argument.bodyLength, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length), + guard let bodyByteRange = argument.bodyByteRange, + let range = file.stringView.byteRangeToNSRange(bodyByteRange), let match = regex("\\s*\\{").firstMatch(in: file.contents, options: [], range: range)?.range, - match.location == range.location else { - return false + match.location == range.location + else { + return false } return true @@ -115,21 +116,20 @@ public struct TrailingClosureRule: OptInRule, ConfigurationProviderRule { } private func isAlreadyTrailingClosure(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { - guard let offset = dictionary.offset, - let length = dictionary.length, - let text = file.stringView.substringWithByteRange(start: offset, length: length) else { - return false + guard let byteRange = dictionary.byteRange, + let text = file.stringView.substringWithByteRange(byteRange) + else { + return false } return !text.hasSuffix(")") } - private func isAnonymousClosureCall(dictionary: SourceKittenDictionary, - file: SwiftLintFile) -> Bool { - guard let offset = dictionary.offset, - let length = dictionary.length, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else { - return false + private func isAnonymousClosureCall(dictionary: SourceKittenDictionary, file: SwiftLintFile) -> Bool { + guard let byteRange = dictionary.byteRange, + let range = file.stringView.byteRangeToNSRange(byteRange) + else { + return false } let pattern = regex("\\)\\s*\\)\\z") diff --git a/Source/SwiftLintFramework/Rules/Style/TrailingCommaRule.swift b/Source/SwiftLintFramework/Rules/Style/TrailingCommaRule.swift index 1d4d166c2..915fa7d68 100644 --- a/Source/SwiftLintFramework/Rules/Style/TrailingCommaRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/TrailingCommaRule.swift @@ -6,7 +6,7 @@ private enum TrailingCommaReason: String { case extraTrailingCommaReason = "Collection literals should not have trailing commas." } -private typealias CommaRuleViolation = (index: Int, reason: TrailingCommaReason) +private typealias CommaRuleViolation = (index: ByteCount, reason: TrailingCommaReason) public struct TrailingCommaRule: SubstitutionCorrectableASTRule, ConfigurationProviderRule { public var configuration = TrailingCommaConfiguration() @@ -67,9 +67,11 @@ public struct TrailingCommaRule: SubstitutionCorrectableASTRule, ConfigurationPr public func violationRanges(in file: SwiftLintFile, kind: SwiftExpressionKind, dictionary: SourceKittenDictionary) -> [NSRange] { guard let (offset, reason) = violationIndexAndReason(in: file, kind: kind, dictionary: dictionary), - case let length = reason == .extraTrailingCommaReason ? 1 : 0, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else { - return [] + case let length: ByteCount = reason == .extraTrailingCommaReason ? 1 : 0, + case let byteRange = ByteRange(location: offset, length: length), + let range = file.stringView.byteRangeToNSRange(byteRange) + else { + return [] } return [range] @@ -89,14 +91,7 @@ public struct TrailingCommaRule: SubstitutionCorrectableASTRule, ConfigurationPr return nil } - let endPositions = dictionary.elements.compactMap { dictionary -> Int? in - guard let offset = dictionary.offset, - let length = dictionary.length else { - return nil - } - - return offset + length - } + let endPositions = dictionary.elements.compactMap { $0.byteRange?.upperBound } guard let lastPosition = endPositions.max(), bodyLength + bodyOffset >= lastPosition else { return nil @@ -111,11 +106,12 @@ public struct TrailingCommaRule: SubstitutionCorrectableASTRule, ConfigurationPr } let length = bodyLength + bodyOffset - lastPosition - let contentsAfterLastElement = contents.substringWithByteRange(start: lastPosition, length: length) ?? "" + let byteRangeAfterLastElement = ByteRange(location: lastPosition, length: length) + let contentsAfterLastElement = contents.substringWithByteRange(byteRangeAfterLastElement) ?? "" // if a trailing comma is not present - guard let commaIndex = trailingCommaIndex(contents: contentsAfterLastElement, file: file, - offset: lastPosition) else { + guard let commaIndex = trailingCommaIndex(contents: contentsAfterLastElement, file: file, offset: lastPosition) + else { guard configuration.mandatoryComma else { return nil } @@ -132,49 +128,48 @@ public struct TrailingCommaRule: SubstitutionCorrectableASTRule, ConfigurationPr return (violationOffset, .extraTrailingCommaReason) } - private func violations(file: SwiftLintFile, byteOffset: Int, reason: String) -> [StyleViolation] { + private func violations(file: SwiftLintFile, byteOffset: ByteCount, reason: String) -> [StyleViolation] { return [ StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severityConfiguration.severity, location: Location(file: file, byteOffset: byteOffset), - reason: reason - ) + reason: reason) ] } - private func trailingCommaIndex(contents: String, file: SwiftLintFile, offset: Int) -> Int? { - let nsstring = contents.bridge() - let range = contents.fullNSRange - let ranges = TrailingCommaRule.commaRegex.matches(in: contents, options: [], range: range).map { $0.range } - + private func trailingCommaIndex(contents: String, file: SwiftLintFile, offset: ByteCount) -> ByteCount? { // skip commas in comments - return ranges.last { - let range = NSRange(location: $0.location + offset, length: $0.length) - let kinds = file.syntaxMap.kinds(inByteRange: range) - return SyntaxKind.commentKinds.isDisjoint(with: kinds) - }.flatMap { - nsstring.NSRangeToByteRange(start: $0.location, length: $0.length) - }?.location + return TrailingCommaRule.commaRegex + .matches(in: contents, options: [], range: contents.fullNSRange) + .map { $0.range } + .last { nsRange in + let offsetCharacter = file.stringView.location(fromByteOffset: offset) + let offsetNSRange = NSRange(location: nsRange.location + offsetCharacter, length: nsRange.length) + let byteRange = file.stringView.NSRangeToByteRange(offsetNSRange)! + let kinds = file.syntaxMap.kinds(inByteRange: byteRange) + return SyntaxKind.commentKinds.isDisjoint(with: kinds) + } + .flatMap(contents.NSRangeToByteRange)? + .location } } -private extension NSString { - func NSRangeToByteRange(start: Int, length: Int) -> NSRange? { - let string = bridge() - let utf16View = string.utf16 - let utf8View = string.utf8 +private extension String { + func NSRangeToByteRange(_ nsRange: NSRange) -> ByteRange? { + let utf16View = utf16 + let utf8View = utf8 - let startUTF16Index = utf16View.index(utf16View.startIndex, offsetBy: start) - let endUTF16Index = utf16View.index(startUTF16Index, offsetBy: length) + let startUTF16Index = utf16View.index(utf16View.startIndex, offsetBy: nsRange.location) + let endUTF16Index = utf16View.index(startUTF16Index, offsetBy: nsRange.length) guard let startUTF8Index = startUTF16Index.samePosition(in: utf8View), - let endUTF8Index = endUTF16Index.samePosition(in: utf8View) else { - return nil + let endUTF8Index = endUTF16Index.samePosition(in: utf8View) + else { + return nil } let byteOffset = utf8View.distance(from: utf8View.startIndex, to: startUTF8Index) - let length = utf8View.distance(from: startUTF8Index, to: endUTF8Index) - return NSRange(location: byteOffset, length: length) + return ByteRange(location: ByteCount(byteOffset), length: ByteCount(length)) } } diff --git a/Source/SwiftLintFramework/Rules/Style/TypeContentsOrderRule.swift b/Source/SwiftLintFramework/Rules/Style/TypeContentsOrderRule.swift index f89fd0905..d47032f69 100644 --- a/Source/SwiftLintFramework/Rules/Style/TypeContentsOrderRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/TypeContentsOrderRule.swift @@ -1,7 +1,7 @@ import SourceKittenFramework public struct TypeContentsOrderRule: ConfigurationProviderRule, OptInRule { - private typealias TypeContentOffset = (typeContent: TypeContent, offset: Int) + private typealias TypeContentOffset = (typeContent: TypeContent, offset: ByteCount) public var configuration = TypeContentsOrderConfiguration() @@ -31,7 +31,7 @@ public struct TypeContentsOrderRule: ConfigurationProviderRule, OptInRule { let typeContentOffsets = self.typeContentOffsets(in: substructure) let orderedTypeContentOffsets = typeContentOffsets.sorted { lhs, rhs in lhs.offset < rhs.offset } - var violations = [StyleViolation]() + var violations = [StyleViolation]() var lastMatchingIndex = -1 for expectedTypesContents in configuration.order { diff --git a/Source/SwiftLintFramework/Rules/Style/UnusedOptionalBindingRule.swift b/Source/SwiftLintFramework/Rules/Style/UnusedOptionalBindingRule.swift index 8fe2967c0..454c1d2bf 100644 --- a/Source/SwiftLintFramework/Rules/Style/UnusedOptionalBindingRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/UnusedOptionalBindingRule.swift @@ -53,10 +53,10 @@ public struct UnusedOptionalBindingRule: ASTRule, ConfigurationProviderRule { let elements = dictionary.elements.filter { $0.kind == conditionKind } return elements.flatMap { element -> [StyleViolation] in - guard let offset = element.offset, - let length = element.length, - let range = file.stringView.byteRangeToNSRange(start: offset, length: length) else { - return [] + guard let byteRange = element.byteRange, + let range = file.stringView.byteRangeToNSRange(byteRange) + else { + return [] } return violations(in: range, of: file, with: kind).map { diff --git a/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentOnCallRule.swift b/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentOnCallRule.swift index f35ca104c..63940c0fe 100644 --- a/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentOnCallRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentOnCallRule.swift @@ -88,7 +88,7 @@ public struct VerticalParameterAlignmentOnCallRule: ASTRule, ConfigurationProvid var previousArgumentWasMultiline = false let lastIndex = arguments.count - 1 - let violatingOffsets: [Int] = arguments.enumerated().compactMap { idx, argument in + let violatingOffsets: [ByteCount] = arguments.enumerated().compactMap { idx, argument in defer { previousArgumentWasMultiline = isMultiline(argument: argument, file: file) } @@ -131,8 +131,9 @@ public struct VerticalParameterAlignmentOnCallRule: ASTRule, ConfigurationProvid let length = argument.bodyLength, case let contents = file.stringView, let (startLine, _) = contents.lineAndCharacter(forByteOffset: offset), - let (endLine, _) = contents.lineAndCharacter(forByteOffset: offset + length) else { - return false + let (endLine, _) = contents.lineAndCharacter(forByteOffset: offset + length) + else { + return false } return endLine > startLine @@ -142,8 +143,10 @@ public struct VerticalParameterAlignmentOnCallRule: ASTRule, ConfigurationProvid guard let offset = dictionary.offset, let length = dictionary.length, case let start = min(offset, offset + length - 1), - let text = file.stringView.substringWithByteRange(start: start, length: length) else { - return false + case let byteRange = ByteRange(location: start, length: length), + let text = file.stringView.substringWithByteRange(byteRange) + else { + return false } return !text.hasSuffix(")") diff --git a/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentRule.swift b/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentRule.swift index 72d6d9a59..00423687c 100644 --- a/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentRule.swift +++ b/Source/SwiftLintFramework/Rules/Style/VerticalParameterAlignmentRule.swift @@ -19,13 +19,14 @@ public struct VerticalParameterAlignmentRule: ASTRule, ConfigurationProviderRule guard SwiftDeclarationKind.functionKinds.contains(kind), let startOffset = dictionary.nameOffset, let length = dictionary.nameLength, - case let endOffset = startOffset + length else { + case let endOffset = startOffset + length + else { return [] } let params = dictionary.substructure.filter { subDict in return subDict.declarationKind == .varParameter && - (subDict.offset ?? .max) < endOffset + (subDict.offset ?? ByteCount(Int.max)) < endOffset } guard params.count > 1 else { @@ -35,8 +36,9 @@ public struct VerticalParameterAlignmentRule: ASTRule, ConfigurationProviderRule let contents = file.stringView let calculateLocation = { (dict: SourceKittenDictionary) -> Location? in guard let byteOffset = dict.offset, - let lineAndChar = contents.lineAndCharacter(forByteOffset: byteOffset) else { - return nil + let lineAndChar = contents.lineAndCharacter(forByteOffset: byteOffset) + else { + return nil } return Location(file: file.path, line: lineAndChar.line, character: lineAndChar.character) diff --git a/SwiftLintFramework.podspec b/SwiftLintFramework.podspec index 313af2a8b..ea457c6ed 100644 --- a/SwiftLintFramework.podspec +++ b/SwiftLintFramework.podspec @@ -10,6 +10,6 @@ Pod::Spec.new do |s| s.source_files = 'Source/SwiftLintFramework/**/*.swift' s.swift_versions = ['5.0', '5.1'] s.pod_target_xcconfig = { 'APPLICATION_EXTENSION_API_ONLY' => 'YES' } - s.dependency 'SourceKittenFramework', '~> 0.28.0' + s.dependency 'SourceKittenFramework', '~> 0.29.0' s.dependency 'Yams', '~> 2.0' end