Add documentation comments to all public declarations (#3027)
This commit is contained in:
parent
df6cf175d8
commit
37167a8a35
10
.jazzy.yaml
10
.jazzy.yaml
|
@ -18,3 +18,13 @@ custom_categories:
|
||||||
- name: Rules
|
- name: Rules
|
||||||
children:
|
children:
|
||||||
- Rule Directory
|
- Rule Directory
|
||||||
|
- name: Reporters
|
||||||
|
children:
|
||||||
|
- CSVReporter
|
||||||
|
- CheckstyleReporter
|
||||||
|
- EmojiReporter
|
||||||
|
- GitHubActionsLoggingReporter
|
||||||
|
- HTMLReporter
|
||||||
|
- JSONReporter
|
||||||
|
- JUnitReporter
|
||||||
|
- MarkdownReporter
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/// The rule list containing all available rules built into SwiftLint.
|
||||||
public let masterRuleList = RuleList(rules: [
|
public let masterRuleList = RuleList(rules: [
|
||||||
{% for rule in types.structs where rule.name|hasSuffix:"Rule" or rule.name|hasSuffix:"Rules" %} {{ rule.name }}.self{% if not forloop.last %},{% endif %}
|
{% for rule in types.structs where rule.name|hasSuffix:"Rule" or rule.name|hasSuffix:"Rules" %} {{ rule.name }}.self{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}])
|
{% endfor %}])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/// User-facing documentation for a SwiftLint rule.
|
/// User-facing documentation for a SwiftLint rule.
|
||||||
public struct RuleDocumentation {
|
struct RuleDocumentation {
|
||||||
private let ruleType: Rule.Type
|
private let ruleType: Rule.Type
|
||||||
|
|
||||||
/// Creates a RuleDocumentation instance from a Rule type.
|
/// Creates a RuleDocumentation instance from a Rule type.
|
||||||
|
|
|
@ -37,6 +37,8 @@ extension Configuration {
|
||||||
return cachedConfigurationsByPath[path]
|
return cachedConfigurationsByPath[path]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the current `Configuration` with its `computedCacheDescription` property set to the value of
|
||||||
|
/// `cacheDescription`, which is expensive to compute.
|
||||||
public func withPrecomputedCacheDescription() -> Configuration {
|
public func withPrecomputedCacheDescription() -> Configuration {
|
||||||
var result = self
|
var result = self
|
||||||
result.computedCacheDescription = result.cacheDescription
|
result.computedCacheDescription = result.cacheDescription
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
public extension Configuration {
|
public extension Configuration {
|
||||||
|
/// The style of indentation used in a Swift project.
|
||||||
enum IndentationStyle: Equatable {
|
enum IndentationStyle: Equatable {
|
||||||
|
/// Swift source code should be indented using tabs.
|
||||||
case tabs
|
case tabs
|
||||||
|
/// Swift source code should be indented using spaces with `count` spaces per indentation level.
|
||||||
case spaces(count: Int)
|
case spaces(count: Int)
|
||||||
|
|
||||||
|
/// The default indentation style if none is explicitly provided.
|
||||||
public static var `default` = spaces(count: 4)
|
public static var `default` = spaces(count: 4)
|
||||||
|
|
||||||
|
/// Creates an indentation style based on an untyped configuration value.
|
||||||
|
///
|
||||||
|
/// - parameter object: The configuration value.
|
||||||
internal init?(_ object: Any?) {
|
internal init?(_ object: Any?) {
|
||||||
switch object {
|
switch object {
|
||||||
case let value as Int: self = .spaces(count: value)
|
case let value as Int: self = .spaces(count: value)
|
||||||
|
|
|
@ -1,11 +1,26 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Configuration {
|
extension Configuration {
|
||||||
|
/// Returns the files that can be linted by SwiftLint in the specified parent path.
|
||||||
|
///
|
||||||
|
/// - parameter path: The parent path in which to search for lintable files. Can be a directory or a file.
|
||||||
|
/// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if `path`
|
||||||
|
/// is an exact match.
|
||||||
|
///
|
||||||
|
/// - returns: Files to lint.
|
||||||
public func lintableFiles(inPath path: String, forceExclude: Bool) -> [SwiftLintFile] {
|
public func lintableFiles(inPath path: String, forceExclude: Bool) -> [SwiftLintFile] {
|
||||||
return lintablePaths(inPath: path, forceExclude: forceExclude)
|
return lintablePaths(inPath: path, forceExclude: forceExclude)
|
||||||
.compactMap(SwiftLintFile.init(pathDeferringReading:))
|
.compactMap(SwiftLintFile.init(pathDeferringReading:))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the paths for files that can be linted by SwiftLint in the specified parent path.
|
||||||
|
///
|
||||||
|
/// - parameter path: The parent path in which to search for lintable files. Can be a directory or a file.
|
||||||
|
/// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if `path`
|
||||||
|
/// is an exact match.
|
||||||
|
/// - parameter fileManager: The lintable file manager to use to search for lintable files.
|
||||||
|
///
|
||||||
|
/// - returns: Paths for files to lint.
|
||||||
internal func lintablePaths(inPath path: String, forceExclude: Bool,
|
internal func lintablePaths(inPath path: String, forceExclude: Bool,
|
||||||
fileManager: LintableFileManager = FileManager.default) -> [String] {
|
fileManager: LintableFileManager = FileManager.default) -> [String] {
|
||||||
// If path is a file and we're not forcing excludes, skip filtering with excluded/included paths
|
// If path is a file and we're not forcing excludes, skip filtering with excluded/included paths
|
||||||
|
@ -18,9 +33,13 @@ extension Configuration {
|
||||||
}
|
}
|
||||||
return filterExcludedPaths(fileManager: fileManager, in: pathsForPath, includedPaths)
|
return filterExcludedPaths(fileManager: fileManager, in: pathsForPath, includedPaths)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension Configuration {
|
/// Returns an array of file paths after removing the excluded paths as defined by this configuration.
|
||||||
|
///
|
||||||
|
/// - parameter fileManager: The lintable file manager to use to expand the excluded paths into all matching paths.
|
||||||
|
/// - parameter paths: The input paths to filter.
|
||||||
|
///
|
||||||
|
/// - returns: The input paths after removing the excluded paths.
|
||||||
public func filterExcludedPaths(fileManager: LintableFileManager = FileManager.default,
|
public func filterExcludedPaths(fileManager: LintableFileManager = FileManager.default,
|
||||||
in paths: [String]...) -> [String] {
|
in paths: [String]...) -> [String] {
|
||||||
let allPaths = paths.flatMap { $0 }
|
let allPaths = paths.flatMap { $0 }
|
||||||
|
@ -36,6 +55,12 @@ extension Configuration {
|
||||||
return result.map { $0 as! String }
|
return result.map { $0 as! String }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the file paths that are excluded by this configuration after expanding them using the specified file
|
||||||
|
/// manager.
|
||||||
|
///
|
||||||
|
/// - parameter fileManager: The file manager to get child paths in a given parent location.
|
||||||
|
///
|
||||||
|
/// - returns: The expanded excluded file paths.
|
||||||
internal func excludedPaths(fileManager: LintableFileManager) -> [String] {
|
internal func excludedPaths(fileManager: LintableFileManager) -> [String] {
|
||||||
return excluded
|
return excluded
|
||||||
.flatMap(Glob.resolveGlob)
|
.flatMap(Glob.resolveGlob)
|
||||||
|
|
|
@ -2,6 +2,12 @@ import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
extension Configuration {
|
extension Configuration {
|
||||||
|
/// Returns a new configuration that applies to the specified file by merging the current configuration with any
|
||||||
|
/// child configurations in the directory inheritance graph present until the level of the specified file.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to obtain a configuration value.
|
||||||
|
///
|
||||||
|
/// - returns: A new configuration.
|
||||||
public func configuration(for file: SwiftLintFile) -> Configuration {
|
public func configuration(for file: SwiftLintFile) -> Configuration {
|
||||||
if let containingDir = file.path?.bridge().deletingLastPathComponent {
|
if let containingDir = file.path?.bridge().deletingLastPathComponent {
|
||||||
return configuration(forPath: containingDir)
|
return configuration(forPath: containingDir)
|
||||||
|
|
|
@ -52,6 +52,14 @@ extension Configuration {
|
||||||
return .default
|
return .default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a Configuration value based on the specified parameters.
|
||||||
|
///
|
||||||
|
/// - parameter dict: The untyped dictionary to serve as the input for this typed configuration.
|
||||||
|
/// Typically generated from a YAML-formatted file.
|
||||||
|
/// - parameter ruleList: The list of rules to be available to this configuration.
|
||||||
|
/// - parameter enableAllRules: Whether all rules from `ruleList` should be enabled, regardless of the
|
||||||
|
/// settings in `dict`.
|
||||||
|
/// - parameter cachePath: The location of the persisted cache on disk.
|
||||||
public init?(dict: [String: Any], ruleList: RuleList = masterRuleList, enableAllRules: Bool = false,
|
public init?(dict: [String: Any], ruleList: RuleList = masterRuleList, enableAllRules: Bool = false,
|
||||||
cachePath: String? = nil, customRulesIdentifiers: [String] = []) {
|
cachePath: String? = nil, customRulesIdentifiers: [String] = []) {
|
||||||
// Use either new 'opt_in_rules' or deprecated 'enabled_rules' for now.
|
// Use either new 'opt_in_rules' or deprecated 'enabled_rules' for now.
|
||||||
|
|
|
@ -1,16 +1,27 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// A collection of keys and values as parsed out of SourceKit, with many conveniences for accessing SwiftLint-specific
|
||||||
|
/// values.
|
||||||
public struct SourceKittenDictionary {
|
public struct SourceKittenDictionary {
|
||||||
|
/// The underlying SourceKitten dictionary.
|
||||||
public let value: [String: SourceKitRepresentable]
|
public let value: [String: SourceKitRepresentable]
|
||||||
|
/// The cached substructure for this dictionary. Empty if there is no substructure.
|
||||||
public let substructure: [SourceKittenDictionary]
|
public let substructure: [SourceKittenDictionary]
|
||||||
|
|
||||||
|
/// The kind of Swift expression represented by this dictionary, if it is an expression.
|
||||||
public let expressionKind: SwiftExpressionKind?
|
public let expressionKind: SwiftExpressionKind?
|
||||||
|
/// The kind of Swift declaration represented by this dictionary, if it is a declaration.
|
||||||
public let declarationKind: SwiftDeclarationKind?
|
public let declarationKind: SwiftDeclarationKind?
|
||||||
|
/// The kind of Swift statement represented by this dictionary, if it is a statement.
|
||||||
public let statementKind: StatementKind?
|
public let statementKind: StatementKind?
|
||||||
|
|
||||||
|
/// The accessibility level for this dictionary, if it is a declaration.
|
||||||
public let accessibility: AccessControlLevel?
|
public let accessibility: AccessControlLevel?
|
||||||
|
|
||||||
|
/// Creates a SourceKitten dictionary given a `Dictionary<String, SourceKitRepresentable>` input.
|
||||||
|
///
|
||||||
|
/// - parameter value: The input dictionary/
|
||||||
init(_ value: [String: SourceKitRepresentable]) {
|
init(_ value: [String: SourceKitRepresentable]) {
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
@ -91,15 +102,18 @@ public struct SourceKittenDictionary {
|
||||||
return (value["key.doclength"] as? Int64).flatMap({ Int($0) })
|
return (value["key.doclength"] as? Int64).flatMap({ Int($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The attribute for this dictionary, as returned by SourceKit.
|
||||||
var attribute: String? {
|
var attribute: String? {
|
||||||
return value["key.attribute"] as? String
|
return value["key.attribute"] as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `SwiftDeclarationAttributeKind` values associated with this dictionary.
|
||||||
var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
|
var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
|
||||||
return swiftAttributes.compactMap { $0.attribute }
|
return swiftAttributes.compactMap { $0.attribute }
|
||||||
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
|
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The fully preserved SourceKitten dictionaries for all the attributes associated with this dictionary.
|
||||||
var swiftAttributes: [SourceKittenDictionary] {
|
var swiftAttributes: [SourceKittenDictionary] {
|
||||||
let array = value["key.attributes"] as? [SourceKitRepresentable] ?? []
|
let array = value["key.attributes"] as? [SourceKitRepresentable] ?? []
|
||||||
let dictionaries = array.compactMap { $0 as? [String: SourceKitRepresentable] }
|
let dictionaries = array.compactMap { $0 as? [String: SourceKitRepresentable] }
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// An interface for enumerating files that can be linted by SwiftLint.
|
||||||
public protocol LintableFileManager {
|
public protocol LintableFileManager {
|
||||||
func filesToLint(inPath: String, rootDirectory: String?) -> [String]
|
/// Returns all files that can be linted in the specified path. If the path is relative, it will be appended to the
|
||||||
func modificationDate(forFileAtPath: String) -> Date?
|
/// specified root path, or currentt working directory if no root directory is specified.
|
||||||
|
///
|
||||||
|
/// - parameter path: The path in which lintable files should be found.
|
||||||
|
/// - parameter rootDirectory: The parent directory for the specified path. If none is provided, the current working
|
||||||
|
/// directory will be used.
|
||||||
|
///
|
||||||
|
/// - returns: Files to lint.
|
||||||
|
func filesToLint(inPath path: String, rootDirectory: String?) -> [String]
|
||||||
|
|
||||||
|
/// Returns the date when the file at the specified path was last modified. Returns `nil` if the file cannot be
|
||||||
|
/// found or its last modification date cannot be determined.
|
||||||
|
///
|
||||||
|
/// - parameter path: The file whose modification date should be determined.
|
||||||
|
///
|
||||||
|
/// - returns: A date, if one was determined.
|
||||||
|
func modificationDate(forFileAtPath path: String) -> Date?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FileManager: LintableFileManager {
|
extension FileManager: LintableFileManager {
|
||||||
|
|
|
@ -5,17 +5,12 @@ private var regexCache = [RegexCacheKey: NSRegularExpression]()
|
||||||
private let regexCacheLock = NSLock()
|
private let regexCacheLock = NSLock()
|
||||||
|
|
||||||
private struct RegexCacheKey: Hashable {
|
private struct RegexCacheKey: Hashable {
|
||||||
// Disable unused private declaration rule here because even though we don't use these properties
|
|
||||||
// directly, we rely on them for their hashable and equatable behavior.
|
|
||||||
// swiftlint:disable unused_declaration
|
|
||||||
let pattern: String
|
let pattern: String
|
||||||
let options: NSRegularExpression.Options
|
let options: NSRegularExpression.Options
|
||||||
// swiftlint:enable unused_declaration
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NSRegularExpression.Options: Hashable {
|
func hash(into hasher: inout Hasher) {
|
||||||
public func hash(into hasher: inout Hasher) {
|
hasher.combine(pattern)
|
||||||
hasher.combine(rawValue)
|
hasher.combine(options.rawValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ private let outputQueue: DispatchQueue = {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A thread-safe version of Swift's standard print().
|
A thread-safe version of Swift's standard `print()`.
|
||||||
|
|
||||||
- parameter object: Object to print.
|
- parameter object: Object to print.
|
||||||
*/
|
*/
|
||||||
|
@ -29,7 +29,7 @@ public func queuedPrint<T>(_ object: T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A thread-safe, newline-terminated version of fputs(..., stderr).
|
A thread-safe, newline-terminated version of `fputs(..., stderr)`.
|
||||||
|
|
||||||
- parameter string: String to print.
|
- parameter string: String to print.
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +41,7 @@ public func queuedPrintError(_ string: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A thread-safe, newline-terminated version of fatalError that doesn't leak
|
A thread-safe, newline-terminated version of `fatalError(...)` that doesn't leak
|
||||||
the source path from the compiled binary.
|
the source path from the compiled binary.
|
||||||
*/
|
*/
|
||||||
public func queuedFatalError(_ string: String, file: StaticString = #file, line: UInt = #line) -> Never {
|
public func queuedFatalError(_ string: String, file: StaticString = #file, line: UInt = #line) -> Never {
|
||||||
|
|
|
@ -74,6 +74,7 @@ extension String {
|
||||||
return NSRange(location: 0, length: utf16.count)
|
return NSRange(location: 0, length: utf16.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new string, converting the path to a canonical absolute path.
|
||||||
public func absolutePathStandardized() -> String {
|
public func absolutePathStandardized() -> String {
|
||||||
return bridge().absolutePathRepresentation().bridge().standardizingPath
|
return bridge().absolutePathRepresentation().bridge().standardizingPath
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
public extension SwiftDeclarationAttributeKind {
|
extension SwiftDeclarationAttributeKind {
|
||||||
enum ModifierGroup: String, CustomDebugStringConvertible {
|
enum ModifierGroup: String, CustomDebugStringConvertible {
|
||||||
case `override`
|
case `override`
|
||||||
case acl
|
case acl
|
||||||
|
@ -85,7 +85,7 @@ public extension SwiftDeclarationAttributeKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var debugDescription: String {
|
var debugDescription: String {
|
||||||
return self.rawValue
|
return self.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
|
/// The kind of expression for a contiguous set of Swift source tokens.
|
||||||
public enum SwiftExpressionKind: String {
|
public enum SwiftExpressionKind: String {
|
||||||
|
/// A call to a named function or closure.
|
||||||
case call = "source.lang.swift.expr.call"
|
case call = "source.lang.swift.expr.call"
|
||||||
|
/// An argument value for a function or closure.
|
||||||
case argument = "source.lang.swift.expr.argument"
|
case argument = "source.lang.swift.expr.argument"
|
||||||
|
/// An Array expression.
|
||||||
case array = "source.lang.swift.expr.array"
|
case array = "source.lang.swift.expr.array"
|
||||||
|
/// A Dictionary expression.
|
||||||
case dictionary = "source.lang.swift.expr.dictionary"
|
case dictionary = "source.lang.swift.expr.dictionary"
|
||||||
|
/// An object literal expression. https://developer.apple.com/swift/blog/?id=33
|
||||||
case objectLiteral = "source.lang.swift.expr.object_literal"
|
case objectLiteral = "source.lang.swift.expr.object_literal"
|
||||||
|
/// A closure expression. https://docs.swift.org/swift-book/LanguageGuide/Closures.html
|
||||||
case closure = "source.lang.swift.expr.closure"
|
case closure = "source.lang.swift.expr.closure"
|
||||||
|
/// A tuple expression. https://docs.swift.org/swift-book/ReferenceManual/Types.html#ID448
|
||||||
case tuple = "source.lang.swift.expr.tuple"
|
case tuple = "source.lang.swift.expr.tuple"
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,6 +180,7 @@ extension SwiftLintFile {
|
||||||
return syntaxKindsByLines
|
return syntaxKindsByLines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invalidates all cached data for this file.
|
||||||
public func invalidateCache() {
|
public func invalidateCache() {
|
||||||
responseCache.invalidate(self)
|
responseCache.invalidate(self)
|
||||||
assertHandlerCache.invalidate(self)
|
assertHandlerCache.invalidate(self)
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
struct RegexHelpers {
|
struct RegexHelpers {
|
||||||
// A single variable
|
/// A single variable
|
||||||
static let varName = "[a-zA-Z_][a-zA-Z0-9_]+"
|
static let varName = "[a-zA-Z_][a-zA-Z0-9_]+"
|
||||||
|
|
||||||
// A single variable in a group (capturable)
|
/// A single variable in a group (capturable)
|
||||||
static let varNameGroup = "\\s*(\(varName))\\s*"
|
static let varNameGroup = "\\s*(\(varName))\\s*"
|
||||||
|
|
||||||
// Two variables (capturables)
|
/// Two variables (capturables)
|
||||||
static let twoVars = "\(varNameGroup),\(varNameGroup)"
|
static let twoVars = "\(varNameGroup),\(varNameGroup)"
|
||||||
|
|
||||||
// A number
|
/// A number
|
||||||
static let number = "[\\-0-9\\.]+"
|
static let number = "[\\-0-9\\.]+"
|
||||||
|
|
||||||
// A variable or a number (capturable)
|
/// A variable or a number (capturable)
|
||||||
static let variableOrNumber = "\\s*(\(varName)|\(number))\\s*"
|
static let variableOrNumber = "\\s*(\(varName)|\(number))\\s*"
|
||||||
|
|
||||||
// Two 'variable or number'
|
/// Two 'variable or number'
|
||||||
static let twoVariableOrNumber = "\(variableOrNumber),\(variableOrNumber)"
|
static let twoVariableOrNumber = "\(variableOrNumber),\(variableOrNumber)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
|
/// The accessibility of a Swift source declaration.
|
||||||
|
///
|
||||||
|
/// - SeeAlso: https://github.com/apple/swift/blob/master/docs/AccessControl.rst
|
||||||
public enum AccessControlLevel: String, CustomStringConvertible {
|
public enum AccessControlLevel: String, CustomStringConvertible {
|
||||||
|
/// Accessible by the declaration's immediate lexical scope.
|
||||||
case `private` = "source.lang.swift.accessibility.private"
|
case `private` = "source.lang.swift.accessibility.private"
|
||||||
|
/// Accessible by the declaration's same file.
|
||||||
case `fileprivate` = "source.lang.swift.accessibility.fileprivate"
|
case `fileprivate` = "source.lang.swift.accessibility.fileprivate"
|
||||||
|
/// Accessible by the declaration's same module, or modules importing it with the `@testable` attribute.
|
||||||
case `internal` = "source.lang.swift.accessibility.internal"
|
case `internal` = "source.lang.swift.accessibility.internal"
|
||||||
|
/// Accessible by the declaration's same program.
|
||||||
case `public` = "source.lang.swift.accessibility.public"
|
case `public` = "source.lang.swift.accessibility.public"
|
||||||
|
/// Accessible and customizable (via subclassing or overrides) by the declaration's same program.
|
||||||
case `open` = "source.lang.swift.accessibility.open"
|
case `open` = "source.lang.swift.accessibility.open"
|
||||||
|
|
||||||
|
/// 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) {
|
internal init?(description value: String) {
|
||||||
switch value {
|
switch value {
|
||||||
case "private": self = .private
|
case "private": self = .private
|
||||||
|
@ -16,7 +27,10 @@ public enum AccessControlLevel: String, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init?(identifier value: String) {
|
/// Initializes an access control level by its SourceKit unique identifier.
|
||||||
|
///
|
||||||
|
/// - parameter value: The value used by SourceKit to refer to this access control level.
|
||||||
|
internal init?(identifier value: String) {
|
||||||
self.init(rawValue: value)
|
self.init(rawValue: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,17 @@ private extension Scanner {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// A SwiftLint-interpretable command to modify SwiftLint's behavior embedded as comments in source code.
|
||||||
public struct Command: Equatable {
|
public struct Command: Equatable {
|
||||||
|
/// The action (verb) that SwiftLint should perform when interpreting this command.
|
||||||
public enum Action: String {
|
public enum Action: String {
|
||||||
|
/// The rule(s) associated with this command should be enabled by the SwiftLint engine.
|
||||||
case enable
|
case enable
|
||||||
|
/// The rule(s) associated with this command should be disabled by the SwiftLint engine.
|
||||||
case disable
|
case disable
|
||||||
|
|
||||||
|
/// Returns the inverse action that can cancel out the current action, restoring the SwifttLint engine's state
|
||||||
|
/// prior to the current action.
|
||||||
internal func inverse() -> Action {
|
internal func inverse() -> Action {
|
||||||
switch self {
|
switch self {
|
||||||
case .enable: return .disable
|
case .enable: return .disable
|
||||||
|
@ -42,14 +48,18 @@ public struct Command: Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The modifier for a command, used to modify its scope.
|
||||||
public enum Modifier: String {
|
public enum Modifier: String {
|
||||||
|
/// The command should only apply to the line preceding its definition.
|
||||||
case previous
|
case previous
|
||||||
|
/// The command should only apply to the same line as its definition.
|
||||||
case this
|
case this
|
||||||
|
/// The command should only apply to the line following its definition.
|
||||||
case next
|
case next
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Text after this delimiter is not considered part of the rule.
|
/// Text after this delimiter is not considered part of the rule.
|
||||||
/// The purpose of this delimiter is to allow swiftlint
|
/// The purpose of this delimiter is to allow SwiftLint
|
||||||
/// commands to be documented in source code.
|
/// commands to be documented in source code.
|
||||||
///
|
///
|
||||||
/// swiftlint:disable:next force_try - Explanation here
|
/// swiftlint:disable:next force_try - Explanation here
|
||||||
|
@ -63,6 +73,15 @@ public struct Command: Equatable {
|
||||||
/// Currently unused but parsed separate from rule identifiers
|
/// Currently unused but parsed separate from rule identifiers
|
||||||
internal let trailingComment: String?
|
internal let trailingComment: String?
|
||||||
|
|
||||||
|
/// Creates a command based on the specified parameters.
|
||||||
|
///
|
||||||
|
/// - parameter action: This command's action.
|
||||||
|
/// - parameter ruleIdentifiers: The identifiers for the rules associated with this command.
|
||||||
|
/// - parameter line: The line in the source file where this command is defined.
|
||||||
|
/// - parameter character: The character offset within the line in the source file where this command is
|
||||||
|
/// defined.
|
||||||
|
/// - parameter modifier: This command's modifier, if any.
|
||||||
|
/// - parameter trailingComment: The comment following this command's `-` delimeter, if any.
|
||||||
public init(action: Action, ruleIdentifiers: Set<RuleIdentifier>, line: Int = 0,
|
public init(action: Action, ruleIdentifiers: Set<RuleIdentifier>, line: Int = 0,
|
||||||
character: Int? = nil, modifier: Modifier? = nil, trailingComment: String? = nil) {
|
character: Int? = nil, modifier: Modifier? = nil, trailingComment: String? = nil) {
|
||||||
self.action = action
|
self.action = action
|
||||||
|
@ -73,6 +92,12 @@ public struct Command: Equatable {
|
||||||
self.trailingComment = trailingComment
|
self.trailingComment = trailingComment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a command based on the specified parameters.
|
||||||
|
///
|
||||||
|
/// - parameter actionString: The string in the command's definition describing its action.
|
||||||
|
/// - parameter line: The line in the source file where this command is defined.
|
||||||
|
/// - parameter character: The character offset within the line in the source file where this command is
|
||||||
|
/// defined.
|
||||||
public init?(actionString: String, line: Int, character: Int) {
|
public init?(actionString: String, line: Int, character: Int) {
|
||||||
let scanner = Scanner(string: actionString)
|
let scanner = Scanner(string: actionString)
|
||||||
_ = scanner.scanString(string: "swiftlint:")
|
_ = scanner.scanString(string: "swiftlint:")
|
||||||
|
@ -117,6 +142,8 @@ public struct Command: Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands the current command into its fully descriptive form without any modifiers.
|
||||||
|
/// If the command doesn't have a modifier, it is returned as-is.
|
||||||
internal func expand() -> [Command] {
|
internal func expand() -> [Command] {
|
||||||
guard let modifier = modifier else {
|
guard let modifier = modifier else {
|
||||||
return [self]
|
return [self]
|
||||||
|
|
|
@ -1,25 +1,40 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// The configuration struct for SwiftLint. User-defined in the `.swiftlint.yml` file, drives the behavior of SwiftLint.
|
||||||
public struct Configuration: Hashable {
|
public struct Configuration: Hashable {
|
||||||
// Represents how a Configuration object can be configured with regards to rules.
|
/// Represents how a Configuration object can be configured with regards to rules.
|
||||||
public enum RulesMode {
|
public enum RulesMode {
|
||||||
|
/// The default rules mode, which will enable all rules that aren't defined as being opt-in
|
||||||
|
/// (conforming to the `OptInRule` protocol), minus the rules listed in `disabled`, plus the rules lised in
|
||||||
|
/// `optIn`.
|
||||||
case `default`(disabled: [String], optIn: [String])
|
case `default`(disabled: [String], optIn: [String])
|
||||||
|
/// Only enable the rules explicitly listed.
|
||||||
case whitelisted([String])
|
case whitelisted([String])
|
||||||
|
/// Enable all available rules.
|
||||||
case allEnabled
|
case allEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The standard file name to look for user-defined configurations.
|
||||||
public static let fileName = ".swiftlint.yml"
|
public static let fileName = ".swiftlint.yml"
|
||||||
|
|
||||||
public let indentation: IndentationStyle // style to use when indenting
|
/// The style to use when indenting Swift source code.
|
||||||
public let included: [String] // included
|
public let indentation: IndentationStyle
|
||||||
public let excluded: [String] // excluded
|
/// Included paths to lint.
|
||||||
public let reporter: String // reporter (xcode, json, csv, checkstyle)
|
public let included: [String]
|
||||||
public let warningThreshold: Int? // warning threshold
|
/// Excluded paths to not lint.
|
||||||
public private(set) var rootPath: String? // the root path to search for nested configurations
|
public let excluded: [String]
|
||||||
public private(set) var configurationPath: String? // if successfully loaded from a path
|
/// The identifier for the `Reporter` to use to report style violations.
|
||||||
|
public let reporter: String
|
||||||
|
/// The threshold for the number of warnings to tolerate before treating the lint as having failed.
|
||||||
|
public let warningThreshold: Int?
|
||||||
|
/// The root directory to search for nested configurations.
|
||||||
|
public private(set) var rootPath: String?
|
||||||
|
/// The absolute path from where this configuration was loaded from, if any.
|
||||||
|
public private(set) var configurationPath: String?
|
||||||
|
/// The location of the persisted cache to use whith this configuration.
|
||||||
public let cachePath: String?
|
public let cachePath: String?
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
@ -45,13 +60,14 @@ public struct Configuration: Hashable {
|
||||||
|
|
||||||
// MARK: Rules Properties
|
// MARK: Rules Properties
|
||||||
|
|
||||||
// All rules enabled in this configuration, derived from disabled, opt-in and whitelist rules
|
/// All rules enabled in this configuration, derived from disabled, opt-in and whitelist rules
|
||||||
public let rules: [Rule]
|
public let rules: [Rule]
|
||||||
|
|
||||||
internal let rulesMode: RulesMode
|
internal let rulesMode: RulesMode
|
||||||
|
|
||||||
// MARK: Initializers
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Creates a `Configuration` by specifying its properties directly.
|
||||||
public init?(rulesMode: RulesMode = .default(disabled: [], optIn: []),
|
public init?(rulesMode: RulesMode = .default(disabled: [], optIn: []),
|
||||||
included: [String] = [],
|
included: [String] = [],
|
||||||
excluded: [String] = [],
|
excluded: [String] = [],
|
||||||
|
@ -126,6 +142,7 @@ public struct Configuration: Hashable {
|
||||||
indentation = configuration.indentation
|
indentation = configuration.indentation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `Configuration` with convenience parameters.
|
||||||
public init(path: String = Configuration.fileName, rootPath: String? = nil,
|
public init(path: String = Configuration.fileName, rootPath: String? = nil,
|
||||||
optional: Bool = true, quiet: Bool = false, enableAllRules: Bool = false,
|
optional: Bool = true, quiet: Bool = false, enableAllRules: Bool = false,
|
||||||
cachePath: String? = nil, customRulesIdentifiers: [String] = []) {
|
cachePath: String? = nil, customRulesIdentifiers: [String] = []) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// All possible configuration errors.
|
||||||
public enum ConfigurationError: Error {
|
public enum ConfigurationError: Error {
|
||||||
|
/// The configuration didn't match internal expectations.
|
||||||
case unknownConfiguration
|
case unknownConfiguration
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
/// A value describing a SwiftLint violation that was corrected.
|
||||||
public struct Correction: Equatable {
|
public struct Correction: Equatable {
|
||||||
|
/// The description of the rule for which this correction was applied.
|
||||||
public let ruleDescription: RuleDescription
|
public let ruleDescription: RuleDescription
|
||||||
|
/// The location of the original violation that was corrected.
|
||||||
public let location: Location
|
public let location: Location
|
||||||
|
|
||||||
|
/// The console-printable description for this correction.
|
||||||
public var consoleDescription: String {
|
public var consoleDescription: String {
|
||||||
return "\(location) Corrected \(ruleDescription.name)"
|
return "\(location) Corrected \(ruleDescription.name)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,13 +110,21 @@ private extension Rule {
|
||||||
|
|
||||||
/// Represents a file that can be linted for style violations and corrections after being collected.
|
/// Represents a file that can be linted for style violations and corrections after being collected.
|
||||||
public struct Linter {
|
public struct Linter {
|
||||||
|
/// The file to lint with this linter.
|
||||||
public let file: SwiftLintFile
|
public let file: SwiftLintFile
|
||||||
|
/// Whether or not this linter will be used to collect information from several files.
|
||||||
public var isCollecting: Bool
|
public var isCollecting: Bool
|
||||||
fileprivate let rules: [Rule]
|
fileprivate let rules: [Rule]
|
||||||
fileprivate let cache: LinterCache?
|
fileprivate let cache: LinterCache?
|
||||||
fileprivate let configuration: Configuration
|
fileprivate let configuration: Configuration
|
||||||
fileprivate let compilerArguments: [String]
|
fileprivate let compilerArguments: [String]
|
||||||
|
|
||||||
|
/// Creates a `Linter` by specifying its properties directly.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file to lint with this linter.
|
||||||
|
/// - parameter configuration: The SwiftLint configuration to apply to this linter.
|
||||||
|
/// - parameter cache: The persisted cache to use for this linter.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments to use for this linter if it is to execute analyzer rules.
|
||||||
public init(file: SwiftLintFile, configuration: Configuration = Configuration()!, cache: LinterCache? = nil,
|
public init(file: SwiftLintFile, configuration: Configuration = Configuration()!, cache: LinterCache? = nil,
|
||||||
compilerArguments: [String] = []) {
|
compilerArguments: [String] = []) {
|
||||||
self.file = file
|
self.file = file
|
||||||
|
@ -147,6 +155,7 @@ public struct Linter {
|
||||||
///
|
///
|
||||||
/// A `CollectedLinter` is only created after a `Linter` has run its collection steps in `Linter.collect(into:)`.
|
/// A `CollectedLinter` is only created after a `Linter` has run its collection steps in `Linter.collect(into:)`.
|
||||||
public struct CollectedLinter {
|
public struct CollectedLinter {
|
||||||
|
/// The file to lint with this linter.
|
||||||
public let file: SwiftLintFile
|
public let file: SwiftLintFile
|
||||||
private let rules: [Rule]
|
private let rules: [Rule]
|
||||||
private let cache: LinterCache?
|
private let cache: LinterCache?
|
||||||
|
@ -161,10 +170,12 @@ public struct CollectedLinter {
|
||||||
compilerArguments = linter.compilerArguments
|
compilerArguments = linter.compilerArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes or retrieves style violations.
|
||||||
public func styleViolations(using storage: RuleStorage) -> [StyleViolation] {
|
public func styleViolations(using storage: RuleStorage) -> [StyleViolation] {
|
||||||
return getStyleViolations(using: storage).0
|
return getStyleViolations(using: storage).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes or retrieves style violations and the time spent executing each rule.
|
||||||
public func styleViolationsAndRuleTimes(using storage: RuleStorage)
|
public func styleViolationsAndRuleTimes(using storage: RuleStorage)
|
||||||
-> ([StyleViolation], [(id: String, time: Double)]) {
|
-> ([StyleViolation], [(id: String, time: Double)]) {
|
||||||
return getStyleViolations(using: storage, benchmark: true)
|
return getStyleViolations(using: storage, benchmark: true)
|
||||||
|
@ -234,6 +245,7 @@ public struct CollectedLinter {
|
||||||
return (cachedViolations, ruleTimes)
|
return (cachedViolations, ruleTimes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies corrections for all rules to this file, returning performed corrections.
|
||||||
public func correct(using storage: RuleStorage) -> [Correction] {
|
public func correct(using storage: RuleStorage) -> [Correction] {
|
||||||
if let violations = cachedStyleViolations()?.0, violations.isEmpty {
|
if let violations = cachedStyleViolations()?.0, violations.isEmpty {
|
||||||
return []
|
return []
|
||||||
|
@ -250,6 +262,10 @@ public struct CollectedLinter {
|
||||||
return corrections
|
return corrections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats the file associated with this linter.
|
||||||
|
///
|
||||||
|
/// - parameter useTabs: Should the file be formatted using tabs?
|
||||||
|
/// - parameter indentWidth: How many spaces should be used per indentation level.
|
||||||
public func format(useTabs: Bool, indentWidth: Int) {
|
public func format(useTabs: Bool, indentWidth: Int) {
|
||||||
let formattedContents = try? file.file.format(trimmingTrailingWhitespace: true,
|
let formattedContents = try? file.file.format(trimmingTrailingWhitespace: true,
|
||||||
useTabs: useTabs,
|
useTabs: useTabs,
|
||||||
|
|
|
@ -16,6 +16,7 @@ private struct FileCache: Codable {
|
||||||
static var empty: FileCache { return FileCache(entries: [:]) }
|
static var empty: FileCache { return FileCache(entries: [:]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A persisted cache for storing and retrieving linter results.
|
||||||
public final class LinterCache {
|
public final class LinterCache {
|
||||||
#if canImport(Darwin) || compiler(>=5.1.0)
|
#if canImport(Darwin) || compiler(>=5.1.0)
|
||||||
private typealias Encoder = PropertyListEncoder
|
private typealias Encoder = PropertyListEncoder
|
||||||
|
@ -44,6 +45,10 @@ public final class LinterCache {
|
||||||
self.swiftVersion = swiftVersion
|
self.swiftVersion = swiftVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `LinterCache` by specifying a SwiftLint configuration and a file manager.
|
||||||
|
///
|
||||||
|
/// - parameter configuration: The SwiftLint configuration for which this cache will be used.
|
||||||
|
/// - parameter fileManager: The file manager to use to read lintable file information.
|
||||||
public init(configuration: Configuration, fileManager: LintableFileManager = FileManager.default) {
|
public init(configuration: Configuration, fileManager: LintableFileManager = FileManager.default) {
|
||||||
location = configuration.cacheURL
|
location = configuration.cacheURL
|
||||||
lazyReadCache = Cache()
|
lazyReadCache = Cache()
|
||||||
|
@ -85,6 +90,7 @@ public final class LinterCache {
|
||||||
return entry.violations
|
return entry.violations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Persists the cache to disk.
|
||||||
public func save() throws {
|
public func save() throws {
|
||||||
guard let url = location else {
|
guard let url = location else {
|
||||||
throw LinterCacheError.noLocation
|
throw LinterCacheError.noLocation
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// The placement of a segment of Swift in a collection of source files.
|
||||||
public struct Location: CustomStringConvertible, Comparable, Codable {
|
public struct Location: CustomStringConvertible, Comparable, Codable {
|
||||||
|
/// The file path on disk for this location.
|
||||||
public let file: String?
|
public let file: String?
|
||||||
|
/// The line offset in the file for this location. 1-indexed.
|
||||||
public let line: Int?
|
public let line: Int?
|
||||||
|
/// The character offset in the file for this location. 1-indexed.
|
||||||
public let character: Int?
|
public let character: Int?
|
||||||
|
|
||||||
|
/// A lossless printable description of this location.
|
||||||
public var description: String {
|
public var description: String {
|
||||||
// Xcode likes warnings and errors in the following format:
|
// Xcode likes warnings and errors in the following format:
|
||||||
// {full_path_to_file}{:line}{:character}: {error,warning}: {content}
|
// {full_path_to_file}{:line}{:character}: {error,warning}: {content}
|
||||||
|
@ -13,16 +19,28 @@ public struct Location: CustomStringConvertible, Comparable, Codable {
|
||||||
let charString: String = ":\(character ?? 1)"
|
let charString: String = ":\(character ?? 1)"
|
||||||
return [fileString, lineString, charString].joined()
|
return [fileString, lineString, charString].joined()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The file path for this location relative to the current working directory.
|
||||||
public var relativeFile: String? {
|
public var relativeFile: String? {
|
||||||
return file?.replacingOccurrences(of: FileManager.default.currentDirectoryPath + "/", with: "")
|
return file?.replacingOccurrences(of: FileManager.default.currentDirectoryPath + "/", with: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `Location` by specifying its properties directly.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file path on disk for this location.
|
||||||
|
/// - parameter line: The line offset in the file for this location. 1-indexed.
|
||||||
|
/// - parameter character: The character offset in the file for this location. 1-indexed.
|
||||||
public init(file: String?, line: Int? = nil, character: Int? = nil) {
|
public init(file: String?, line: Int? = nil, character: Int? = nil) {
|
||||||
self.file = file
|
self.file = file
|
||||||
self.line = line
|
self.line = line
|
||||||
self.character = character
|
self.character = character
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// - 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: Int) {
|
||||||
self.file = file.path
|
self.file = file.path
|
||||||
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
|
if let lineAndCharacter = file.stringView.lineAndCharacter(forByteOffset: offset) {
|
||||||
|
@ -34,6 +52,11 @@ public struct Location: CustomStringConvertible, Comparable, Codable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `Location` based on a `SwiftLintFile` and a UTF8 character-offset into the file.
|
||||||
|
/// Fails if tthe specified offset was not a valid location in the file.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for this location.
|
||||||
|
/// - parameter offset: The offset in UTF8 fragments into the file for this location.
|
||||||
public init(file: SwiftLintFile, characterOffset offset: Int) {
|
public init(file: SwiftLintFile, characterOffset offset: Int) {
|
||||||
self.file = file.path
|
self.file = file.path
|
||||||
if let lineAndCharacter = file.stringView.lineAndCharacter(forCharacterOffset: offset) {
|
if let lineAndCharacter = file.stringView.lineAndCharacter(forCharacterOffset: offset) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Generated using Sourcery 0.17.0 — https://github.com/krzysztofzablocki/Sourcery
|
// Generated using Sourcery 0.17.0 — https://github.com/krzysztofzablocki/Sourcery
|
||||||
// DO NOT EDIT
|
// DO NOT EDIT
|
||||||
|
|
||||||
|
/// The rule list containing all available rules built into SwiftLint.
|
||||||
public let masterRuleList = RuleList(rules: [
|
public let masterRuleList = RuleList(rules: [
|
||||||
AnyObjectProtocolRule.self,
|
AnyObjectProtocolRule.self,
|
||||||
ArrayInitRule.self,
|
ArrayInitRule.self,
|
||||||
|
|
|
@ -1,22 +1,42 @@
|
||||||
|
/// A contiguous region of Swift source code.
|
||||||
public struct Region: Equatable {
|
public struct Region: Equatable {
|
||||||
|
/// The location describing the start of the region. All locations that are less than this value
|
||||||
|
/// (earlier in the source file) are not contained in this region.
|
||||||
public let start: Location
|
public let start: Location
|
||||||
|
/// The location describing the end of the region. All locations that are greater than this value
|
||||||
|
/// (later in the source file) are not contained in this region.
|
||||||
public let end: Location
|
public let end: Location
|
||||||
|
/// All SwiftLint rule identifiers that are disabled in this region.
|
||||||
public let disabledRuleIdentifiers: Set<RuleIdentifier>
|
public let disabledRuleIdentifiers: Set<RuleIdentifier>
|
||||||
|
|
||||||
|
/// Creates a Region by setting explicit values for all its properties.
|
||||||
|
///
|
||||||
|
/// - parameter start: The region's starting location.
|
||||||
|
/// - parameter end: The region's ending location.
|
||||||
|
/// - parameter disabledRuleIdentifiers: All SwiftLint rule identifiers that are disabled in this region.
|
||||||
public init(start: Location, end: Location, disabledRuleIdentifiers: Set<RuleIdentifier>) {
|
public init(start: Location, end: Location, disabledRuleIdentifiers: Set<RuleIdentifier>) {
|
||||||
self.start = start
|
self.start = start
|
||||||
self.end = end
|
self.end = end
|
||||||
self.disabledRuleIdentifiers = disabledRuleIdentifiers
|
self.disabledRuleIdentifiers = disabledRuleIdentifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the specific location is contained in this region.
|
||||||
|
///
|
||||||
|
/// - parameter location: The location to check for containment.
|
||||||
public func contains(_ location: Location) -> Bool {
|
public func contains(_ location: Location) -> Bool {
|
||||||
return start <= location && end >= location
|
return start <= location && end >= location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the specified rule is enabled in this region.
|
||||||
|
///
|
||||||
|
/// - parameter rule: The rule whose status should be determined.
|
||||||
public func isRuleEnabled(_ rule: Rule) -> Bool {
|
public func isRuleEnabled(_ rule: Rule) -> Bool {
|
||||||
return !isRuleDisabled(rule)
|
return !isRuleDisabled(rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the specified rule is disabled in this region.
|
||||||
|
///
|
||||||
|
/// - parameter rule: The rule whose status should be determined.
|
||||||
public func isRuleDisabled(_ rule: Rule) -> Bool {
|
public func isRuleDisabled(_ rule: Rule) -> Bool {
|
||||||
guard !disabledRuleIdentifiers.contains(.all) else {
|
guard !disabledRuleIdentifiers.contains(.all) else {
|
||||||
return true
|
return true
|
||||||
|
@ -27,6 +47,10 @@ public struct Region: Equatable {
|
||||||
return !regionIdentifiers.isDisjoint(with: identifiersToCheck)
|
return !regionIdentifiers.isDisjoint(with: identifiersToCheck)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the deprecated rule aliases that are disabling the specified rule in this region.
|
||||||
|
/// Returns the empty set if the rule isn't disabled in this region.
|
||||||
|
///
|
||||||
|
/// - parameter rule: The rule to check.
|
||||||
public func deprecatedAliasesDisabling(rule: Rule) -> Set<String> {
|
public func deprecatedAliasesDisabling(rule: Rule) -> Set<String> {
|
||||||
let identifiers = type(of: rule).description.deprecatedAliases
|
let identifiers = type(of: rule).description.deprecatedAliases
|
||||||
return Set(disabledRuleIdentifiers.map { $0.stringRepresentation }).intersection(identifiers)
|
return Set(disabledRuleIdentifiers.map { $0.stringRepresentation }).intersection(identifiers)
|
||||||
|
|
|
@ -1,21 +1,65 @@
|
||||||
|
/// A detailed description for a SwiftLint rule. Used for both documentation and testing purposes.
|
||||||
public struct RuleDescription: Equatable, Codable {
|
public struct RuleDescription: Equatable, Codable {
|
||||||
|
/// The rule's unique identifier, to be used in configuration files and SwiftLint commands.
|
||||||
|
/// Should be short and only comprised of lowercase latin alphabet letters and underscores formatted in snake case.
|
||||||
public let identifier: String
|
public let identifier: String
|
||||||
|
|
||||||
|
/// The rule's human-readable name. Should be short, descriptive and formatted in Title Case. May contain spaces.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
|
/// The rule's verbose description. Should read as a sentence or short paragraph. Good things to include are an
|
||||||
|
/// explanation of the rule's purpose and rationale.
|
||||||
public let description: String
|
public let description: String
|
||||||
|
|
||||||
|
/// The `RuleKind` that best categorizes this rule.
|
||||||
public let kind: RuleKind
|
public let kind: RuleKind
|
||||||
|
|
||||||
|
/// Swift source examples that do not trigger a violation for this rule. Used for documentation purposes to inform
|
||||||
|
/// users of various samples of code that are considered valid by this rule. Should be valid Swift syntax but is not
|
||||||
|
/// required to compile.
|
||||||
|
///
|
||||||
|
/// These examples are also used for testing purposes if the rule conforms to `AutomaticTestableRule`. Tests will
|
||||||
|
/// validate that the rule does not trigger any violations for these examples.
|
||||||
public let nonTriggeringExamples: [String]
|
public let nonTriggeringExamples: [String]
|
||||||
|
|
||||||
|
/// Swift source examples that do trigger one or more violations for this rule. Used for documentation purposes to
|
||||||
|
/// inform users of various samples of code that are considered invalid by this rule. Should be valid Swift syntax
|
||||||
|
/// but is not required to compile.
|
||||||
|
///
|
||||||
|
/// Violations should occur where `↓` markers are located.
|
||||||
|
///
|
||||||
|
/// These examples are also used for testing purposes if the rule conforms to `AutomaticTestableRule`. Tests will
|
||||||
|
/// validate that the rule triggers violations for these examples wherever `↓` markers are located.
|
||||||
public let triggeringExamples: [String]
|
public let triggeringExamples: [String]
|
||||||
|
|
||||||
|
/// Pairs of Swift source examples, where keys are examples that trigger violations for this rule, and the values
|
||||||
|
/// are the expected value after applying corrections with the rule.
|
||||||
|
///
|
||||||
|
/// Rules that aren't correctable (conforming to the `CorrectableRule` protocol) should leave property empty.
|
||||||
|
///
|
||||||
|
/// These examples are used for testing purposes if the rule conforms to `AutomaticTestableRule`. Tests will
|
||||||
|
/// validate that the rule corrects all keys to their corresponding values.
|
||||||
public let corrections: [String: String]
|
public let corrections: [String: String]
|
||||||
|
|
||||||
|
/// Any previous iteration of the rule's identifier that was previously shipped with SwiftLint.
|
||||||
public let deprecatedAliases: Set<String>
|
public let deprecatedAliases: Set<String>
|
||||||
|
|
||||||
|
/// The oldest version of the Swift compiler supported by this rule.
|
||||||
public let minSwiftVersion: SwiftVersion
|
public let minSwiftVersion: SwiftVersion
|
||||||
|
|
||||||
|
/// Whether or not this rule can only be executed on a file physically on-disk. Typically necessary for rules
|
||||||
|
/// conforming to `AnalyzerRule`.
|
||||||
public let requiresFileOnDisk: Bool
|
public let requiresFileOnDisk: Bool
|
||||||
|
|
||||||
|
/// The console-printable string for this description.
|
||||||
public var consoleDescription: String { return "\(name) (\(identifier)): \(description)" }
|
public var consoleDescription: String { return "\(name) (\(identifier)): \(description)" }
|
||||||
|
|
||||||
|
/// All identifiers that have been used to uniquely identify this rule in past and current SwiftLint versions.
|
||||||
public var allIdentifiers: [String] {
|
public var allIdentifiers: [String] {
|
||||||
return Array(deprecatedAliases) + [identifier]
|
return Array(deprecatedAliases) + [identifier]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `RuleDescription` by specifying all its properties directly.
|
||||||
public init(identifier: String, name: String, description: String, kind: RuleKind,
|
public init(identifier: String, name: String, description: String, kind: RuleKind,
|
||||||
minSwiftVersion: SwiftVersion = .three,
|
minSwiftVersion: SwiftVersion = .three,
|
||||||
nonTriggeringExamples: [String] = [], triggeringExamples: [String] = [],
|
nonTriggeringExamples: [String] = [], triggeringExamples: [String] = [],
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
|
/// An identifier representing a SwiftLint rule, or all rules.
|
||||||
public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral {
|
public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral {
|
||||||
|
// MARK: - Values
|
||||||
|
|
||||||
|
/// Special identifier that should be treated as referring to 'all' SwiftLint rules. One helpful usecase is in
|
||||||
|
/// disabling all SwiftLint rules in a given file by adding a `// swiftlint:disable all` comment at the top of the
|
||||||
|
/// file.
|
||||||
case all
|
case all
|
||||||
|
|
||||||
|
/// Represents a single SwiftLint rule with the specified identifier.
|
||||||
case single(identifier: String)
|
case single(identifier: String)
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
private static let allStringRepresentation = "all"
|
private static let allStringRepresentation = "all"
|
||||||
|
|
||||||
|
/// The spelling of the string for this idenfitier.
|
||||||
public var stringRepresentation: String {
|
public var stringRepresentation: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .all:
|
case .all:
|
||||||
|
@ -14,10 +25,17 @@ public enum RuleIdentifier: Hashable, ExpressibleByStringLiteral {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
|
||||||
|
/// Creates a `RuleIdentifier` by its string representation.
|
||||||
|
///
|
||||||
|
/// - parameter value: The string representation.
|
||||||
public init(_ value: String) {
|
public init(_ value: String) {
|
||||||
self = value == RuleIdentifier.allStringRepresentation ? .all : .single(identifier: value)
|
self = value == RuleIdentifier.allStringRepresentation ? .all : .single(identifier: value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - ExpressibleByStringLiteral Conformance
|
||||||
|
|
||||||
public init(stringLiteral value: String) {
|
public init(stringLiteral value: String) {
|
||||||
self = RuleIdentifier(value)
|
self = RuleIdentifier(value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
|
/// All the possible rule kinds (categories).
|
||||||
public enum RuleKind: String, Codable {
|
public enum RuleKind: String, Codable {
|
||||||
|
/// Describes rules that validate Swift source conventions.
|
||||||
case lint
|
case lint
|
||||||
|
/// Describes rules that validate common practices in the Swift community.
|
||||||
case idiomatic
|
case idiomatic
|
||||||
|
/// Describes rules that validate stylistic choices.
|
||||||
case style
|
case style
|
||||||
|
/// Describes rules that validate magnitudes or measurements of Swift source.
|
||||||
case metrics
|
case metrics
|
||||||
|
/// Describes rules that validate that code patterns with poor performance are avoided.
|
||||||
case performance
|
case performance
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
|
/// All possible configuration errors.
|
||||||
public enum RuleListError: Error {
|
public enum RuleListError: Error {
|
||||||
|
/// The rule list contains more than one configuration for the specified rule.
|
||||||
case duplicatedConfigurations(rule: Rule.Type)
|
case duplicatedConfigurations(rule: Rule.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of available SwiftLint rules.
|
||||||
public struct RuleList {
|
public struct RuleList {
|
||||||
|
/// The rules contained in this list.
|
||||||
public let list: [String: Rule.Type]
|
public let list: [String: Rule.Type]
|
||||||
private let aliases: [String: String]
|
private let aliases: [String: String]
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
|
||||||
|
/// Creates a `RuleList` by specifying all its rules.
|
||||||
|
///
|
||||||
|
/// - parameter rules: The rules to be contained in this list.
|
||||||
public init(rules: Rule.Type...) {
|
public init(rules: Rule.Type...) {
|
||||||
self.init(rules: rules)
|
self.init(rules: rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `RuleList` by specifying all its rules.
|
||||||
|
///
|
||||||
|
/// - parameter rules: The rules to be contained in this list.
|
||||||
public init(rules: [Rule.Type]) {
|
public init(rules: [Rule.Type]) {
|
||||||
var tmpList = [String: Rule.Type]()
|
var tmpList = [String: Rule.Type]()
|
||||||
var tmpAliases = [String: String]()
|
var tmpAliases = [String: String]()
|
||||||
|
@ -26,6 +38,8 @@ public struct RuleList {
|
||||||
aliases = tmpAliases
|
aliases = tmpAliases
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal
|
||||||
|
|
||||||
internal func configuredRules(with dictionary: [String: Any]) throws -> [Rule] {
|
internal func configuredRules(with dictionary: [String: Any]) throws -> [Rule] {
|
||||||
var rules = [String: Rule]()
|
var rules = [String: Rule]()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
|
/// A configuration parameter for rules.
|
||||||
public struct RuleParameter<T: Equatable>: Equatable {
|
public struct RuleParameter<T: Equatable>: Equatable {
|
||||||
|
/// The severity that should be assigned to the violation of this parameter's value is met.
|
||||||
public let severity: ViolationSeverity
|
public let severity: ViolationSeverity
|
||||||
|
/// The value to configure the rule.
|
||||||
public let value: T
|
public let value: T
|
||||||
|
|
||||||
|
/// Creates a `RuleParameter` by specifying its properties directly.
|
||||||
|
///
|
||||||
|
/// - parameter severity: The severity that should be assigned to the violation of this parameter's value is met.
|
||||||
|
/// - parameter value: The value to configure the rule.
|
||||||
public init(severity: ViolationSeverity, value: T) {
|
public init(severity: ViolationSeverity, value: T) {
|
||||||
self.severity = severity
|
self.severity = severity
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
import Dispatch
|
import Dispatch
|
||||||
|
|
||||||
|
/// A storage mechanism for aggregating the results of `CollectingRule`s.
|
||||||
public class RuleStorage {
|
public class RuleStorage {
|
||||||
private var storage: [ObjectIdentifier: [SwiftLintFile: Any]]
|
private var storage: [ObjectIdentifier: [SwiftLintFile: Any]]
|
||||||
private let access = DispatchQueue(label: "io.realm.swiftlint.ruleStorageAccess", attributes: .concurrent)
|
private let access = DispatchQueue(label: "io.realm.swiftlint.ruleStorageAccess", attributes: .concurrent)
|
||||||
|
|
||||||
|
/// Creates a `RuleStorage` with no initial stored data.
|
||||||
public init() {
|
public init() {
|
||||||
storage = [:]
|
storage = [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collects file info for a given rule into the storage.s
|
||||||
|
///
|
||||||
|
/// - parameter info: The file information to store.
|
||||||
|
/// - parameter file: The file for which this information pertains to.
|
||||||
|
/// - parameter rule: The SwiftLint rule that generated this info.
|
||||||
func collect<R: CollectingRule>(info: R.FileInfo, for file: SwiftLintFile, in rule: R) {
|
func collect<R: CollectingRule>(info: R.FileInfo, for file: SwiftLintFile, in rule: R) {
|
||||||
let key = ObjectIdentifier(R.self)
|
let key = ObjectIdentifier(R.self)
|
||||||
access.sync(flags: .barrier) {
|
access.sync(flags: .barrier) {
|
||||||
|
@ -15,6 +22,9 @@ public class RuleStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves all file information for a given rule that was collected via `collect(...)`.
|
||||||
|
///
|
||||||
|
/// - parameter rule: The rule whose collected information should be retrieved.
|
||||||
func collectedInfo<R: CollectingRule>(for rule: R) -> [SwiftLintFile: R.FileInfo]? {
|
func collectedInfo<R: CollectingRule>(for rule: R) -> [SwiftLintFile: R.FileInfo]? {
|
||||||
return access.sync {
|
return access.sync {
|
||||||
storage[ObjectIdentifier(R.self)] as? [SwiftLintFile: R.FileInfo]
|
storage[ObjectIdentifier(R.self)] as? [SwiftLintFile: R.FileInfo]
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
|
/// A value describing an instance of Swift source code that is considered invalid by a SwiftLint rule.
|
||||||
public struct StyleViolation: CustomStringConvertible, Equatable, Codable {
|
public struct StyleViolation: CustomStringConvertible, Equatable, Codable {
|
||||||
|
/// The description of the rule that generated this violation.
|
||||||
public let ruleDescription: RuleDescription
|
public let ruleDescription: RuleDescription
|
||||||
|
|
||||||
|
/// The severity of this violation.
|
||||||
public let severity: ViolationSeverity
|
public let severity: ViolationSeverity
|
||||||
|
|
||||||
|
/// The location of this violation.
|
||||||
public let location: Location
|
public let location: Location
|
||||||
|
|
||||||
|
/// The justification for this violation.
|
||||||
public let reason: String
|
public let reason: String
|
||||||
|
|
||||||
|
/// A printable description for this violation.
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return XcodeReporter.generateForSingleViolation(self)
|
return XcodeReporter.generateForSingleViolation(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `StyleViolation` by specifying its properties directly.
|
||||||
|
///
|
||||||
|
/// - parameter ruleDescription: The description of the rule that generated this violation.
|
||||||
|
/// - parameter severity: The severity of this violation.
|
||||||
|
/// - parameter location: The location of this violation.
|
||||||
|
/// - parameter reason: The justification for this violation.
|
||||||
public init(ruleDescription: RuleDescription, severity: ViolationSeverity = .warning,
|
public init(ruleDescription: RuleDescription, severity: ViolationSeverity = .warning,
|
||||||
location: Location, reason: String? = nil) {
|
location: Location, reason: String? = nil) {
|
||||||
self.ruleDescription = ruleDescription
|
self.ruleDescription = ruleDescription
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// A unit of Swift source code, either on disk or in memory.
|
||||||
public final class SwiftLintFile {
|
public final class SwiftLintFile {
|
||||||
private static var id = 0
|
private static var id = 0
|
||||||
private static var lock = NSLock()
|
private static var lock = NSLock()
|
||||||
|
|
||||||
private static func nextId () -> Int {
|
private static func nextID () -> Int {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer { lock.unlock() }
|
defer { lock.unlock() }
|
||||||
id += 1
|
id += 1
|
||||||
|
@ -15,41 +16,61 @@ public final class SwiftLintFile {
|
||||||
let file: File
|
let file: File
|
||||||
let id: Int
|
let id: Int
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintFile` with a SourceKitten `File`.
|
||||||
|
///
|
||||||
|
/// - parameter file: A file from SourceKitten.
|
||||||
public init(file: File) {
|
public init(file: File) {
|
||||||
self.file = file
|
self.file = file
|
||||||
self.id = SwiftLintFile.nextId()
|
self.id = SwiftLintFile.nextID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintFile` by specifying its path on disk.
|
||||||
|
/// Fails if the file does not exist.
|
||||||
|
///
|
||||||
|
/// - parameter path: The path to a file on disk. Relative and absolute paths supported.
|
||||||
public convenience init?(path: String) {
|
public convenience init?(path: String) {
|
||||||
guard let file = File(path: path) else { return nil }
|
guard let file = File(path: path) else { return nil }
|
||||||
self.init(file: file)
|
self.init(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintFile` by specifying its path on disk. Unlike the `SwiftLintFile(path:)` initializer, this
|
||||||
|
/// one does not read its contents immediately, but rather traps at runtime when attempting to access its contents.
|
||||||
|
///
|
||||||
|
/// - parameter path: The path to a file on disk. Relative and absolute paths supported.
|
||||||
public convenience init(pathDeferringReading path: String) {
|
public convenience init(pathDeferringReading path: String) {
|
||||||
self.init(file: File(pathDeferringReading: path))
|
self.init(file: File(pathDeferringReading: path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintFile` that is not backed by a file on disk by specifying its contents.
|
||||||
|
///
|
||||||
|
/// - parameter contents: The contents of the file.
|
||||||
public convenience init(contents: String) {
|
public convenience init(contents: String) {
|
||||||
self.init(file: File(contents: contents))
|
self.init(file: File(contents: contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The path on disk for this file.
|
||||||
public var path: String? {
|
public var path: String? {
|
||||||
return file.path
|
return file.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The file's contents.
|
||||||
public var contents: String {
|
public var contents: String {
|
||||||
return file.contents
|
return file.contents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A string view into the contents of this file optimized for string manipulation operations.
|
||||||
public var stringView: StringView {
|
public var stringView: StringView {
|
||||||
return file.stringView
|
return file.stringView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The parsed lines for this file's contents.
|
||||||
public var lines: [Line] {
|
public var lines: [Line] {
|
||||||
return file.lines
|
return file.lines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Hashable Conformance
|
||||||
|
|
||||||
extension SwiftLintFile: Hashable {
|
extension SwiftLintFile: Hashable {
|
||||||
public static func == (lhs: SwiftLintFile, rhs: SwiftLintFile) -> Bool {
|
public static func == (lhs: SwiftLintFile, rhs: SwiftLintFile) -> Bool {
|
||||||
return lhs.id == rhs.id
|
return lhs.id == rhs.id
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// Represents a Swift file's syntax information.
|
||||||
public struct SwiftLintSyntaxMap {
|
public struct SwiftLintSyntaxMap {
|
||||||
|
/// The raw `SyntaxMap` obtained by SourceKitten.
|
||||||
public let value: SyntaxMap
|
public let value: SyntaxMap
|
||||||
|
|
||||||
|
/// The SwiftLint-specific syntax tokens for this syntax map.
|
||||||
public let tokens: [SwiftLintSyntaxToken]
|
public let tokens: [SwiftLintSyntaxToken]
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintSyntaxMap` from the raw `SyntaxMap` obtained by SourceKitten.
|
||||||
|
///
|
||||||
|
/// - arameter value: The raw `SyntaxMap` obtained by SourceKitten.
|
||||||
public init(value: SyntaxMap) {
|
public init(value: SyntaxMap) {
|
||||||
self.value = value
|
self.value = value
|
||||||
self.tokens = value.tokens.map(SwiftLintSyntaxToken.init)
|
self.tokens = value.tokens.map(SwiftLintSyntaxToken.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns array of SyntaxTokens intersecting with byte range
|
/// Returns array of SyntaxTokens intersecting with byte range.
|
||||||
///
|
///
|
||||||
/// - Parameter byteRange: byte based NSRange
|
/// - parameter byteRange: Byte-based NSRange.
|
||||||
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
|
internal func tokens(inByteRange byteRange: NSRange) -> [SwiftLintSyntaxToken] {
|
||||||
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
|
func intersect(_ token: SwiftLintSyntaxToken) -> Bool {
|
||||||
return token.range.intersects(byteRange)
|
return token.range.intersects(byteRange)
|
||||||
|
@ -35,6 +42,9 @@ public struct SwiftLintSyntaxMap {
|
||||||
return Array(tokensAfterFirstIntersection)
|
return Array(tokensAfterFirstIntersection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the syntax kinds in the specified byte range.
|
||||||
|
///
|
||||||
|
/// - parameter byteRange: Byte-based NSRange.
|
||||||
internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
|
internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] {
|
||||||
return tokens(inByteRange: byteRange).compactMap { $0.kind }
|
return tokens(inByteRange: byteRange).compactMap { $0.kind }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,33 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// A SwiftLint-aware Swift syntax token.
|
||||||
public struct SwiftLintSyntaxToken {
|
public struct SwiftLintSyntaxToken {
|
||||||
|
/// The raw `SyntaxToken` obtained by SourceKitten.
|
||||||
public let value: SyntaxToken
|
public let value: SyntaxToken
|
||||||
|
|
||||||
|
/// The syntax kind associated with is token.
|
||||||
public let kind: SyntaxKind?
|
public let kind: SyntaxKind?
|
||||||
|
|
||||||
|
/// Creates a `SwiftLintSyntaxToken` from the raw `SyntaxToken` obtained by SourceKitten.
|
||||||
|
///
|
||||||
|
/// - parameter value: The raw `SyntaxToken` obtained by SourceKitten.
|
||||||
public init(value: SyntaxToken) {
|
public init(value: SyntaxToken) {
|
||||||
self.value = value
|
self.value = value
|
||||||
kind = SyntaxKind(rawValue: value.type)
|
kind = SyntaxKind(rawValue: value.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The byte range in a source file for this token.
|
||||||
public var range: NSRange {
|
public var range: NSRange {
|
||||||
return NSRange(location: value.offset, length: value.length)
|
return NSRange(location: value.offset, length: value.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The starting byte offset in a source file for this token.
|
||||||
public var offset: Int {
|
public var offset: Int {
|
||||||
return value.offset
|
return value.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The length in bytes for this token.
|
||||||
public var length: Int {
|
public var length: Int {
|
||||||
return value.length
|
return value.length
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
public struct SwiftVersion: RawRepresentable, Codable {
|
/// A value describing the version of the Swift compiler.
|
||||||
|
public struct SwiftVersion: RawRepresentable, Codable, Comparable {
|
||||||
public typealias RawValue = String
|
public typealias RawValue = String
|
||||||
|
|
||||||
public let rawValue: String
|
public let rawValue: String
|
||||||
|
@ -9,23 +10,29 @@ public struct SwiftVersion: RawRepresentable, Codable {
|
||||||
public init(rawValue: String) {
|
public init(rawValue: String) {
|
||||||
self.rawValue = rawValue
|
self.rawValue = rawValue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension SwiftVersion: Comparable {
|
|
||||||
// Comparable
|
|
||||||
public static func < (lhs: SwiftVersion, rhs: SwiftVersion) -> Bool {
|
public static func < (lhs: SwiftVersion, rhs: SwiftVersion) -> Bool {
|
||||||
return lhs.rawValue < rhs.rawValue
|
return lhs.rawValue < rhs.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension SwiftVersion {
|
public extension SwiftVersion {
|
||||||
|
/// Swift 3.x - https://swift.org/download/#swift-30
|
||||||
static let three = SwiftVersion(rawValue: "3.0.0")
|
static let three = SwiftVersion(rawValue: "3.0.0")
|
||||||
|
/// Swift 4.0.x - https://swift.org/download/#swift-40
|
||||||
static let four = SwiftVersion(rawValue: "4.0.0")
|
static let four = SwiftVersion(rawValue: "4.0.0")
|
||||||
|
/// Swift 4.1.x - https://swift.org/download/#swift-41
|
||||||
static let fourDotOne = SwiftVersion(rawValue: "4.1.0")
|
static let fourDotOne = SwiftVersion(rawValue: "4.1.0")
|
||||||
|
/// Swift 4.2.x - https://swift.org/download/#swift-42
|
||||||
static let fourDotTwo = SwiftVersion(rawValue: "4.2.0")
|
static let fourDotTwo = SwiftVersion(rawValue: "4.2.0")
|
||||||
|
/// Swift 5.0.x - https://swift.org/download/#swift-50
|
||||||
static let five = SwiftVersion(rawValue: "5.0.0")
|
static let five = SwiftVersion(rawValue: "5.0.0")
|
||||||
|
/// Swift 5.1.x - https://swift.org/download/#swift-51
|
||||||
static let fiveDotOne = SwiftVersion(rawValue: "5.1.0")
|
static let fiveDotOne = SwiftVersion(rawValue: "5.1.0")
|
||||||
|
|
||||||
|
/// The current detected Swift compiler version, based on the currently accessible SourceKit version.
|
||||||
|
///
|
||||||
|
/// - note: Override by setting the `SWIFTLINT_SWIFT_VERSION` environment variable.
|
||||||
static let current: SwiftVersion = {
|
static let current: SwiftVersion = {
|
||||||
// Allow forcing the Swift version, useful in cases where SourceKit isn't available
|
// Allow forcing the Swift version, useful in cases where SourceKit isn't available
|
||||||
if let envVersion = ProcessInfo.processInfo.environment["SWIFTLINT_SWIFT_VERSION"] {
|
if let envVersion = ProcessInfo.processInfo.environment["SWIFTLINT_SWIFT_VERSION"] {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
/// A type describing the SwiftLint version.
|
||||||
public struct Version {
|
public struct Version {
|
||||||
|
/// The string value for this version.
|
||||||
public let value: String
|
public let value: String
|
||||||
|
|
||||||
|
/// The current SwiftLint version.
|
||||||
public static let current = Version(value: "0.38.1")
|
public static let current = Version(value: "0.38.1")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
/// The magnitude of a `StyleViolation`.
|
||||||
public enum ViolationSeverity: String, Comparable, Codable {
|
public enum ViolationSeverity: String, Comparable, Codable {
|
||||||
|
/// Non-fatal. If using SwiftLint as an Xcode build phase, Xcode will mark the build as having succeeded.
|
||||||
case warning
|
case warning
|
||||||
|
/// Fatal. If using SwiftLint as an Xcode build phase, Xcode will mark the build as having failed.
|
||||||
case error
|
case error
|
||||||
|
|
||||||
// MARK: Comparable
|
// MARK: Comparable
|
||||||
|
|
|
@ -9,7 +9,12 @@ internal enum YamlParserError: Error, Equatable {
|
||||||
|
|
||||||
// MARK: - YamlParser
|
// MARK: - YamlParser
|
||||||
|
|
||||||
|
/// An interface for parsing YAML.
|
||||||
public struct YamlParser {
|
public struct YamlParser {
|
||||||
|
/// Parses the input YAML string as an untyped dictionary.
|
||||||
|
///
|
||||||
|
/// - parameter yaml: YAML-formatted string.
|
||||||
|
/// - parameter env: The environment to use to expand variables in the YAML.
|
||||||
public static func parse(_ yaml: String,
|
public static func parse(_ yaml: String,
|
||||||
env: [String: String] = ProcessInfo.processInfo.environment) throws -> [String: Any] {
|
env: [String: String] = ProcessInfo.processInfo.environment) throws -> [String: Any] {
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// A rule that leverages the Swift source's pre-typechecked Abstract Syntax Tree to recurse into the source's
|
||||||
|
/// structure, validating the rule recursively in nested source bodies.
|
||||||
public protocol ASTRule: Rule {
|
public protocol ASTRule: Rule {
|
||||||
|
/// The kind of token being recursed over.
|
||||||
associatedtype KindType: RawRepresentable
|
associatedtype KindType: RawRepresentable
|
||||||
|
|
||||||
|
/// Executes the rule on a file and a subset of its AST structure, returnng any violations to the rule's
|
||||||
|
/// expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter kind: The kind of token being recursed over.
|
||||||
|
/// - parameter dictionary: The dicttionary for an AST subset to validate.
|
||||||
func validate(file: SwiftLintFile, kind: KindType, dictionary: SourceKittenDictionary) -> [StyleViolation]
|
func validate(file: SwiftLintFile, kind: KindType, dictionary: SourceKittenDictionary) -> [StyleViolation]
|
||||||
|
|
||||||
|
/// Get the `kind` from the specified dictionary.
|
||||||
func kind(from dictionary: SourceKittenDictionary) -> KindType?
|
func kind(from dictionary: SourceKittenDictionary) -> KindType?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +23,11 @@ public extension ASTRule {
|
||||||
return validate(file: file, dictionary: file.structureDictionary)
|
return validate(file: file, dictionary: file.structureDictionary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the rule on a file and a subset of its AST structure, returnng any violations to the rule's
|
||||||
|
/// expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter dictionary: The dicttionary for an AST subset to validate.
|
||||||
func validate(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
func validate(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> [StyleViolation] {
|
||||||
return dictionary.traverseDepthFirst { subDict in
|
return dictionary.traverseDepthFirst { subDict in
|
||||||
guard let kind = self.kind(from: subDict) else { return nil }
|
guard let kind = self.kind(from: subDict) else { return nil }
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
|
/// An interface for reporting violations as strings.
|
||||||
public protocol Reporter: CustomStringConvertible {
|
public protocol Reporter: CustomStringConvertible {
|
||||||
|
/// The unique identifier for this reporter.
|
||||||
static var identifier: String { get }
|
static var identifier: String { get }
|
||||||
|
|
||||||
|
/// Whether or not this reporter can output incrementally as violations are found or if all violations must be
|
||||||
|
/// collected before generating the report.
|
||||||
static var isRealtime: Bool { get }
|
static var isRealtime: Bool { get }
|
||||||
|
|
||||||
|
/// Return a string with the report for the specified violations.
|
||||||
|
///
|
||||||
|
/// - parameter violations: The violations to report.
|
||||||
|
///
|
||||||
|
/// - returns: The report.
|
||||||
static func generateReport(_ violations: [StyleViolation]) -> String
|
static func generateReport(_ violations: [StyleViolation]) -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the reporter with the specified identifier. Traps if the specified identifier doesn't correspond to any
|
||||||
|
/// known reporters.
|
||||||
|
///
|
||||||
|
/// - parameter identifier: The identifier corresponding to the reporter.
|
||||||
public func reporterFrom(identifier: String) -> Reporter.Type { // swiftlint:disable:this cyclomatic_complexity
|
public func reporterFrom(identifier: String) -> Reporter.Type { // swiftlint:disable:this cyclomatic_complexity
|
||||||
switch identifier {
|
switch identifier {
|
||||||
case XcodeReporter.identifier:
|
case XcodeReporter.identifier:
|
||||||
|
|
|
@ -1,19 +1,53 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// An executable value that can identify issues (violations) in Swift source code.
|
||||||
public protocol Rule {
|
public protocol Rule {
|
||||||
|
/// A verbose description of many of this rule's properties.
|
||||||
static var description: RuleDescription { get }
|
static var description: RuleDescription { get }
|
||||||
|
|
||||||
|
/// A description of how this rule has been configured to run.
|
||||||
var configurationDescription: String { get }
|
var configurationDescription: String { get }
|
||||||
|
|
||||||
init() // Rules need to be able to be initialized with default values
|
/// A default initializer for rules. All rules need to be trivially initializable.
|
||||||
|
init()
|
||||||
|
|
||||||
|
/// Creates a rule by applying its configuration.
|
||||||
|
///
|
||||||
|
/// - throws: Throws if the configuration didn't match the expected format.
|
||||||
init(configuration: Any) throws
|
init(configuration: Any) throws
|
||||||
|
|
||||||
|
/// Executes the rule on a file and returns any violations to the rule's expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func validate(file: SwiftLintFile, compilerArguments: [String]) -> [StyleViolation]
|
func validate(file: SwiftLintFile, compilerArguments: [String]) -> [StyleViolation]
|
||||||
|
|
||||||
|
/// Executes the rule on a file and returns any violations to the rule's expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
func validate(file: SwiftLintFile) -> [StyleViolation]
|
func validate(file: SwiftLintFile) -> [StyleViolation]
|
||||||
|
|
||||||
|
/// Whether or not the specified rule is equivalent to the current rule.
|
||||||
func isEqualTo(_ rule: Rule) -> Bool
|
func isEqualTo(_ rule: Rule) -> Bool
|
||||||
|
|
||||||
// These are called by the linter and are always implemented in extensions.
|
/// Collects information for the specified file in a storage object, to be analyzed by a `CollectedLinter`.
|
||||||
|
///
|
||||||
|
/// - note: This function is called by the linter and is always implemented in extensions.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to collect info.
|
||||||
|
/// - parameter storage: The storage object where collected info should be saved.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func collectInfo(for file: SwiftLintFile, into storage: RuleStorage, compilerArguments: [String])
|
func collectInfo(for file: SwiftLintFile, into storage: RuleStorage, compilerArguments: [String])
|
||||||
|
|
||||||
|
/// Executes the rule on a file after collecting file info for all files and returns any violations to the rule's
|
||||||
|
/// expectations.
|
||||||
|
///
|
||||||
|
/// - note: This function is called by the linter and is always implemented in extensions.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter storage: The storage object containing all collected info.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func validate(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [StyleViolation]
|
func validate(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [StyleViolation]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,21 +74,46 @@ extension Rule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A rule that is not enabled by default. Rules conforming to this need to be explicitly enabled by users.
|
||||||
public protocol OptInRule: Rule {}
|
public protocol OptInRule: Rule {}
|
||||||
|
|
||||||
|
/// A rule that has unit tests automatically generated using Sourcery.
|
||||||
public protocol AutomaticTestableRule: Rule {}
|
public protocol AutomaticTestableRule: Rule {}
|
||||||
|
|
||||||
|
/// A rule that is user-configurable.
|
||||||
public protocol ConfigurationProviderRule: Rule {
|
public protocol ConfigurationProviderRule: Rule {
|
||||||
|
/// The type of configuration used to configure this rule.
|
||||||
associatedtype ConfigurationType: RuleConfiguration
|
associatedtype ConfigurationType: RuleConfiguration
|
||||||
|
|
||||||
|
/// This rule's configuration.
|
||||||
var configuration: ConfigurationType { get set }
|
var configuration: ConfigurationType { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A rule that can correct violations.
|
||||||
public protocol CorrectableRule: Rule {
|
public protocol CorrectableRule: Rule {
|
||||||
|
/// Attempts to correct the violations to this rule in the specified file.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to correct violations.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
|
///
|
||||||
|
/// - returns: All corrections that were applied.
|
||||||
func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction]
|
func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction]
|
||||||
|
|
||||||
|
/// Attempts to correct the violations to this rule in the specified file.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to correct violations.
|
||||||
|
///
|
||||||
|
/// - returns: All corrections that were applied.
|
||||||
func correct(file: SwiftLintFile) -> [Correction]
|
func correct(file: SwiftLintFile) -> [Correction]
|
||||||
|
|
||||||
// Called by the linter and are always implemented in extensions.
|
/// Attempts to correct the violations to this rule in the specified file after collecting file info for all files
|
||||||
|
/// and returns all corrections that were applied.
|
||||||
|
///
|
||||||
|
/// - note: This function is called by the linter and is always implemented in extensions.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter storage: The storage object containing all collected info.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [Correction]
|
func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [Correction]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +126,13 @@ public extension CorrectableRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A correctable rule that can apply its corrections by replacing the content of ranges in the offending file with
|
||||||
|
/// updated content.
|
||||||
public protocol SubstitutionCorrectableRule: CorrectableRule {
|
public protocol SubstitutionCorrectableRule: CorrectableRule {
|
||||||
|
/// Returns the NSString-based `NSRange`s to be replaced in the specified file.
|
||||||
func violationRanges(in file: SwiftLintFile) -> [NSRange]
|
func violationRanges(in file: SwiftLintFile) -> [NSRange]
|
||||||
|
|
||||||
|
/// Returns the substitution to apply for the given range.
|
||||||
func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)?
|
func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +158,9 @@ public extension SubstitutionCorrectableRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `SubstitutionCorrectableRule` that is also an `ASTRule`.
|
||||||
public protocol SubstitutionCorrectableASTRule: SubstitutionCorrectableRule, ASTRule {
|
public protocol SubstitutionCorrectableASTRule: SubstitutionCorrectableRule, ASTRule {
|
||||||
|
/// Returns the NSString-based `NSRange`s to be replaced in the specified file.
|
||||||
func violationRanges(in file: SwiftLintFile, kind: KindType,
|
func violationRanges(in file: SwiftLintFile, kind: KindType,
|
||||||
dictionary: SourceKittenDictionary) -> [NSRange]
|
dictionary: SourceKittenDictionary) -> [NSRange]
|
||||||
}
|
}
|
||||||
|
@ -108,8 +174,11 @@ public extension SubstitutionCorrectableASTRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A rule that does not need SourceKit to operate and can still operate even after SourceKit has crashed.
|
||||||
public protocol SourceKitFreeRule: Rule {}
|
public protocol SourceKitFreeRule: Rule {}
|
||||||
|
|
||||||
|
/// A rule that can operate on the post-typechecked AST using compiler arguments. Performs rules that are more like
|
||||||
|
/// static analysis than syntactic checks.
|
||||||
public protocol AnalyzerRule: OptInRule {}
|
public protocol AnalyzerRule: OptInRule {}
|
||||||
|
|
||||||
public extension AnalyzerRule {
|
public extension AnalyzerRule {
|
||||||
|
@ -118,6 +187,7 @@ public extension AnalyzerRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public extension AnalyzerRule where Self: CorrectableRule {
|
public extension AnalyzerRule where Self: CorrectableRule {
|
||||||
func correct(file: SwiftLintFile) -> [Correction] {
|
func correct(file: SwiftLintFile) -> [Correction] {
|
||||||
queuedFatalError("Must call `correct(file:compilerArguments:)` for AnalyzerRule")
|
queuedFatalError("Must call `correct(file:compilerArguments:)` for AnalyzerRule")
|
||||||
|
@ -129,12 +199,36 @@ public extension AnalyzerRule where Self: CorrectableRule {
|
||||||
/// Type-erased protocol used to check whether a rule is collectable.
|
/// Type-erased protocol used to check whether a rule is collectable.
|
||||||
public protocol AnyCollectingRule: Rule { }
|
public protocol AnyCollectingRule: Rule { }
|
||||||
|
|
||||||
|
/// A rule that requires knowledge of all other files being linted.
|
||||||
public protocol CollectingRule: AnyCollectingRule {
|
public protocol CollectingRule: AnyCollectingRule {
|
||||||
|
/// The kind of information to collect for each file being linted for this rule.
|
||||||
associatedtype FileInfo
|
associatedtype FileInfo
|
||||||
|
|
||||||
|
/// Collects information for the specified file, to be analyzed by a `CollectedLinter`.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to collect info.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func collectInfo(for file: SwiftLintFile, compilerArguments: [String]) -> FileInfo
|
func collectInfo(for file: SwiftLintFile, compilerArguments: [String]) -> FileInfo
|
||||||
|
|
||||||
|
/// Collects information for the specified file, to be analyzed by a `CollectedLinter`.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to collect info.
|
||||||
func collectInfo(for file: SwiftLintFile) -> FileInfo
|
func collectInfo(for file: SwiftLintFile) -> FileInfo
|
||||||
|
|
||||||
|
/// Executes the rule on a file after collecting file info for all files and returns any violations to the rule's
|
||||||
|
/// expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter collectedInfo: All collected info for all files.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func validate(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo],
|
func validate(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo],
|
||||||
compilerArguments: [String]) -> [StyleViolation]
|
compilerArguments: [String]) -> [StyleViolation]
|
||||||
|
|
||||||
|
/// Executes the rule on a file after collecting file info for all files and returns any violations to the rule's
|
||||||
|
/// expectations.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter collectedInfo: All collected info for all files.
|
||||||
func validate(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [StyleViolation]
|
func validate(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [StyleViolation]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,9 +276,26 @@ public extension CollectingRule where Self: AnalyzerRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `CollectingRule` that is also a `CorrectableRule`.
|
||||||
public protocol CollectingCorrectableRule: CollectingRule, CorrectableRule {
|
public protocol CollectingCorrectableRule: CollectingRule, CorrectableRule {
|
||||||
|
/// Attempts to correct the violations to this rule in the specified file after collecting file info for all files
|
||||||
|
/// and returns all corrections that were applied.
|
||||||
|
///
|
||||||
|
/// - note: This function is called by the linter and is always implemented in extensions.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter collectedInfo: All collected info.
|
||||||
|
/// - parameter compilerArguments: The compiler arguments needed to compile this file.
|
||||||
func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo],
|
func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo],
|
||||||
compilerArguments: [String]) -> [Correction]
|
compilerArguments: [String]) -> [Correction]
|
||||||
|
|
||||||
|
/// Attempts to correct the violations to this rule in the specified file after collecting file info for all files
|
||||||
|
/// and returns all corrections that were applied.
|
||||||
|
///
|
||||||
|
/// - note: This function is called by the linter and is always implemented in extensions.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file for which to execute the rule.
|
||||||
|
/// - parameter collectedInfo: All collected info.
|
||||||
func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [Correction]
|
func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [Correction]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +350,7 @@ public extension ConfigurationProviderRule {
|
||||||
|
|
||||||
// MARK: - == Implementations
|
// MARK: - == Implementations
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public extension Array where Element == Rule {
|
public extension Array where Element == Rule {
|
||||||
static func == (lhs: Array, rhs: Array) -> Bool {
|
static func == (lhs: Array, rhs: Array) -> Bool {
|
||||||
if lhs.count != rhs.count { return false }
|
if lhs.count != rhs.count { return false }
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
|
/// A configuration value for a rule to allow users to modify its behavior.
|
||||||
public protocol RuleConfiguration {
|
public protocol RuleConfiguration {
|
||||||
|
/// A human-readable description for this configuration and its applied values.
|
||||||
var consoleDescription: String { get }
|
var consoleDescription: String { get }
|
||||||
|
|
||||||
|
/// Apply an untyped configuration to the current value.
|
||||||
|
///
|
||||||
|
/// - throws: Throws if the configuration is not in the expected format.
|
||||||
mutating func apply(configuration: Any) throws
|
mutating func apply(configuration: Any) throws
|
||||||
|
|
||||||
|
/// Whether the specified configuration is equivalent to the current value.
|
||||||
func isEqualTo(_ ruleConfiguration: RuleConfiguration) -> Bool
|
func isEqualTo(_ ruleConfiguration: RuleConfiguration) -> Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
private extension String {
|
/// Reports violations as a newline-separated string of comma-separated values (CSV).
|
||||||
func escapedForCSV() -> String {
|
|
||||||
let escapedString = replacingOccurrences(of: "\"", with: "\"\"")
|
|
||||||
if escapedString.contains(",") || escapedString.contains("\n") {
|
|
||||||
return "\"\(escapedString)\""
|
|
||||||
}
|
|
||||||
return escapedString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct CSVReporter: Reporter {
|
public struct CSVReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "csv"
|
public static let identifier = "csv"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
@ -33,7 +26,9 @@ public struct CSVReporter: Reporter {
|
||||||
return rows.joined(separator: "\n")
|
return rows.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate static func csvRow(for violation: StyleViolation) -> String {
|
// MARK: - Private
|
||||||
|
|
||||||
|
private static func csvRow(for violation: StyleViolation) -> String {
|
||||||
return [
|
return [
|
||||||
violation.location.file?.escapedForCSV() ?? "",
|
violation.location.file?.escapedForCSV() ?? "",
|
||||||
violation.location.line?.description ?? "",
|
violation.location.line?.description ?? "",
|
||||||
|
@ -45,3 +40,13 @@ public struct CSVReporter: Reporter {
|
||||||
].joined(separator: ",")
|
].joined(separator: ",")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
func escapedForCSV() -> String {
|
||||||
|
let escapedString = replacingOccurrences(of: "\"", with: "\"\"")
|
||||||
|
if escapedString.contains(",") || escapedString.contains("\n") {
|
||||||
|
return "\"\(escapedString)\""
|
||||||
|
}
|
||||||
|
return escapedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
/// Reports violations as XML conforming to the Checkstyle specification, as defined here:
|
||||||
|
/// https://www.jetbrains.com/help/teamcity/xml-report-processing.html
|
||||||
public struct CheckstyleReporter: Reporter {
|
public struct CheckstyleReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "checkstyle"
|
public static let identifier = "checkstyle"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
@ -17,6 +21,8 @@ public struct CheckstyleReporter: Reporter {
|
||||||
].joined()
|
].joined()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
private static func generateForViolationFile(_ file: String, violations: [StyleViolation]) -> String {
|
private static func generateForViolationFile(_ file: String, violations: [StyleViolation]) -> String {
|
||||||
return [
|
return [
|
||||||
"\n\t<file name=\"", file, "\">\n",
|
"\n\t<file name=\"", file, "\">\n",
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
/// Reports violations in the format that's both fun and easy to read.
|
||||||
public struct EmojiReporter: Reporter {
|
public struct EmojiReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "emoji"
|
public static let identifier = "emoji"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
@ -13,6 +16,8 @@ public struct EmojiReporter: Reporter {
|
||||||
.map(report).joined(separator: "\n")
|
.map(report).joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
private static func report(for file: String, with violations: [StyleViolation]) -> String {
|
private static func report(for file: String, with violations: [StyleViolation]) -> String {
|
||||||
let lines = [file] + violations.sorted { lhs, rhs in
|
let lines = [file] + violations.sorted { lhs, rhs in
|
||||||
guard lhs.severity == rhs.severity else {
|
guard lhs.severity == rhs.severity else {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
/// Reports violations in the format GitHub-hosted virtual machine for Actions can recognize as messages.
|
||||||
public struct GitHubActionsLoggingReporter: Reporter {
|
public struct GitHubActionsLoggingReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "github-actions-logging"
|
public static let identifier = "github-actions-logging"
|
||||||
public static let isRealtime = true
|
public static let isRealtime = true
|
||||||
|
|
||||||
|
@ -10,7 +13,9 @@ public struct GitHubActionsLoggingReporter: Reporter {
|
||||||
return violations.map(generateForSingleViolation).joined(separator: "\n")
|
return violations.map(generateForSingleViolation).joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static func generateForSingleViolation(_ violation: StyleViolation) -> String {
|
// MARK: - Private
|
||||||
|
|
||||||
|
private static func generateForSingleViolation(_ violation: StyleViolation) -> String {
|
||||||
// swiftlint:disable:next line_length
|
// swiftlint:disable:next line_length
|
||||||
// https://help.github.com/en/github/automating-your-workflow-with-github-actions/development-tools-for-github-actions#logging-commands
|
// https://help.github.com/en/github/automating-your-workflow-with-github-actions/development-tools-for-github-actions#logging-commands
|
||||||
// ::(warning|error) file={relative_path_to_file},line={:line},col={:character}::{content}
|
// ::(warning|error) file={relative_path_to_file},line={:line},col={:character}::{content}
|
||||||
|
|
|
@ -6,12 +6,15 @@ private let formatter: DateFormatter = {
|
||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
/// Reports violations as HTML.
|
||||||
public struct HTMLReporter: Reporter {
|
public struct HTMLReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "html"
|
public static let identifier = "html"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "Reports violations as HTML"
|
return "Reports violations as HTML."
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func generateReport(_ violations: [StyleViolation]) -> String {
|
public static func generateReport(_ violations: [StyleViolation]) -> String {
|
||||||
|
@ -19,6 +22,8 @@ public struct HTMLReporter: Reporter {
|
||||||
dateString: formatter.string(from: Date()))
|
dateString: formatter.string(from: Date()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal
|
||||||
|
|
||||||
// swiftlint:disable:next function_body_length
|
// swiftlint:disable:next function_body_length
|
||||||
internal static func generateReport(_ violations: [StyleViolation], swiftlintVersion: String,
|
internal static func generateReport(_ violations: [StyleViolation], swiftlintVersion: String,
|
||||||
dateString: String) -> String {
|
dateString: String) -> String {
|
||||||
|
@ -143,6 +148,8 @@ public struct HTMLReporter: Reporter {
|
||||||
].joined()
|
].joined()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
private static func generateSingleRow(for violation: StyleViolation, at index: Int) -> String {
|
private static func generateSingleRow(for violation: StyleViolation, at index: Int) -> String {
|
||||||
let severity: String = violation.severity.rawValue.capitalized
|
let severity: String = violation.severity.rawValue.capitalized
|
||||||
let location = violation.location
|
let location = violation.location
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// Reports violations as a JSON array.
|
||||||
public struct JSONReporter: Reporter {
|
public struct JSONReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "json"
|
public static let identifier = "json"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
@ -13,7 +16,9 @@ public struct JSONReporter: Reporter {
|
||||||
return toJSON(violations.map(dictionary(for:)))
|
return toJSON(violations.map(dictionary(for:)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate static func dictionary(for violation: StyleViolation) -> [String: Any] {
|
// MARK: - Private
|
||||||
|
|
||||||
|
private static func dictionary(for violation: StyleViolation) -> [String: Any] {
|
||||||
return [
|
return [
|
||||||
"file": violation.location.file ?? NSNull() as Any,
|
"file": violation.location.file ?? NSNull() as Any,
|
||||||
"line": violation.location.line ?? NSNull() as Any,
|
"line": violation.location.line ?? NSNull() as Any,
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
/// Reports violations as JUnit XML.
|
||||||
public struct JUnitReporter: Reporter {
|
public struct JUnitReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "junit"
|
public static let identifier = "junit"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
private extension String {
|
/// Reports violations as markdown formated (with tables).
|
||||||
func escapedForMarkdown() -> String {
|
|
||||||
let escapedString = replacingOccurrences(of: "\"", with: "\"\"")
|
|
||||||
if escapedString.contains("|") || escapedString.contains("\n") {
|
|
||||||
return "\"\(escapedString)\""
|
|
||||||
}
|
|
||||||
return escapedString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct MarkdownReporter: Reporter {
|
public struct MarkdownReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "markdown"
|
public static let identifier = "markdown"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return "Reports violations as markdown formated (with tables)"
|
return "Reports violations as markdown formated (with tables)."
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func generateReport(_ violations: [StyleViolation]) -> String {
|
public static func generateReport(_ violations: [StyleViolation]) -> String {
|
||||||
|
@ -31,7 +24,9 @@ public struct MarkdownReporter: Reporter {
|
||||||
return rows.joined(separator: "\n")
|
return rows.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate static func markdownRow(for violation: StyleViolation) -> String {
|
// MARK: - Private
|
||||||
|
|
||||||
|
private static func markdownRow(for violation: StyleViolation) -> String {
|
||||||
return [
|
return [
|
||||||
violation.location.file?.escapedForMarkdown() ?? "",
|
violation.location.file?.escapedForMarkdown() ?? "",
|
||||||
violation.location.line?.description ?? "",
|
violation.location.line?.description ?? "",
|
||||||
|
@ -41,7 +36,7 @@ public struct MarkdownReporter: Reporter {
|
||||||
].joined(separator: " | ")
|
].joined(separator: " | ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate static func severity(for severity: ViolationSeverity) -> String {
|
private static func severity(for severity: ViolationSeverity) -> String {
|
||||||
switch severity {
|
switch severity {
|
||||||
case .error:
|
case .error:
|
||||||
return ":stop\\_sign:"
|
return ":stop\\_sign:"
|
||||||
|
@ -50,3 +45,13 @@ public struct MarkdownReporter: Reporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
func escapedForMarkdown() -> String {
|
||||||
|
let escapedString = replacingOccurrences(of: "\"", with: "\"\"")
|
||||||
|
if escapedString.contains("|") || escapedString.contains("\n") {
|
||||||
|
return "\"\(escapedString)\""
|
||||||
|
}
|
||||||
|
return escapedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import SourceKittenFramework
|
import SourceKittenFramework
|
||||||
|
|
||||||
|
/// Reports violations in SonarQube import format.
|
||||||
public struct SonarQubeReporter: Reporter {
|
public struct SonarQubeReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "sonarqube"
|
public static let identifier = "sonarqube"
|
||||||
public static let isRealtime = false
|
public static let isRealtime = false
|
||||||
|
|
||||||
|
@ -12,6 +15,8 @@ public struct SonarQubeReporter: Reporter {
|
||||||
return toJSON(["issues": violations.map(dictionary(for:))])
|
return toJSON(["issues": violations.map(dictionary(for:))])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
// refer to https://docs.sonarqube.org/display/SONAR/Generic+Issue+Data
|
// refer to https://docs.sonarqube.org/display/SONAR/Generic+Issue+Data
|
||||||
private static func dictionary(for violation: StyleViolation) -> [String: Any] {
|
private static func dictionary(for violation: StyleViolation) -> [String: Any] {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
/// Reports violations in the format Xcode uses to display in the IDE. (default)
|
||||||
public struct XcodeReporter: Reporter {
|
public struct XcodeReporter: Reporter {
|
||||||
|
// MARK: - Reporter Conformance
|
||||||
|
|
||||||
public static let identifier = "xcode"
|
public static let identifier = "xcode"
|
||||||
public static let isRealtime = true
|
public static let isRealtime = true
|
||||||
|
|
||||||
|
@ -10,6 +13,9 @@ public struct XcodeReporter: Reporter {
|
||||||
return violations.map(generateForSingleViolation).joined(separator: "\n")
|
return violations.map(generateForSingleViolation).joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a report for a single violation.
|
||||||
|
///
|
||||||
|
/// - parameter violation: The violation to report.
|
||||||
internal static func generateForSingleViolation(_ violation: StyleViolation) -> String {
|
internal static func generateForSingleViolation(_ violation: StyleViolation) -> String {
|
||||||
// {full_path_to_file}{:line}{:character}: {error,warning}: {content}
|
// {full_path_to_file}{:line}{:character}: {error,warning}: {content}
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -8,7 +8,11 @@ public struct ModifierOrderConfiguration: RuleConfiguration, Equatable {
|
||||||
return severityConfiguration.consoleDescription + ", preferred_modifier_order: \(preferredModifierOrder)"
|
return severityConfiguration.consoleDescription + ", preferred_modifier_order: \(preferredModifierOrder)"
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(preferredModifierOrder: [SwiftDeclarationAttributeKind.ModifierGroup] = []) {
|
public init() {
|
||||||
|
self.preferredModifierOrder = []
|
||||||
|
}
|
||||||
|
|
||||||
|
init(preferredModifierOrder: [SwiftDeclarationAttributeKind.ModifierGroup] = []) {
|
||||||
self.preferredModifierOrder = preferredModifierOrder
|
self.preferredModifierOrder = preferredModifierOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,17 +106,23 @@ jobs:
|
||||||
|
|
||||||
- job: jazzy
|
- job: jazzy
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'Ubuntu 16.04'
|
vmImage: 'macOS 10.14'
|
||||||
container: norionomura/jazzy:0.13.0_swift-5.1.3
|
variables:
|
||||||
|
DEVELOPER_DIR: /Applications/Xcode_11.3.app
|
||||||
steps:
|
steps:
|
||||||
- script: swift run swiftlint generate-docs
|
- script: swift run swiftlint generate-docs
|
||||||
displayName: Run swiftlint generate-docs
|
displayName: Run swiftlint generate-docs
|
||||||
- script: bundle install --path vendor/bundle
|
- script: bundle install --path vendor/bundle
|
||||||
displayName: bundle install
|
displayName: bundle install
|
||||||
- script: sourcekitten doc --spm-module SwiftLintFramework > docs.json
|
- script: bundle exec jazzy
|
||||||
displayName: Generate documentation json
|
|
||||||
- script: bundle exec jazzy --sourcekitten-sourcefile docs.json
|
|
||||||
displayName: Run jazzy
|
displayName: Run jazzy
|
||||||
|
- script: >
|
||||||
|
if ruby -rjson -e "j = JSON.parse(File.read('docs/undocumented.json')); exit j['warnings'].length != 0"; then
|
||||||
|
echo "Undocumented declarations:"
|
||||||
|
cat docs/undocumented.json
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
displayName: Validate documentation coverage
|
||||||
- task: PublishPipelineArtifact@0
|
- task: PublishPipelineArtifact@0
|
||||||
inputs:
|
inputs:
|
||||||
artifactName: 'API Docs'
|
artifactName: 'API Docs'
|
||||||
|
|
Loading…
Reference in New Issue