Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds force-exlucde flag to ling/autocorrect commands #2056

Closed
wants to merge 9 commits into from
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

#### Enhancements

* Adds `--force-exclude` option to `lint` and `autocorrect` commands, which will
force SwiftLint to exclude files specified in the config `excluded` even if
they are explicitly specified with `--path`.
[Ash Furrow](https://github.com/ashfurrow)
[#2051](https://github.com/realm/SwiftLint/issues/2051)
* Add a new `excluded` config parameter to the `explicit_type_interface` rule
to exempt certain types of variables from the rule.
[Rounak Jain](https://github.com/rounak)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import Foundation
import SourceKittenFramework

extension Configuration {
public func lintableFiles(inPath path: String) -> [File] {
return lintablePaths(inPath: path).flatMap(File.init(pathDeferringReading:))
public func lintableFiles(inPath path: String, forceExclude: Bool) -> [File] {
return lintablePaths(inPath: path, forceExclude: forceExclude).flatMap(File.init(pathDeferringReading:))
}

internal func lintablePaths(inPath path: String,
internal func lintablePaths(inPath path: String, forceExclude: Bool,
fileManager: LintableFileManager = FileManager.default) -> [String] {
// If path is a file, skip filtering with excluded/included paths
if path.isFile {
// If path is a file and we're not forcing excludes, skip filtering with excluded/included paths
if path.isFile && !forceExclude {
return [path]
}
let pathsForPath = included.isEmpty ? fileManager.filesToLint(inPath: path, rootDirectory: nil) : []
Expand Down
13 changes: 9 additions & 4 deletions Source/swiftlint/Commands/AutoCorrectCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct AutoCorrectCommand: CommandProtocol {
return configuration.visitLintableFiles(path: options.path, action: "Correcting",
quiet: options.quiet,
useScriptInputFiles: options.useScriptInputFiles,
forceExclude: options.forceExclude,
cache: cache, parallel: true) { linter in
let corrections = linter.correct()
if !corrections.isEmpty && !options.quiet {
Expand Down Expand Up @@ -65,16 +66,17 @@ struct AutoCorrectOptions: OptionsProtocol {
let configurationFile: String
let useScriptInputFiles: Bool
let quiet: Bool
let forceExclude: Bool
let format: Bool
let cachePath: String
let ignoreCache: Bool
let useTabs: Bool

// swiftlint:disable line_length
static func create(_ path: String) -> (_ configurationFile: String) -> (_ useScriptInputFiles: Bool) -> (_ quiet: Bool) -> (_ format: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ useTabs: Bool) -> AutoCorrectOptions {
return { configurationFile in { useScriptInputFiles in { quiet in { format in { cachePath in { ignoreCache in { useTabs in
self.init(path: path, configurationFile: configurationFile, useScriptInputFiles: useScriptInputFiles, quiet: quiet, format: format, cachePath: cachePath, ignoreCache: ignoreCache, useTabs: useTabs)
}}}}}}}
static func create(_ path: String) -> (_ configurationFile: String) -> (_ useScriptInputFiles: Bool) -> (_ quiet: Bool) -> (_ forceExclude: Bool) -> (_ format: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ useTabs: Bool) -> AutoCorrectOptions {
return { configurationFile in { useScriptInputFiles in { quiet in { forceExclude in { format in { cachePath in { ignoreCache in { useTabs in
self.init(path: path, configurationFile: configurationFile, useScriptInputFiles: useScriptInputFiles, quiet: quiet, forceExclude: forceExclude, format: format, cachePath: cachePath, ignoreCache: ignoreCache, useTabs: useTabs)
}}}}}}}}
}

static func evaluate(_ mode: CommandMode) -> Result<AutoCorrectOptions, CommandantError<CommandantError<()>>> {
Expand All @@ -84,6 +86,9 @@ struct AutoCorrectOptions: OptionsProtocol {
<*> mode <| configOption
<*> mode <| useScriptInputFilesOption
<*> mode <| quietOption(action: "correcting")
<*> mode <| Option(key: "force-exclude",
defaultValue: false,
usage: "exclude files in config `excluded` even if their paths are explicitly specified")
<*> mode <| Option(key: "format",
defaultValue: false,
usage: "should reformat the Swift files")
Expand Down
11 changes: 7 additions & 4 deletions Source/swiftlint/Commands/LintCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct LintOptions: OptionsProtocol {
let configurationFile: String
let strict: Bool
let lenient: Bool
let forceExclude: Bool
let useScriptInputFiles: Bool
let benchmark: Bool
let reporter: String
Expand All @@ -139,10 +140,10 @@ struct LintOptions: OptionsProtocol {
let enableAllRules: Bool

// swiftlint:disable line_length
static func create(_ path: String) -> (_ useSTDIN: Bool) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ enableAllRules: Bool) -> LintOptions {
return { useSTDIN in { configurationFile in { strict in { lenient in { useScriptInputFiles in { benchmark in { reporter in { quiet in { cachePath in { ignoreCache in { enableAllRules in
self.init(path: path, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: ignoreCache, enableAllRules: enableAllRules)
}}}}}}}}}}}
static func create(_ path: String) -> (_ useSTDIN: Bool) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ forceExclude: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ enableAllRules: Bool) -> LintOptions {
return { useSTDIN in { configurationFile in { strict in { lenient in { forceExclude in { useScriptInputFiles in { benchmark in { reporter in { quiet in { cachePath in { ignoreCache in { enableAllRules in
self.init(path: path, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, forceExclude: forceExclude, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: ignoreCache, enableAllRules: enableAllRules)
}}}}}}}}}}}}
}

static func evaluate(_ mode: CommandMode) -> Result<LintOptions, CommandantError<CommandantError<()>>> {
Expand All @@ -156,6 +157,8 @@ struct LintOptions: OptionsProtocol {
usage: "fail on warnings")
<*> mode <| Option(key: "lenient", defaultValue: false,
usage: "downgrades serious violations to warnings, warning threshold is disabled")
<*> mode <| Option(key: "force-exclude", defaultValue: false,
usage: "exclude files in config `excluded` even if their paths are explicitly specified")
<*> mode <| useScriptInputFilesOption
<*> mode <| Option(key: "benchmark", defaultValue: false,
usage: "save benchmarks to benchmark_files.txt " +
Expand Down
13 changes: 8 additions & 5 deletions Source/swiftlint/Extensions/Configuration+CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ private func autoreleasepool(block: () -> Void) { block() }
extension Configuration {

func visitLintableFiles(path: String, action: String, useSTDIN: Bool = false,
quiet: Bool = false, useScriptInputFiles: Bool,
quiet: Bool = false, useScriptInputFiles: Bool, forceExclude: Bool,
cache: LinterCache? = nil, parallel: Bool = false,
visitorBlock: @escaping (Linter) -> Void) -> Result<[File], CommandantError<()>> {
return getFiles(path: path, action: action, useSTDIN: useSTDIN, quiet: quiet,
return getFiles(path: path, action: action, useSTDIN: useSTDIN, quiet: quiet, forceExclude: forceExclude,
useScriptInputFiles: useScriptInputFiles)
.flatMap { files -> Result<[Configuration: [File]], CommandantError<()>> in
if files.isEmpty {
Expand Down Expand Up @@ -112,7 +112,8 @@ extension Configuration {
}
}

fileprivate func getFiles(path: String, action: String, useSTDIN: Bool, quiet: Bool,
// swiftlint:disable function_parameter_count
fileprivate func getFiles(path: String, action: String, useSTDIN: Bool, quiet: Bool, forceExclude: Bool,
useScriptInputFiles: Bool) -> Result<[File], CommandantError<()>> {
if useSTDIN {
let stdinData = FileHandle.standardInput.readDataToEndOfFile()
Expand All @@ -127,8 +128,9 @@ extension Configuration {
let message = "\(action) Swift files " + (path.isEmpty ? "in current working directory" : "at path \(path)")
queuedPrintError(message)
}
return .success(lintableFiles(inPath: path))
return .success(lintableFiles(inPath: path, forceExclude: forceExclude))
}
// swiftlint:enable function_parameter_count

// MARK: Lint Command

Expand All @@ -144,7 +146,8 @@ extension Configuration {
visitorBlock: @escaping (Linter) -> Void) -> Result<[File], CommandantError<()>> {
return visitLintableFiles(path: options.path, action: "Linting", useSTDIN: options.useSTDIN,
quiet: options.quiet, useScriptInputFiles: options.useScriptInputFiles,
cache: cache, parallel: true, visitorBlock: visitorBlock)
forceExclude: options.forceExclude, cache: cache, parallel: true,
visitorBlock: visitorBlock)
}

// MARK: AutoCorrect Command
Expand Down
33 changes: 31 additions & 2 deletions Tests/SwiftLintFrameworkTests/ConfigurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ private extension Configuration {
}
}

// swiftlint:disable type_body_length
class ConfigurationTests: XCTestCase {

func testInit() {
Expand Down Expand Up @@ -176,12 +177,40 @@ class ConfigurationTests: XCTestCase {
let configuration = Configuration(included: ["directory"],
excluded: ["directory/excluded",
"directory/ExcludedFile.swift"])!
let paths = configuration.lintablePaths(inPath: "", fileManager: TestFileManager())
let paths = configuration.lintablePaths(inPath: "", forceExclude: false, fileManager: TestFileManager())
XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
}

func testForceExcludesFile() {
let configuration = Configuration(excluded: ["directory/ExcludedFile.swift"])!
let paths = configuration.lintablePaths(inPath: "directory/ExcludedFile.swift", forceExclude: true,
fileManager: TestFileManager())
XCTAssertEqual([], paths)
}

func testForceExcludesFileNotPresentInExcluded() {
let configuration = Configuration(included: ["directory"],
excluded: ["directory/ExcludedFile.swift", "directory/excluded"])!
let paths = configuration.lintablePaths(inPath: "", forceExclude: true, fileManager: TestFileManager())
XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
}

func testForceExcludesDirectory() {
let configuration = Configuration(excluded: ["directory/excluded", "directory/ExcludedFile.swift"])!
let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true,
fileManager: TestFileManager())
XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
}

func testForceExcludesDirectoryThatIsNotInExcludedButHasChildrenThatAre() {
let configuration = Configuration(excluded: ["directory/excluded", "directory/ExcludedFile.swift"])!
let paths = configuration.lintablePaths(inPath: "directory", forceExclude: true,
fileManager: TestFileManager())
XCTAssertEqual(["directory/File1.swift", "directory/File2.swift"], paths)
}

func testLintablePaths() {
let paths = Configuration()!.lintablePaths(inPath: projectMockPathLevel0)
let paths = Configuration()!.lintablePaths(inPath: projectMockPathLevel0, forceExclude: false)
let filenames = paths.map { $0.bridge().lastPathComponent }.sorted()
let expectedFilenames = [
"DirectoryLevel1.swift",
Expand Down
4 changes: 2 additions & 2 deletions Tests/SwiftLintFrameworkTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class IntegrationTests: XCTestCase {

func testSwiftLintLints() {
// This is as close as we're ever going to get to a self-hosting linter.
let swiftFiles = config.lintableFiles(inPath: "")
let swiftFiles = config.lintableFiles(inPath: "", forceExclude: false)
XCTAssert(swiftFiles.map({ $0.path! }).contains(#file), "current file should be included")

let violations = swiftFiles.flatMap {
Expand All @@ -38,7 +38,7 @@ class IntegrationTests: XCTestCase {
}

func testSwiftLintAutoCorrects() {
let swiftFiles = config.lintableFiles(inPath: "")
let swiftFiles = config.lintableFiles(inPath: "", forceExclude: false)
let corrections = swiftFiles.flatMap { Linter(file: $0, configuration: config).correct() }
for correction in corrections {
correction.location.file!.withStaticString {
Expand Down