Make some SwiftLintFramework declarations public (#4945)

* Make some SwiftLintFramework declarations public

If built-in rules depend on them.

This will allow separating the rules from the core infrastructure in
separate modules in an upcoming PR.

It also helps formalize the API contract for what should be accessible
to rule implementations.

* Exclude extensions from jazzy
This commit is contained in:
JP Simard 2023-04-26 16:37:25 -04:00 committed by GitHub
parent a7e3909c6a
commit 3a2bb15ca3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 204 additions and 199 deletions

View File

@ -13,6 +13,8 @@ documentation: rule_docs/*.md
hide_unlisted_documentation: true
custom_categories_unlisted_prefix: ''
exclude:
# TODO: Document extensions
- Source/SwiftLintFramework/Extensions/*.swift
- Source/SwiftLintFramework/Rules/**/*.swift
custom_categories:
- name: Rules

View File

@ -1,6 +1,6 @@
import Dispatch
extension Array where Element: Equatable {
public extension Array where Element: Equatable {
/// The elements in this array, discarding duplicates after the first one.
/// Order-preserving.
var unique: [Element] {
@ -12,7 +12,7 @@ extension Array where Element: Equatable {
}
}
extension Array where Element: Hashable {
public extension Array where Element: Hashable {
/// Produces an array containing the passed `obj` value.
/// If `obj` is an array already, return it.
/// If `obj` is a set, copy its elements to a new array.
@ -33,7 +33,7 @@ extension Array where Element: Hashable {
}
}
extension Array {
public extension Array {
/// Produces an array containing the passed `obj` value.
/// If `obj` is an array already, return it.
/// If `obj` is a value of type `Element`, return a single-item array containing it.
@ -67,8 +67,7 @@ extension Array {
///
/// - returns: The elements failing the `belongsInSecondPartition` test, followed by the elements passing the
/// `belongsInSecondPartition` test.
@_spi(TestHelper)
public func partitioned(by belongsInSecondPartition: (Element) throws -> Bool) rethrows ->
func partitioned(by belongsInSecondPartition: (Element) throws -> Bool) rethrows ->
(first: ArraySlice<Element>, second: ArraySlice<Element>) {
var copy = self
let pivot = try copy.partition(by: belongsInSecondPartition)
@ -80,8 +79,7 @@ extension Array {
/// - parameter transform: The transformation to apply to each element.
///
/// - returns: The result of applying `transform` on every element and flattening the results.
@_spi(TestHelper)
public func parallelFlatMap<T>(transform: (Element) -> [T]) -> [T] {
func parallelFlatMap<T>(transform: (Element) -> [T]) -> [T] {
return parallelMap(transform: transform).flatMap { $0 }
}
@ -110,10 +108,9 @@ extension Array {
}
}
extension Collection {
public extension Collection {
/// Whether this collection has one or more element.
@_spi(TestHelper)
public var isNotEmpty: Bool {
var isNotEmpty: Bool {
return !isEmpty
}

View File

@ -1,7 +1,7 @@
import SourceKittenFramework
import SwiftSyntax
extension ByteCount {
public extension ByteCount {
/// Converts a SwiftSyntax `AbsolutePosition` to a SourceKitten `ByteCount`.
///
/// - parameter position: The SwiftSyntax position to convert.

View File

@ -21,7 +21,7 @@ public struct SourceKittenDictionary {
/// Creates a SourceKitten dictionary given a `Dictionary<String, SourceKitRepresentable>` input.
///
/// - parameter value: The input dictionary/
init(_ value: [String: SourceKitRepresentable]) {
public init(_ value: [String: SourceKitRepresentable]) {
self.value = value
let substructure = value["key.substructure"] as? [SourceKitRepresentable] ?? []
@ -37,123 +37,123 @@ public struct SourceKittenDictionary {
}
/// Body length
var bodyLength: ByteCount? {
public var bodyLength: ByteCount? {
return (value["key.bodylength"] as? Int64).map(ByteCount.init)
}
/// Body offset.
var bodyOffset: ByteCount? {
public var bodyOffset: ByteCount? {
return (value["key.bodyoffset"] as? Int64).map(ByteCount.init)
}
/// Body byte range.
var bodyByteRange: ByteRange? {
public var bodyByteRange: ByteRange? {
guard let offset = bodyOffset, let length = bodyLength else { return nil }
return ByteRange(location: offset, length: length)
}
/// Kind.
var kind: String? {
public var kind: String? {
return value["key.kind"] as? String
}
/// Length.
var length: ByteCount? {
public var length: ByteCount? {
return (value["key.length"] as? Int64).map(ByteCount.init)
}
/// Name.
var name: String? {
public var name: String? {
return value["key.name"] as? String
}
/// Name length.
var nameLength: ByteCount? {
public var nameLength: ByteCount? {
return (value["key.namelength"] as? Int64).map(ByteCount.init)
}
/// Name offset.
var nameOffset: ByteCount? {
public var nameOffset: ByteCount? {
return (value["key.nameoffset"] as? Int64).map(ByteCount.init)
}
/// Byte range of name.
var nameByteRange: ByteRange? {
public var nameByteRange: ByteRange? {
guard let offset = nameOffset, let length = nameLength else { return nil }
return ByteRange(location: offset, length: length)
}
/// Offset.
var offset: ByteCount? {
public var offset: ByteCount? {
return (value["key.offset"] as? Int64).map(ByteCount.init)
}
/// Returns byte range starting from `offset` with `length` bytes
var byteRange: ByteRange? {
public var byteRange: ByteRange? {
guard let offset, let length else { return nil }
return ByteRange(location: offset, length: length)
}
/// Setter accessibility.
var setterAccessibility: String? {
public var setterAccessibility: String? {
return value["key.setter_accessibility"] as? String
}
/// Type name.
var typeName: String? {
public var typeName: String? {
return value["key.typename"] as? String
}
/// Documentation length.
var docLength: ByteCount? {
public var docLength: ByteCount? {
return (value["key.doclength"] as? Int64).flatMap(ByteCount.init)
}
/// The attribute for this dictionary, as returned by SourceKit.
var attribute: String? {
public var attribute: String? {
return value["key.attribute"] as? String
}
/// Module name in `@import` expressions.
var moduleName: String? {
public var moduleName: String? {
return value["key.modulename"] as? String
}
/// The line number for this declaration.
var line: Int64? {
public var line: Int64? {
return value["key.line"] as? Int64
}
/// The column number for this declaration.
var column: Int64? {
public var column: Int64? {
return value["key.column"] as? Int64
}
/// The `SwiftDeclarationAttributeKind` values associated with this dictionary.
var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
public var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
return swiftAttributes.compactMap { $0.attribute }
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
}
/// The fully preserved SourceKitten dictionaries for all the attributes associated with this dictionary.
var swiftAttributes: [SourceKittenDictionary] {
public var swiftAttributes: [SourceKittenDictionary] {
let array = value["key.attributes"] as? [SourceKitRepresentable] ?? []
return array.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(Self.init)
}
var elements: [SourceKittenDictionary] {
public var elements: [SourceKittenDictionary] {
let elements = value["key.elements"] as? [SourceKitRepresentable] ?? []
return elements.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(Self.init)
}
var entities: [SourceKittenDictionary] {
public var entities: [SourceKittenDictionary] {
let entities = value["key.entities"] as? [SourceKitRepresentable] ?? []
return entities.compactMap { $0 as? [String: SourceKitRepresentable] }
.map(Self.init)
}
var enclosedVarParameters: [SourceKittenDictionary] {
public var enclosedVarParameters: [SourceKittenDictionary] {
return substructure.flatMap { subDict -> [SourceKittenDictionary] in
if subDict.declarationKind == .varParameter {
return [subDict]
@ -166,7 +166,7 @@ public struct SourceKittenDictionary {
}
}
var enclosedArguments: [SourceKittenDictionary] {
public var enclosedArguments: [SourceKittenDictionary] {
return substructure.flatMap { subDict -> [SourceKittenDictionary] in
guard subDict.expressionKind == .argument else {
return []
@ -176,7 +176,7 @@ public struct SourceKittenDictionary {
}
}
var inheritedTypes: [String] {
public var inheritedTypes: [String] {
let array = value["key.inheritedtypes"] as? [SourceKitRepresentable] ?? []
return array.compactMap { ($0 as? [String: String]).flatMap { $0["key.name"] } }
}
@ -189,7 +189,7 @@ extension SourceKittenDictionary {
/// - parameter traverseBlock: block that will be called for each substructure in the dictionary.
///
/// - returns: The list of substructure dictionaries with updated values from the traverse block.
func traverseDepthFirst<T>(traverseBlock: (SourceKittenDictionary) -> [T]?) -> [T] {
public func traverseDepthFirst<T>(traverseBlock: (SourceKittenDictionary) -> [T]?) -> [T] {
var result: [T] = []
traverseDepthFirst(collectingValuesInto: &result, traverseBlock: traverseBlock)
return result
@ -212,7 +212,7 @@ extension SourceKittenDictionary {
/// - parameter traverseBlock: block that will be called for each entity in the dictionary.
///
/// - returns: The list of entity dictionaries with updated values from the traverse block.
func traverseEntitiesDepthFirst<T>(traverseBlock: (SourceKittenDictionary) -> T?) -> [T] {
public func traverseEntitiesDepthFirst<T>(traverseBlock: (SourceKittenDictionary) -> T?) -> [T] {
var result: [T] = []
traverseEntitiesDepthFirst(collectingValuesInto: &result, traverseBlock: traverseBlock)
return result
@ -230,7 +230,7 @@ extension SourceKittenDictionary {
}
}
extension Dictionary where Key == Example {
public extension Dictionary where Key == Example {
/// Returns a dictionary with SwiftLint violation markers () removed from keys.
///
/// - returns: A new `Dictionary`.

View File

@ -14,8 +14,8 @@ private struct RegexCacheKey: Hashable {
}
}
extension NSRegularExpression {
internal static func cached(pattern: String, options: Options? = nil) throws -> NSRegularExpression {
public extension NSRegularExpression {
static func cached(pattern: String, options: Options? = nil) throws -> NSRegularExpression {
let options = options ?? [.anchorsMatchLines, .dotMatchesLineSeparators]
let key = RegexCacheKey(pattern: pattern, options: options)
regexCacheLock.lock()
@ -29,19 +29,19 @@ extension NSRegularExpression {
return result
}
internal func matches(in stringView: StringView,
options: NSRegularExpression.MatchingOptions = []) -> [NSTextCheckingResult] {
func matches(in stringView: StringView,
options: NSRegularExpression.MatchingOptions = []) -> [NSTextCheckingResult] {
return matches(in: stringView.string, options: options, range: stringView.range)
}
internal func matches(in stringView: StringView,
options: NSRegularExpression.MatchingOptions = [],
range: NSRange) -> [NSTextCheckingResult] {
func matches(in stringView: StringView,
options: NSRegularExpression.MatchingOptions = [],
range: NSRange) -> [NSTextCheckingResult] {
return matches(in: stringView.string, options: options, range: range)
}
internal func matches(in file: SwiftLintFile,
options: NSRegularExpression.MatchingOptions = []) -> [NSTextCheckingResult] {
func matches(in file: SwiftLintFile,
options: NSRegularExpression.MatchingOptions = []) -> [NSTextCheckingResult] {
return matches(in: file.stringView.string, options: options, range: file.stringView.range)
}
}

View File

@ -1,4 +1,4 @@
extension RandomAccessCollection where Index == Int {
public extension RandomAccessCollection where Index == Int {
/// Returns the first index in which an element of the collection satisfies the given predicate.
/// The collection assumed to be sorted. If collection is not have sorted values the result is undefined.
///

View File

@ -1,7 +1,7 @@
import Foundation
import SourceKittenFramework
extension Request {
public extension Request {
static let disableSourceKit = ProcessInfo.processInfo.environment["SWIFTLINT_DISABLE_SOURCEKIT"] != nil
func sendIfNotDisabled() throws -> [String: SourceKitRepresentable] {

View File

@ -1,13 +1,13 @@
import SourceKittenFramework
extension SourceKittenDictionary {
public extension SourceKittenDictionary {
/// Returns array of tuples containing "key.kind" and "byteRange" from Structure
/// that contains the byte offset. Returns all kinds if no parameter specified.
///
/// - parameter byteOffset: Int?
///
/// - returns: The kinds and byte ranges.
internal func kinds(forByteOffset byteOffset: ByteCount? = nil)
func kinds(forByteOffset byteOffset: ByteCount? = nil)
-> [(kind: String, byteRange: ByteRange)] {
var results = [(kind: String, byteRange: ByteRange)]()
@ -27,7 +27,7 @@ extension SourceKittenDictionary {
return results
}
internal func structures(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
func structures(forByteOffset byteOffset: ByteCount) -> [SourceKittenDictionary] {
var results = [SourceKittenDictionary]()
func parse(_ dictionary: SourceKittenDictionary) {

View File

@ -1,6 +1,6 @@
import SwiftSyntax
extension SourceRange {
public extension SourceRange {
/// Check if a position is contained within this range.
///
/// - parameter position: The position to check.

View File

@ -1,8 +1,8 @@
import Foundation
import SourceKittenFramework
extension String {
internal func hasTrailingWhitespace() -> Bool {
public extension String {
func hasTrailingWhitespace() -> Bool {
if isEmpty {
return false
}
@ -14,22 +14,14 @@ extension String {
return false
}
internal func isUppercase() -> Bool {
func isUppercase() -> Bool {
return self == uppercased()
}
internal func isLowercase() -> Bool {
func isLowercase() -> Bool {
return self == lowercased()
}
internal func nameStrippingLeadingUnderscoreIfPrivate(_ dict: SourceKittenDictionary) -> String {
if let acl = dict.accessibility,
acl.isPrivate && first == "_" {
return String(self[index(after: startIndex)...])
}
return self
}
private subscript (range: Range<Int>) -> String {
let nsrange = NSRange(location: range.lowerBound,
length: range.upperBound - range.lowerBound)
@ -39,21 +31,21 @@ extension String {
queuedFatalError("invalid range")
}
internal func substring(from: Int, length: Int? = nil) -> String {
func substring(from: Int, length: Int? = nil) -> String {
if let length {
return self[from..<from + length]
}
return String(self[index(startIndex, offsetBy: from, limitedBy: endIndex)!...])
}
internal func lastIndex(of search: String) -> Int? {
func lastIndex(of search: String) -> Int? {
if let range = range(of: search, options: [.literal, .backwards]) {
return distance(from: startIndex, to: range.lowerBound)
}
return nil
}
internal func nsrangeToIndexRange(_ nsrange: NSRange) -> Range<Index>? {
func nsrangeToIndexRange(_ nsrange: NSRange) -> Range<Index>? {
guard nsrange.location != NSNotFound else {
return nil
}
@ -70,18 +62,18 @@ extension String {
return fromIndex..<toIndex
}
internal var fullNSRange: NSRange {
var fullNSRange: NSRange {
return NSRange(location: 0, length: utf16.count)
}
/// Returns a new string, converting the path to a canonical absolute path.
///
/// - returns: A new `String`.
public func absolutePathStandardized() -> String {
func absolutePathStandardized() -> String {
return bridge().absolutePathRepresentation().bridge().standardizingPath
}
internal var isFile: Bool {
var isFile: Bool {
if self.isEmpty {
return false
}
@ -95,14 +87,14 @@ extension String {
/// Count the number of occurrences of the given character in `self`
/// - Parameter character: Character to count
/// - Returns: Number of times `character` occurs in `self`
public func countOccurrences(of character: Character) -> Int {
func countOccurrences(of character: Character) -> Int {
return self.reduce(0, {
$1 == character ? $0 + 1 : $0
})
}
/// If self is a path, this method can be used to get a path expression relative to a root directory
public func path(relativeTo rootDirectory: String) -> String {
func path(relativeTo rootDirectory: String) -> String {
let normalizedRootDir = rootDirectory.bridge().standardizingPath
let normalizedSelf = bridge().standardizingPath
if normalizedRootDir.isEmpty {
@ -123,7 +115,7 @@ extension String {
}
}
internal func deletingPrefix(_ prefix: String) -> String {
func deletingPrefix(_ prefix: String) -> String {
guard hasPrefix(prefix) else { return self }
return String(dropFirst(prefix.count))
}

View File

@ -1,6 +1,6 @@
import SourceKittenFramework
extension StringView {
public extension StringView {
/// Converts a line and column position in a code snippet to a byte offset.
/// - Parameters:
/// - line: Line in code snippet

View File

@ -2,7 +2,7 @@ import Foundation
import SourceKittenFramework
import SwiftSyntax
extension StringView {
public extension StringView {
/// Converts two absolute positions from SwiftSyntax to a valid `NSRange` if possible.
///
/// - parameter start: Starting position.

View File

@ -1,6 +1,6 @@
import SourceKittenFramework
extension SwiftDeclarationAttributeKind {
public extension SwiftDeclarationAttributeKind {
static var attributesRequiringFoundation: Set<SwiftDeclarationAttributeKind> {
return [
.objc,
@ -24,7 +24,7 @@ extension SwiftDeclarationAttributeKind {
case `dynamic`
case atPrefixed
init?(rawAttribute: String) {
public init?(rawAttribute: String) {
let allModifierGroups: Set<SwiftDeclarationAttributeKind.ModifierGroup> = [
.acl, .setterACL, .mutators, .override, .owned, .atPrefixed, .dynamic, .final, .typeMethods,
.required, .convenience, .lazy
@ -40,7 +40,7 @@ extension SwiftDeclarationAttributeKind {
}
}
var swiftDeclarationAttributeKinds: Set<SwiftDeclarationAttributeKind> {
public var swiftDeclarationAttributeKinds: Set<SwiftDeclarationAttributeKind> {
switch self {
case .acl:
return [
@ -94,7 +94,7 @@ extension SwiftDeclarationAttributeKind {
}
}
var debugDescription: String {
public var debugDescription: String {
return self.rawValue
}
}

View File

@ -1,7 +1,7 @@
import SourceKittenFramework
extension SwiftDeclarationKind {
internal static let variableKinds: Set<SwiftDeclarationKind> = [
public extension SwiftDeclarationKind {
static let variableKinds: Set<SwiftDeclarationKind> = [
.varClass,
.varGlobal,
.varInstance,
@ -10,7 +10,7 @@ extension SwiftDeclarationKind {
.varStatic
]
internal static let functionKinds: Set<SwiftDeclarationKind> = [
static let functionKinds: Set<SwiftDeclarationKind> = [
.functionAccessorAddress,
.functionAccessorDidset,
.functionAccessorGetter,
@ -27,7 +27,7 @@ extension SwiftDeclarationKind {
.functionSubscript
]
internal static let typeKinds: Set<SwiftDeclarationKind> = [
static let typeKinds: Set<SwiftDeclarationKind> = [
.class,
.struct,
.typealias,
@ -35,7 +35,7 @@ extension SwiftDeclarationKind {
.enum
]
internal static let extensionKinds: Set<SwiftDeclarationKind> = [
static let extensionKinds: Set<SwiftDeclarationKind> = [
.extension,
.extensionClass,
.extensionEnum,

View File

@ -1,6 +1,6 @@
import SwiftSyntax
extension SwiftLintFile {
public extension SwiftLintFile {
/// This function determines if given a scope with a left/right brace, such as a function, closure, type, etc, how
/// many lines the "body" spans when you ignore lines only containing comments and/or whitespace.
///

View File

@ -101,7 +101,7 @@ extension SwiftLintFile {
return id
}
internal var sourcekitdFailed: Bool {
public var sourcekitdFailed: Bool {
get {
return responseCache.get(self) == nil
}
@ -123,7 +123,7 @@ extension SwiftLintFile {
}
}
internal var parserDiagnostics: [String]? {
public var parserDiagnostics: [String]? {
if parserDiagnosticsDisabledForTests {
return nil
}
@ -133,9 +133,9 @@ extension SwiftLintFile {
.map(\.message)
}
internal var linesWithTokens: Set<Int> { linesWithTokensCache.get(self) }
public var linesWithTokens: Set<Int> { linesWithTokensCache.get(self) }
internal var structureDictionary: SourceKittenDictionary {
public var structureDictionary: SourceKittenDictionary {
guard let structureDictionary = structureDictionaryCache.get(self) else {
if let handler = assertHandler {
handler()
@ -146,9 +146,9 @@ extension SwiftLintFile {
return structureDictionary
}
internal var syntaxClassifications: SyntaxClassifications { syntaxClassificationsCache.get(self) }
public var syntaxClassifications: SyntaxClassifications { syntaxClassificationsCache.get(self) }
internal var syntaxMap: SwiftLintSyntaxMap {
public var syntaxMap: SwiftLintSyntaxMap {
guard let syntaxMap = syntaxMapCache.get(self) else {
if let handler = assertHandler {
handler()
@ -159,16 +159,17 @@ extension SwiftLintFile {
return syntaxMap
}
internal var syntaxTree: SourceFileSyntax { syntaxTreeCache.get(self) }
public var syntaxTree: SourceFileSyntax { syntaxTreeCache.get(self) }
internal var foldedSyntaxTree: SourceFileSyntax? { foldedSyntaxTreeCache.get(self) }
public var foldedSyntaxTree: SourceFileSyntax? { foldedSyntaxTreeCache.get(self) }
internal var locationConverter: SourceLocationConverter { locationConverterCache.get(self) }
public var locationConverter: SourceLocationConverter { locationConverterCache.get(self) }
internal var commands: [Command] { commandsCache.get(self).filter { $0.isValid } }
internal var invalidCommands: [Command] { commandsCache.get(self).filter { !$0.isValid } }
public var commands: [Command] { commandsCache.get(self).filter { $0.isValid } }
internal var syntaxTokensByLines: [[SwiftLintSyntaxToken]] {
public var invalidCommands: [Command] { commandsCache.get(self).filter { !$0.isValid } }
public var syntaxTokensByLines: [[SwiftLintSyntaxToken]] {
guard let syntaxTokensByLines = syntaxTokensByLinesCache.get(self) else {
if let handler = assertHandler {
handler()
@ -179,7 +180,7 @@ extension SwiftLintFile {
return syntaxTokensByLines
}
internal var syntaxKindsByLines: [[SourceKittenFramework.SyntaxKind]] {
public var syntaxKindsByLines: [[SourceKittenFramework.SyntaxKind]] {
guard let syntaxKindsByLines = syntaxKindsByLinesCache.get(self) else {
if let handler = assertHandler {
handler()

View File

@ -1,8 +1,8 @@
import Foundation
import SourceKittenFramework
internal func regex(_ pattern: String,
options: NSRegularExpression.Options? = nil) -> NSRegularExpression {
public func regex(_ pattern: String,
options: NSRegularExpression.Options? = nil) -> NSRegularExpression {
// all patterns used for regular expressions in SwiftLint are string literals which have been
// confirmed to work, so it's ok to force-try here.
@ -12,7 +12,7 @@ internal func regex(_ pattern: String,
}
extension SwiftLintFile {
internal func regions(restrictingRuleIdentifiers: Set<RuleIdentifier>? = nil) -> [Region] {
public func regions(restrictingRuleIdentifiers: Set<RuleIdentifier>? = nil) -> [Region] {
var regions = [Region]()
var disabledRules = Set<RuleIdentifier>()
let commands: [Command]
@ -57,7 +57,7 @@ extension SwiftLintFile {
return regions
}
internal func commands(in range: NSRange? = nil) -> [Command] {
public func commands(in range: NSRange? = nil) -> [Command] {
guard let range else {
return commands
.flatMap { $0.expand() }
@ -93,14 +93,14 @@ extension SwiftLintFile {
return Location(file: path, line: nextLine, character: nextCharacter)
}
internal func match(pattern: String, with syntaxKinds: [SyntaxKind], range: NSRange? = nil) -> [NSRange] {
public func match(pattern: String, with syntaxKinds: [SyntaxKind], range: NSRange? = nil) -> [NSRange] {
return match(pattern: pattern, range: range)
.filter { $0.1 == syntaxKinds }
.map { $0.0 }
}
internal func matchesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SwiftLintSyntaxToken])] {
public func matchesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SwiftLintSyntaxToken])] {
let contents = stringView
let range = range ?? contents.range
let syntax = syntaxMap
@ -110,25 +110,25 @@ extension SwiftLintFile {
}
}
internal func matchesAndSyntaxKinds(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SyntaxKind])] {
public func matchesAndSyntaxKinds(matching pattern: String,
range: NSRange? = nil) -> [(NSTextCheckingResult, [SyntaxKind])] {
return matchesAndTokens(matching: pattern, range: range).map { textCheckingResult, tokens in
(textCheckingResult, tokens.kinds)
}
}
internal func rangesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSRange, [SwiftLintSyntaxToken])] {
public func rangesAndTokens(matching pattern: String,
range: NSRange? = nil) -> [(NSRange, [SwiftLintSyntaxToken])] {
return matchesAndTokens(matching: pattern, range: range).map { ($0.0.range, $0.1) }
}
internal func match(pattern: String, range: NSRange? = nil, captureGroup: Int = 0) -> [(NSRange, [SyntaxKind])] {
public func match(pattern: String, range: NSRange? = nil, captureGroup: Int = 0) -> [(NSRange, [SyntaxKind])] {
return matchesAndSyntaxKinds(matching: pattern, range: range).map { textCheckingResult, syntaxKinds in
(textCheckingResult.range(at: captureGroup), syntaxKinds)
}
}
internal func swiftDeclarationKindsByLine() -> [[SwiftDeclarationKind]]? {
public func swiftDeclarationKindsByLine() -> [[SwiftDeclarationKind]]? {
if sourcekitdFailed {
return nil
}
@ -151,7 +151,7 @@ extension SwiftLintFile {
return results
}
internal func syntaxTokensByLine() -> [[SwiftLintSyntaxToken]]? {
public func syntaxTokensByLine() -> [[SwiftLintSyntaxToken]]? {
if sourcekitdFailed {
return nil
}
@ -180,7 +180,7 @@ extension SwiftLintFile {
return results
}
internal func syntaxKindsByLine() -> [[SyntaxKind]]? {
public func syntaxKindsByLine() -> [[SyntaxKind]]? {
guard !sourcekitdFailed, let tokens = syntaxTokensByLine() else {
return nil
}
@ -199,22 +199,22 @@ extension SwiftLintFile {
- returns: An array of [NSRange] objects consisting of regex matches inside
file contents.
*/
internal func match(pattern: String,
excludingSyntaxKinds syntaxKinds: Set<SyntaxKind>,
range: NSRange? = nil,
captureGroup: Int = 0) -> [NSRange] {
public func match(pattern: String,
excludingSyntaxKinds syntaxKinds: Set<SyntaxKind>,
range: NSRange? = nil,
captureGroup: Int = 0) -> [NSRange] {
return match(pattern: pattern, range: range, captureGroup: captureGroup)
.filter { syntaxKinds.isDisjoint(with: $0.1) }
.map { $0.0 }
}
internal typealias MatchMapping = (NSTextCheckingResult) -> NSRange
public typealias MatchMapping = (NSTextCheckingResult) -> NSRange
internal func match(pattern: String,
range: NSRange? = nil,
excludingSyntaxKinds: Set<SyntaxKind>,
excludingPattern: String,
exclusionMapping: MatchMapping = { $0.range }) -> [NSRange] {
public func match(pattern: String,
range: NSRange? = nil,
excludingSyntaxKinds: Set<SyntaxKind>,
excludingPattern: String,
exclusionMapping: MatchMapping = { $0.range }) -> [NSRange] {
let matches = match(pattern: pattern, excludingSyntaxKinds: excludingSyntaxKinds)
if matches.isEmpty {
return []
@ -225,7 +225,7 @@ extension SwiftLintFile {
return matches.filter { !$0.intersects(exclusionRanges) }
}
internal func append(_ string: String) {
public func append(_ string: String) {
guard string.isNotEmpty else {
return
}
@ -245,7 +245,7 @@ extension SwiftLintFile {
invalidateCache()
}
internal func write<S: StringProtocol>(_ string: S) {
public func write<S: StringProtocol>(_ string: S) {
guard string != contents else {
return
}
@ -267,7 +267,7 @@ extension SwiftLintFile {
invalidateCache()
}
internal func ruleEnabled(violatingRanges: [NSRange], for rule: Rule) -> [NSRange] {
public func ruleEnabled(violatingRanges: [NSRange], for rule: Rule) -> [NSRange] {
let fileRegions = regions()
if fileRegions.isEmpty { return violatingRanges }
return violatingRanges.filter { range in
@ -278,11 +278,11 @@ extension SwiftLintFile {
}
}
internal func ruleEnabled(violatingRange: NSRange, for rule: Rule) -> NSRange? {
public func ruleEnabled(violatingRange: NSRange, for rule: Rule) -> NSRange? {
return ruleEnabled(violatingRanges: [violatingRange], for: rule).first
}
internal func isACL(token: SwiftLintSyntaxToken) -> Bool {
public func isACL(token: SwiftLintSyntaxToken) -> Bool {
guard token.kind == .attributeBuiltin else {
return false
}
@ -291,7 +291,7 @@ extension SwiftLintFile {
return aclString.flatMap(AccessControlLevel.init(description:)) != nil
}
internal func contents(for token: SwiftLintSyntaxToken) -> String? {
public func contents(for token: SwiftLintSyntaxToken) -> String? {
return stringView.substringWithByteRange(token.range)
}
}

View File

@ -3,10 +3,10 @@ import SourceKittenFramework
import SwiftSyntax
// workaround for https://bugs.swift.org/browse/SR-10121 so we can use `Self` in a closure
protocol SwiftLintSyntaxVisitor: SyntaxVisitor {}
public protocol SwiftLintSyntaxVisitor: SyntaxVisitor {}
extension SyntaxVisitor: SwiftLintSyntaxVisitor {}
extension SwiftLintSyntaxVisitor {
public extension SwiftLintSyntaxVisitor {
func walk<T, SyntaxType: SyntaxProtocol>(tree: SyntaxType, handler: (Self) -> T) -> T {
#if DEBUG
// workaround for stack overflow when running in debug
@ -44,7 +44,7 @@ extension SwiftLintSyntaxVisitor {
}
}
extension SyntaxProtocol {
public extension SyntaxProtocol {
func windowsOfThreeTokens() -> [(TokenSyntax, TokenSyntax, TokenSyntax)] {
Array(tokens(viewMode: .sourceAccurate))
.windows(ofCount: 3)
@ -61,7 +61,7 @@ extension SyntaxProtocol {
}
}
extension AbsolutePosition {
public extension AbsolutePosition {
func isContainedIn(regions: [SourceRange], locationConverter: SourceLocationConverter) -> Bool {
regions.contains { region in
region.contains(self, locationConverter: locationConverter)
@ -69,13 +69,13 @@ extension AbsolutePosition {
}
}
extension ByteSourceRange {
public extension ByteSourceRange {
func toSourceKittenByteRange() -> ByteRange {
ByteRange(location: ByteCount(offset), length: ByteCount(length))
}
}
extension ClassDeclSyntax {
public extension ClassDeclSyntax {
func isXCTestCase(_ testParentClasses: Set<String>) -> Bool {
guard let inheritanceList = inheritanceClause?.inheritedTypeCollection else {
return false
@ -85,7 +85,7 @@ extension ClassDeclSyntax {
}
}
extension ExprSyntax {
public extension ExprSyntax {
var asFunctionCall: FunctionCallExprSyntax? {
if let functionCall = self.as(FunctionCallExprSyntax.self) {
return functionCall
@ -99,19 +99,19 @@ extension ExprSyntax {
}
}
extension StringLiteralExprSyntax {
public extension StringLiteralExprSyntax {
var isEmptyString: Bool {
segments.onlyElement?.contentLength == .zero
}
}
extension TokenKind {
public extension TokenKind {
var isEqualityComparison: Bool {
self == .binaryOperator("==") || self == .binaryOperator("!=")
}
}
extension ModifierListSyntax? {
public extension ModifierListSyntax? {
var containsLazy: Bool {
contains(tokenKind: .keyword(.lazy))
}
@ -160,26 +160,26 @@ extension ModifierListSyntax? {
}
}
extension AttributeSyntax {
public extension AttributeSyntax {
var attributeNameText: String {
attributeName.as(SimpleTypeIdentifierSyntax.self)?.name.text ??
attributeName.description
}
}
extension AttributeListSyntax? {
public extension AttributeListSyntax? {
func contains(attributeNamed attributeName: String) -> Bool {
self?.contains { $0.as(AttributeSyntax.self)?.attributeNameText == attributeName } == true
}
}
extension TokenKind {
public extension TokenKind {
var isUnavailableKeyword: Bool {
self == .keyword(.unavailable) || self == .identifier("unavailable")
}
}
extension VariableDeclSyntax {
public extension VariableDeclSyntax {
var isIBOutlet: Bool {
attributes.contains(attributeNamed: "IBOutlet")
}
@ -220,7 +220,7 @@ public extension EnumDeclSyntax {
}
}
extension FunctionDeclSyntax {
public extension FunctionDeclSyntax {
var isIBAction: Bool {
attributes.contains(attributeNamed: "IBAction")
}
@ -251,7 +251,7 @@ extension FunctionDeclSyntax {
}
}
extension AccessorBlockSyntax {
public extension AccessorBlockSyntax {
var getAccessor: AccessorDeclSyntax? {
accessors.first { accessor in
accessor.accessorKind.tokenKind == .keyword(.get)
@ -273,7 +273,7 @@ extension AccessorBlockSyntax {
}
}
extension TypeInheritanceClauseSyntax? {
public extension TypeInheritanceClauseSyntax? {
func containsInheritedType(inheritedTypes: Set<String>) -> Bool {
self?.inheritedTypeCollection.contains { elem in
guard let simpleType = elem.typeName.as(SimpleTypeIdentifierSyntax.self) else {
@ -285,7 +285,7 @@ extension TypeInheritanceClauseSyntax? {
}
}
extension Trivia {
public extension Trivia {
func containsNewlines() -> Bool {
contains { piece in
if case .newlines = piece {
@ -312,7 +312,7 @@ extension Trivia {
}
}
extension TriviaPiece {
public extension TriviaPiece {
var isHorizontalWhitespace: Bool {
switch self {
case .spaces, .tabs:
@ -323,7 +323,7 @@ extension TriviaPiece {
}
}
extension IntegerLiteralExprSyntax {
public extension IntegerLiteralExprSyntax {
var isZero: Bool {
guard case let .integerLiteral(number) = digits.tokenKind else {
return false
@ -333,7 +333,7 @@ extension IntegerLiteralExprSyntax {
}
}
extension FloatLiteralExprSyntax {
public extension FloatLiteralExprSyntax {
var isZero: Bool {
guard case let .floatingLiteral(number) = floatingDigits.tokenKind else {
return false

View File

@ -1,6 +1,6 @@
import IDEUtils
extension SyntaxClassification {
public extension SyntaxClassification {
// True if it is any kind of comment.
var isComment: Bool {
switch self {

View File

@ -1,6 +1,6 @@
import SourceKittenFramework
extension SyntaxKind {
public extension SyntaxKind {
init(shortName: Swift.String) throws {
let prefix = "source.lang.swift.syntaxtype."
guard let kind = SyntaxKind(rawValue: prefix + shortName.lowercased()) else {

View File

@ -1,31 +1,43 @@
/// A basic stack type implementing the LIFO principle - only the last inserted element can be accessed and removed.
struct Stack<Element> {
public struct Stack<Element> {
private var elements = [Element]()
var isEmpty: Bool {
/// Creates an empty `Stack`.
public init() {}
/// True if the stack has no elements. False otherwise.
public var isEmpty: Bool {
elements.isEmpty
}
var count: Int {
/// The number of elements in this stack.
public var count: Int {
elements.count
}
mutating func push(_ element: Element) {
/// Pushes (appends) an element onto the stack.
///
/// - parameter element: The element to push onto the stack.
public mutating func push(_ element: Element) {
elements.append(element)
}
/// Removes and returns the last element of the stack.
///
/// - returns: The last element of the stack if the stack is not empty; otherwise, nil.
@discardableResult
mutating func pop() -> Element? {
public mutating func pop() -> Element? {
elements.popLast()
}
func peek() -> Element? {
/// Returns the last element of the stack if the stack is not empty; otherwise, nil.
public func peek() -> Element? {
elements.last
}
}
extension Stack: CustomDebugStringConvertible where Element == CustomDebugStringConvertible {
var debugDescription: String {
public var debugDescription: String {
let intermediateElements = count > 1 ? elements[1 ..< count - 1] : []
return """
Stack with \(count) elements:

View File

@ -16,7 +16,7 @@ public enum AccessControlLevel: String, CustomStringConvertible {
/// Initializes an access control level by its Swift source keyword value.
///
/// - parameter value: The value used to describe this level in Swift source code.
internal init?(description value: String) {
public init?(description value: String) {
switch value {
case "private": self = .private
case "fileprivate": self = .fileprivate
@ -45,7 +45,7 @@ public enum AccessControlLevel: String, CustomStringConvertible {
}
/// Returns true if is `private` or `fileprivate`
var isPrivate: Bool {
public var isPrivate: Bool {
return self == .private || self == .fileprivate
}
}

View File

@ -46,15 +46,15 @@ public struct Command: Equatable {
}
/// The action (verb) that SwiftLint should perform when interpreting this command.
internal let action: Action
public let action: Action
/// The identifiers for the rules associated with this command.
internal let ruleIdentifiers: Set<RuleIdentifier>
public let ruleIdentifiers: Set<RuleIdentifier>
/// The line in the source file where this command is defined.
internal let line: Int
public let line: Int
/// The character offset within the line in the source file where this command is defined.
internal let character: Int?
public let character: Int?
/// This command's modifier, if any.
internal let modifier: Modifier?
public let modifier: Modifier?
/// The comment following this command's `-` delimiter, if any.
internal let trailingComment: String?

View File

@ -147,7 +147,7 @@ extension Example: Comparable {
}
}
extension Array where Element == Example {
public extension Array where Element == Example {
/// Make these examples skip wrapping in comment tests.
func skipWrappingInCommentTests() -> Self {
map { $0.skipWrappingInCommentTest() }

View File

@ -4,10 +4,10 @@ import SourceKittenFramework
/// A unit of Swift source code, either on disk or in memory.
public final class SwiftLintFile {
/// The underlying SourceKitten file.
let file: File
public let file: File
let id: UUID
/// Whether or not this is a file generated for testing purposes.
private(set) var isTestFile = false
public private(set) var isTestFile = false
/// A file is virtual if it is not backed by a filesystem path.
private(set) var isVirtual = false

View File

@ -21,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: ByteRange) -> [SwiftLintSyntaxToken] {
public func tokens(inByteRange byteRange: ByteRange) -> [SwiftLintSyntaxToken] {
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
return token.range.intersects(byteRange)
}
@ -48,7 +48,7 @@ public struct SwiftLintSyntaxMap {
/// - parameter byteRange: Byte range.
///
/// - returns: The syntax kinds in the specified byte range.
internal func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] {
public func kinds(inByteRange byteRange: ByteRange) -> [SyntaxKind] {
return tokens(inByteRange: byteRange).compactMap { $0.kind }
}
}

View File

@ -32,7 +32,7 @@ public struct SwiftLintSyntaxToken {
}
}
extension Array where Element == SwiftLintSyntaxToken {
public extension Array where Element == SwiftLintSyntaxToken {
/// The kinds for these tokens.
var kinds: [SyntaxKind] {
return compactMap { $0.kind }

View File

@ -1,5 +1,5 @@
/// Interface providing access to a cache description.
internal protocol CacheDescriptionProvider {
public protocol CacheDescriptionProvider {
/// The cache description which will be used to determine if a previous
/// cached value is still valid given the new cache value.
var cacheDescription: String { get }

View File

@ -63,27 +63,27 @@ public protocol Rule {
func validate(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [StyleViolation]
}
extension Rule {
public func validate(file: SwiftLintFile, using storage: RuleStorage,
compilerArguments: [String]) -> [StyleViolation] {
public extension Rule {
func validate(file: SwiftLintFile, using storage: RuleStorage,
compilerArguments: [String]) -> [StyleViolation] {
return validate(file: file, compilerArguments: compilerArguments)
}
public func validate(file: SwiftLintFile, compilerArguments: [String]) -> [StyleViolation] {
func validate(file: SwiftLintFile, compilerArguments: [String]) -> [StyleViolation] {
return validate(file: file)
}
public func isEqualTo(_ rule: Rule) -> Bool {
func isEqualTo(_ rule: Rule) -> Bool {
return Self.description == type(of: rule).description
}
public func collectInfo(for file: SwiftLintFile, into storage: RuleStorage, compilerArguments: [String]) {
func collectInfo(for file: SwiftLintFile, into storage: RuleStorage, compilerArguments: [String]) {
// no-op: only CollectingRules mutate their storage
}
/// The cache description which will be used to determine if a previous
/// cached value is still valid given the new cache value.
internal var cacheDescription: String {
var cacheDescription: String {
return (self as? CacheDescriptionProvider)?.cacheDescription ?? configurationDescription
}
}
@ -130,7 +130,6 @@ public protocol CorrectableRule: Rule {
func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [Correction]
}
@_spi(TestHelper)
public extension CorrectableRule {
func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction] {
return correct(file: file)
@ -142,7 +141,6 @@ public extension CorrectableRule {
/// A correctable rule that can apply its corrections by replacing the content of ranges in the offending file with
/// updated content.
@_spi(TestHelper)
public protocol SubstitutionCorrectableRule: CorrectableRule {
/// Returns the NSString-based `NSRange`s to be replaced in the specified file.
///
@ -160,7 +158,6 @@ public protocol SubstitutionCorrectableRule: CorrectableRule {
func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)?
}
@_spi(TestHelper)
public extension SubstitutionCorrectableRule {
func correct(file: SwiftLintFile) -> [Correction] {
let violatingRanges = file.ruleEnabled(violatingRanges: violationRanges(in: file), for: self)
@ -184,7 +181,6 @@ public extension SubstitutionCorrectableRule {
}
/// A `SubstitutionCorrectableRule` that is also an `ASTRule`.
@_spi(TestHelper)
public protocol SubstitutionCorrectableASTRule: SubstitutionCorrectableRule, ASTRule {
/// Returns the NSString-based `NSRange`s to be replaced in the specified file.
///
@ -220,7 +216,6 @@ public extension AnalyzerRule {
}
/// :nodoc:
@_spi(TestHelper)
public extension AnalyzerRule where Self: CorrectableRule {
func correct(file: SwiftLintFile) -> [Correction] {
queuedFatalError("Must call `correct(file:compilerArguments:)` for AnalyzerRule")

View File

@ -1,7 +1,6 @@
import SwiftSyntax
/// A SwiftLint CorrectableRule that performs its corrections using a SwiftSyntax `SyntaxRewriter`.
@_spi(TestHelper)
public protocol SwiftSyntaxCorrectableRule: SwiftSyntaxRule, CorrectableRule {
/// Produce a `ViolationsSyntaxRewriter` for the given file.
///
@ -11,7 +10,6 @@ public protocol SwiftSyntaxCorrectableRule: SwiftSyntaxRule, CorrectableRule {
func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter?
}
@_spi(TestHelper)
public extension SwiftSyntaxCorrectableRule {
func correct(file: SwiftLintFile) -> [Correction] {
guard let rewriter = makeRewriter(file: file),

View File

@ -112,7 +112,7 @@ public struct ReasonedRuleViolation: Comparable {
/// Extension for arrays of `ReasonedRuleViolation`s that provides the automatic conversion of
/// `AbsolutePosition`s into `ReasonedRuleViolation`s (without a specific reason).
extension Array where Element == ReasonedRuleViolation {
public extension Array where Element == ReasonedRuleViolation {
/// Append a minimal violation for the specified position.
///
/// - parameter position: The position for the violation to append.
@ -131,9 +131,9 @@ extension Array where Element == ReasonedRuleViolation {
/// A SwiftSyntax `SyntaxVisitor` that produces absolute positions where violations should be reported.
open class ViolationsSyntaxVisitor: SyntaxVisitor {
/// Positions in a source file where violations should be reported.
internal var violations: [ReasonedRuleViolation] = []
public var violations: [ReasonedRuleViolation] = []
/// List of declaration types that shall be skipped while traversing the AST.
internal var skippableDeclarations: [DeclSyntaxProtocol.Type] { [] }
open var skippableDeclarations: [DeclSyntaxProtocol.Type] { [] }
override open func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
skippableDeclarations.contains { $0 == ActorDeclSyntax.self } ? .skipChildren : .visitChildren
@ -172,7 +172,7 @@ open class ViolationsSyntaxVisitor: SyntaxVisitor {
}
}
extension Array where Element == DeclSyntaxProtocol.Type {
public extension Array where Element == DeclSyntaxProtocol.Type {
/// All visitable declaration syntax types.
static let all: Self = [
ActorDeclSyntax.self,

View File

@ -26,7 +26,7 @@ public struct RegexConfiguration: SeverityBasedRuleConfiguration, Hashable, Cach
return "\(severity.rawValue): \(regex.pattern)"
}
internal var cacheDescription: String {
public var cacheDescription: String {
let jsonObject: [String] = [
identifier,
name ?? "",

View File

@ -19,21 +19,21 @@ public struct SeverityLevelsConfiguration: RuleConfiguration, Equatable {
}
/// The threshold for a violation to be a warning.
var warning: Int
public var warning: Int
/// The threshold for a violation to be an error.
var error: Int?
public var error: Int?
/// Create a `SeverityLevelsConfiguration` based on the sepecified `warning` and `error` thresholds.
///
/// - parameter warning: The threshold for a violation to be a warning.
/// - parameter error: The threshold for a violation to be an error.
init(warning: Int, error: Int? = nil) {
public init(warning: Int, error: Int? = nil) {
self.warning = warning
self.error = error
}
/// The rule parameters that define the thresholds that should map to each severity.
var params: [RuleParameter<Int>] {
public var params: [RuleParameter<Int>] {
if let error {
return [RuleParameter(severity: .error, value: error),
RuleParameter(severity: .warning, value: warning)]

View File

@ -142,4 +142,12 @@ private extension String {
let operators = ["/", "=", "-", "+", "!", "*", "|", "^", "~", "?", ".", "%", "<", ">", "&"]
return operators.contains(where: hasPrefix)
}
func nameStrippingLeadingUnderscoreIfPrivate(_ dict: SourceKittenDictionary) -> String {
if let acl = dict.accessibility,
acl.isPrivate && first == "_" {
return String(self[index(after: startIndex)...])
}
return self
}
}