diff --git a/.swift-version b/.swift-version index a3ec5a4bd3d..5186d07068c 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.2 +4.0 diff --git a/.swiftlint.yml b/.swiftlint.yml index fe1089899f1..d65fe2b9fac 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -4,39 +4,40 @@ included: excluded: - Tests/SwiftLintFrameworkTests/Resources opt_in_rules: - - empty_count - - file_header - - explicit_init - - closure_spacing - - overridden_super_call - - redundant_nil_coalescing - - private_outlet - - nimble_operator + - array_init - attributes - - operator_usage_whitespace - closure_end_indentation - - first_where - - sorted_imports - - object_literal - - number_separator - - prohibited_super_call + - closure_spacing + - contains_over_first_not_nil + - empty_count + - explicit_init + - extension_access_modifier - fatal_error_message - - vertical_parameter_alignment_on_call + - file_header + - file_name + - first_where + - joined_default_parameter - let_var_whitespace - - unneeded_parentheses_in_closure_argument - - extension_access_modifier - - pattern_matching_keywords - - array_init - literal_expression_end_indentation - - joined_default_parameter - - contains_over_first_not_nil + - nimble_operator + - number_separator + - object_literal + - operator_usage_whitespace + - overridden_super_call - override_in_extension + - pattern_matching_keywords - private_action + - private_outlet + - prohibited_super_call - quick_discouraged_call - quick_discouraged_focused_test - quick_discouraged_pending_test + - redundant_nil_coalescing - single_test_class - sorted_first_last + - sorted_imports + - unneeded_parentheses_in_closure_argument + - vertical_parameter_alignment_on_call - yoda_condition identifier_name: @@ -45,6 +46,12 @@ identifier_name: line_length: 120 number_separator: minimum_length: 5 +file_name: + excluded: + - main.swift + - LinuxMain.swift + - TestHelpers.swift + - shim.swift custom_rules: rule_id: diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d35228856..17db282a54f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ #### Breaking +* SwiftLint now requires Swift 4.0 or higher to build. + [JP Simard](https://github.com/jpsim) + * `fallthrough` rule is now opt-in. [Marcelo Fabri](https://github.com/marcelofabri) [#1892](https://github.com/realm/SwiftLint/issues/1892) @@ -12,6 +15,9 @@ only a `fallthrough`. [Austin Belknap](https://github.com/dabelknap) +* Updates the `untyped_error_in_catch` rule to support autocorrection. + [Daniel Metzing](https://github.com/dirtydanee) + * Add `indented_cases` support to `switch_case_alignment` rule. [Shai Mishali](https://github.com/freak4pc) [#2119](https://github.com/realm/SwiftLint/issues/2119) @@ -34,11 +40,21 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#2127](https://github.com/realm/SwiftLint/issues/2127) +* Updates the `closure_end_indentation` rule to support autocorrection. + [Eric Horacek](https://github.com/erichoracek) + +* Updates the `literal_expression_end_indentation` rule to support + autocorrection. + [Eric Horacek](https://github.com/erichoracek) + * Fixes a case where the `closure_end_indentation` rule wouldn't lint the end indentation of non-trailing closure parameters. [Eric Horacek](https://github.com/erichoracek) [#2121](https://github.com/realm/SwiftLint/issues/2121)] +* Improves the `mark` rule's autocorrection. + [Eric Horacek](https://github.com/erichoracek) + * Add `redundant_set_access_control` rule to warn against using redundant setter ACLs on variable declarations. [Marcelo Fabri](https://github.com/marcelofabri) @@ -49,12 +65,39 @@ [Michael Gray](https://github.com/mishagray) [#2100](https://github.com/realm/SwiftLint/pull/2100) +* Add a new `ignores_default_parameters` config parameter to the + `function_parameter_count` rule to ignore default parameter when calculating + parameter count. True by default. + [Varun P M](https://github.com/varunpm1) + [#2171](https://github.com/realm/SwiftLint/issues/2171) + +* Add `empty_xctest_method` opt-in rule which warns against empty + XCTest methods. + [Ornithologist Coder](https://github.com/ornithocoder) + [#2190](https://github.com/realm/SwiftLint/pull/2190) + +* Add `function_default_parameter_at_end` opt-in rule to validate that + parameters with defaults are located toward the end of the parameter list in a + function declaration. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2176](https://github.com/realm/SwiftLint/issues/2176) + +* Add `file_name` opt-in rule validating that file names contain the name of a + type or extension declared in the file (if any). + [JP Simard](https://github.com/jpsim) + [#1420](https://github.com/realm/SwiftLint/issues/1420) + #### Bug Fixes * Update `LowerACLThanParent` rule to not lint extensions. [Keith Smiley](https://github.com/keith) [#2164](https://github.com/realm/SwiftLint/pull/2164) +* Fix operator usage spacing nested generics false positive. + [Eric Horacek](https://github.com/erichoracek) + [#1341](https://github.com/realm/SwiftLint/issues/1341) + [#1897](https://github.com/realm/SwiftLint/issues/1897) + * Fix autocorrection for several rules (`empty_parentheses_with_trailing_closure`, `explicit_init`, `joined_default_parameter`, `redundant_optional_initialization` and @@ -62,6 +105,29 @@ [John Szumski](https://github.com/jszumski) [Marcelo Fabri](https://github.com/marcelofabri) +* Fix `unneeded_parentheses_in_closure_argument` false negatives when multiple + violations are nested. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2188](https://github.com/realm/SwiftLint/issues/2188) + +* Fix false negatives in `implicit_return` rule when using closures as + function arguments. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2187](https://github.com/realm/SwiftLint/issues/2187) + +* Fix false positives in `attributes` rule when `@testable` is used. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2211](https://github.com/realm/SwiftLint/issues/2211) + +* Fix false positives in `prohibited_super_call` rule. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2212](https://github.com/realm/SwiftLint/issues/2212) + +* Fix a false positive in `unused_closure_parameter` rule when a parameter + is used in a string interpolation. + [Marcelo Fabri](https://github.com/marcelofabri) + [#2062](https://github.com/realm/SwiftLint/issues/2062) + ## 0.25.1: Lid Locked This is the last release to support building with Swift 3.2 and Swift 3.3. diff --git a/Cartfile.private b/Cartfile.private index 60c09dcfaa6..34899b9ac7a 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1,3 +1,3 @@ github "Carthage/Commandant" ~> 0.13.0 -github "jspahrsummers/xcconfigs" "master" +github "jspahrsummers/xcconfigs" ~> 0.12.0 github "jpsim/Yams" ~> 0.7 diff --git a/Cartfile.resolved b/Cartfile.resolved index f7e844cfb8f..35820d7ff53 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,7 +1,7 @@ github "Carthage/Commandant" "0.13.0" github "antitypical/Result" "3.2.4" -github "drmohundro/SWXMLHash" "4.6.0" +github "drmohundro/SWXMLHash" "4.7.0" github "jpsim/SourceKitten" "0.21.0" github "jpsim/Yams" "0.7.0" -github "jspahrsummers/xcconfigs" "bb795558a76e5daf3688500055bbcfe243bffa8d" +github "jspahrsummers/xcconfigs" "0.12" github "scottrhoyt/SwiftyTextTable" "0.8.0" diff --git a/Carthage/Checkouts/SWXMLHash b/Carthage/Checkouts/SWXMLHash index 17d992beb3a..2211b35c2e0 160000 --- a/Carthage/Checkouts/SWXMLHash +++ b/Carthage/Checkouts/SWXMLHash @@ -1 +1 @@ -Subproject commit 17d992beb3aaeda403fd35f8d5e70ab1a8124f35 +Subproject commit 2211b35c2e0e8b08493f86ba52b26e530cabb751 diff --git a/Package.swift b/Package.swift index b246c87a92c..eb3ff3e1237 100644 --- a/Package.swift +++ b/Package.swift @@ -38,6 +38,5 @@ let package = Package( "Resources", ] ) - ], - swiftLanguageVersions: [3, 4] + ] ) diff --git a/Rules.md b/Rules.md index c9c7f791813..1d5f2ff79e7 100644 --- a/Rules.md +++ b/Rules.md @@ -28,6 +28,7 @@ * [Empty Parameters](#empty-parameters) * [Empty Parentheses with Trailing Closure](#empty-parentheses-with-trailing-closure) * [Empty String](#empty-string) +* [Empty XCTest Method](#empty-xctest-method) * [Explicit ACL](#explicit-acl) * [Explicit Enum Raw Value](#explicit-enum-raw-value) * [Explicit Init](#explicit-init) @@ -38,12 +39,14 @@ * [Fatal Error Message](#fatal-error-message) * [File Header](#file-header) * [File Line Length](#file-line-length) +* [File Name](#file-name) * [First Where](#first-where) * [For Where](#for-where) * [Force Cast](#force-cast) * [Force Try](#force-try) * [Force Unwrapping](#force-unwrapping) * [Function Body Length](#function-body-length) +* [Function Default Parameter at End](#function-default-parameter-at-end) * [Function Parameter Count](#function-parameter-count) * [Generic Type Name](#generic-type-name) * [Identifier Name](#identifier-name) @@ -427,6 +430,15 @@ private struct DefaultError: Error {} private let bar = 1 ``` +```swift +import XCTest +@testable import DeleteMe + +@available (iOS 11.0, *) +class DeleteMeTests: XCTestCase { +} +``` +
Triggering Examples @@ -759,7 +771,7 @@ Closing brace with closing parenthesis should not have any whitespaces in the mi Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version --- | --- | --- | --- | --- -`closure_end_indentation` | Disabled | No | style | 3.0.0 +`closure_end_indentation` | Disabled | Yes | style | 3.0.0 Closure end should have the same indentation as the line that started it. @@ -4193,6 +4205,166 @@ myString↓ != "" +## Empty XCTest Method + +Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version +--- | --- | --- | --- | --- +`empty_xctest_method` | Disabled | No | lint | 3.0.0 + +Empty XCTest method should be avoided. + +### Examples + +
+Non Triggering Examples + +```swift +class TotoTests: XCTestCase { + var foobar: Foobar? + + override func setUp() { + super.setUp() + foobar = Foobar() + } + + override func tearDown() { + foobar = nil + super.tearDown() + } + + func testFoo() { + XCTAssertTrue(foobar?.foo) + } + + func testBar() { + // comment... + + XCTAssertFalse(foobar?.bar) + + // comment... + } +} +``` + +```swift +class Foobar { + func setUp() {} + + func tearDown() {} + + func testFoo() {} +} +``` + +```swift +class TotoTests: XCTestCase { + func setUp(with object: Foobar) {} + + func tearDown(object: Foobar) {} + + func testFoo(_ foo: Foobar) {} + + func testBar(bar: (String) -> Int) {} +} +``` + +```swift +class TotoTests: XCTestCase { + func testFoo() { XCTAssertTrue(foobar?.foo) } + + func testBar() { XCTAssertFalse(foobar?.bar) } +} +``` + +
+
+Triggering Examples + +```swift +class TotoTests: XCTestCase { + override ↓func setUp() { + } + + override ↓func tearDown() { + + } + + ↓func testFoo() { + + + } + + ↓func testBar() { + + + + } + + func helperFunction() { + } +} +``` + +```swift +class TotoTests: XCTestCase { + override ↓func setUp() {} + + override ↓func tearDown() {} + + ↓func testFoo() {} + + func helperFunction() {} +} +``` + +```swift +class TotoTests: XCTestCase { + override ↓func setUp() { + // comment... + } + + override ↓func tearDown() { + // comment... + // comment... + } + + ↓func testFoo() { + // comment... + + // comment... + + // comment... + } + + ↓func testBar() { + /* + * comment... + * + * comment... + * + * comment... + */ + } + + func helperFunction() { + } +} +``` + +```swift +class FooTests: XCTestCase { + override ↓func setUp() {} +} + +class BarTests: XCTestCase { + ↓func testFoo() {} +} +``` + +
+ + + ## Explicit ACL Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version @@ -6086,6 +6258,16 @@ print("swiftlint") +## File Name + +Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version +--- | --- | --- | --- | --- +`file_name` | Disabled | No | idiomatic | 3.0.0 + +File name should match a type or extension declared in the file (if any). + + + ## First Where Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version @@ -6478,6 +6660,70 @@ Functions bodies should not span too many lines. +## Function Default Parameter at End + +Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version +--- | --- | --- | --- | --- +`function_default_parameter_at_end` | Disabled | No | idiomatic | 3.0.0 + +Prefer to locate parameters with defaults toward the end of the parameter list. + +### Examples + +
+Non Triggering Examples + +```swift +func foo(baz: String, bar: Int = 0) {} +``` + +```swift +func foo(x: String, y: Int = 0, z: CGFloat = 0) {} +``` + +```swift +func foo(bar: String, baz: Int = 0, z: () -> Void) {} +``` + +```swift +func foo(bar: String, z: () -> Void, baz: Int = 0) {} +``` + +```swift +func foo(bar: Int = 0) {} +``` + +```swift +func foo() {} +``` + +```swift +class A: B { + override func foo(bar: Int = 0, baz: String) {} +``` + +```swift +func foo(bar: Int = 0, completion: @escaping CompletionHandler) {} +``` + +```swift +func foo(a: Int, b: CGFloat = 0) { + let block = { (error: Error?) in } +} +``` + +
+
+Triggering Examples + +```swift +↓func foo(bar: Int = 0, baz: String) {} +``` + +
+ + + ## Function Parameter Count Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version @@ -7132,6 +7378,16 @@ foo.map { } ``` +```swift +foo.map({ ↓return $0 + 1}) +``` + +```swift +[1, 2].first(where: { + ↓return true +}) +``` +
@@ -8397,7 +8653,7 @@ Lines should not span too many characters. Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version --- | --- | --- | --- | --- -`literal_expression_end_indentation` | Disabled | No | style | 3.0.0 +`literal_expression_end_indentation` | Disabled | Yes | style | 3.0.0 Array and dictionary literal end should have the same indentation as the line that started it. @@ -8671,6 +8927,22 @@ MARK comment should be in valid format. e.g. '// MARK: ...' or '// MARK: - ...' ↓// MARK - bad ``` +```swift +↓//MARK : bad +``` + +```swift +↓// MARKL: +``` + +```swift +↓// MARKR +``` + +```swift +↓// MARKK - +``` + ```swift ↓//MARK:- Top-Level bad mark ↓//MARK:- Another bad mark @@ -10780,6 +11052,11 @@ let foo: Array ``` +```swift +let model = CustomView, NSAttributedString>() + +``` + ```swift let foo: [String] @@ -10833,6 +11110,21 @@ let doubleValue = -9e-11 ``` +```swift +let foo = GenericType<(UIViewController) -> Void>() + +``` + +```swift +let foo = Foo, Baz>() + +``` + +```swift +let foo = SignalProducer, Error>([ self.signal, next ]).flatten(.concat) + +``` +
Triggering Examples @@ -11793,18 +12085,28 @@ Some methods should not call super ```swift class VC: UIViewController { - override func loadView() { - } + override func loadView() { + } } - ``` ```swift class NSView { - func updateLayer() { - self.method1() } + func updateLayer() { + self.method1() + } } +``` +```swift +public class FileProviderExtension: NSFileProviderExtension { + override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) { + guard let identifier = persistentIdentifierForItem(at: url) else { + completionHandler(NSFileProviderError(.noSuchItem)) + return + } + } +} ```
@@ -11813,43 +12115,39 @@ class NSView { ```swift class VC: UIViewController { - override func loadView() {↓ - super.loadView() - } + override func loadView() {↓ + super.loadView() + } } - ``` ```swift class VC: NSFileProviderExtension { - override func providePlaceholder(at url: URL,completionHandler: @escaping (Error?) -> Void) {↓ - self.method1() - super.providePlaceholder(at:url, completionHandler: completionHandler) - } + override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) {↓ + self.method1() + super.providePlaceholder(at:url, completionHandler: completionHandler) + } } - ``` ```swift class VC: NSView { - override func updateLayer() {↓ - self.method1() - super.updateLayer() - self.method2() - } + override func updateLayer() {↓ + self.method1() + super.updateLayer() + self.method2() + } } - ``` ```swift class VC: NSView { - override func updateLayer() {↓ - defer { - super.updateLayer() - } - } + override func updateLayer() {↓ + defer { + super.updateLayer() + } + } } - ``` @@ -18134,6 +18432,32 @@ foo.map { ($0, $0) }.forEach { ↓(x, y) in } foo.bar { [weak self] ↓(x, y) in } ``` +```swift +[].first { ↓(temp) in + [].first { ↓(temp) in + [].first { ↓(temp) in + _ = temp + return false + } + return false + } + return false +} +``` + +```swift +[].first { temp in + [].first { ↓(temp) in + [].first { ↓(temp) in + _ = temp + return false + } + return false + } + return false +} +``` + @@ -18142,7 +18466,7 @@ foo.bar { [weak self] ↓(x, y) in } Identifier | Enabled by default | Supports autocorrection | Kind | Minimum Swift Compiler Version --- | --- | --- | --- | --- -`untyped_error_in_catch` | Disabled | No | idiomatic | 3.0.0 +`untyped_error_in_catch` | Disabled | Yes | idiomatic | 3.0.0 Catch statements should not declare error variables without type casting. @@ -18320,6 +18644,12 @@ hoge(arg: num) { num in })(FileManager.default) ``` +```swift +withPostSideEffect { input in + if true { print("\(input)") } +} +``` +
Triggering Examples diff --git a/Source/SwiftLintFramework/Extensions/Configuration+Merging.swift b/Source/SwiftLintFramework/Extensions/Configuration+Merging.swift index 2337fc7991a..ecbf4316c0d 100644 --- a/Source/SwiftLintFramework/Extensions/Configuration+Merging.swift +++ b/Source/SwiftLintFramework/Extensions/Configuration+Merging.swift @@ -46,11 +46,11 @@ extension Configuration { } let isDirectory: Bool - #if os(Linux) && (!swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3))) - isDirectory = isDirectoryObjC - #else - isDirectory = isDirectoryObjC.boolValue - #endif +#if os(Linux) && !swift(>=4.1) + isDirectory = isDirectoryObjC +#else + isDirectory = isDirectoryObjC.boolValue +#endif if isDirectory { return rootPath diff --git a/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift index d77cda667dc..6fd518afb2a 100644 --- a/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift @@ -122,11 +122,14 @@ extension Dictionary where Key: ExpressibleByStringLiteral { } internal func extractCallsToSuper(methodName: String) -> [String] { - let superCall = "super.\(methodName)" + guard let methodNameWithoutArguments = methodName.split(separator: "(").first else { + return [] + } + let superCall = "super.\(methodNameWithoutArguments)" return substructure.flatMap { elems -> [String] in - guard let type = elems.kind.flatMap({ SwiftExpressionKind(rawValue: $0) }), + guard let type = elems.kind.flatMap(SwiftExpressionKind.init), let name = elems.name, - type == .call && superCall.contains(name) else { + type == .call && superCall == name else { return elems.extractCallsToSuper(methodName: methodName) } return [name] diff --git a/Source/SwiftLintFramework/Extensions/NSFileManager+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift similarity index 100% rename from Source/SwiftLintFramework/Extensions/NSFileManager+SwiftLint.swift rename to Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift diff --git a/Source/SwiftLintFramework/Extensions/NSRegularExpression+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/NSRegularExpression+SwiftLint.swift index a38656d7986..1c3ddaea565 100644 --- a/Source/SwiftLintFramework/Extensions/NSRegularExpression+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/NSRegularExpression+SwiftLint.swift @@ -1,19 +1,5 @@ import Foundation -#if os(Linux) -#if !swift(>=4.0) -public typealias NSTextCheckingResult = TextCheckingResult -#endif -#else -#if !swift(>=4.0) -extension NSTextCheckingResult { - internal func range(at idx: Int) -> NSRange { - return rangeAt(idx) - } -} -#endif -#endif - private var regexCache = [RegexCacheKey: NSRegularExpression]() private let regexCacheLock = NSLock() diff --git a/Source/SwiftLintFramework/Extensions/String+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/String+SwiftLint.swift index 7fb38c017fe..18681e69c05 100644 --- a/Source/SwiftLintFramework/Extensions/String+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/String+SwiftLint.swift @@ -78,11 +78,11 @@ extension String { internal var isFile: Bool { var isDirectoryObjC: ObjCBool = false if FileManager.default.fileExists(atPath: self, isDirectory: &isDirectoryObjC) { - #if os(Linux) && (!swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3))) - return !isDirectoryObjC - #else - return !isDirectoryObjC.boolValue - #endif +#if os(Linux) && !swift(>=4.1) + return !isDirectoryObjC +#else + return !isDirectoryObjC.boolValue +#endif } return false } diff --git a/Source/SwiftLintFramework/Extensions/SyntaxMap+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/SyntaxMap+SwiftLint.swift index faf21fc0b53..3c491ee88d6 100644 --- a/Source/SwiftLintFramework/Extensions/SyntaxMap+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/SyntaxMap+SwiftLint.swift @@ -12,18 +12,11 @@ extension SyntaxMap { .intersects(byteRange) } - func notIntersect(_ token: SyntaxToken) -> Bool { - return !intersect(token) - } - guard let startIndex = tokens.index(where: intersect) else { return [] } let tokensBeginningIntersect = tokens.lazy.suffix(from: startIndex) - if let endIndex = tokensBeginningIntersect.index(where: notIntersect) { - return Array(tokensBeginningIntersect.prefix(upTo: endIndex)) - } - return Array(tokensBeginningIntersect) + return Array(tokensBeginningIntersect.filter(intersect)) } internal func kinds(inByteRange byteRange: NSRange) -> [SyntaxKind] { diff --git a/Source/SwiftLintFramework/Extensions/shim.swift b/Source/SwiftLintFramework/Extensions/shim.swift index 2b6edaa89b4..d7817537fd2 100644 --- a/Source/SwiftLintFramework/Extensions/shim.swift +++ b/Source/SwiftLintFramework/Extensions/shim.swift @@ -1,11 +1,11 @@ -#if (!swift(>=4.1) && swift(>=4.0)) || !swift(>=3.3) +#if !swift(>=4.1) - extension Sequence { - func compactMap( - _ transform: (Self.Element - ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { - return try flatMap(transform) - } +extension Sequence { + func compactMap( + _ transform: (Self.Element + ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { + return try flatMap(transform) } +} #endif diff --git a/Source/SwiftLintFramework/Models/Configuration.swift b/Source/SwiftLintFramework/Models/Configuration.swift index 86464cf87ca..9d975b78cf8 100644 --- a/Source/SwiftLintFramework/Models/Configuration.swift +++ b/Source/SwiftLintFramework/Models/Configuration.swift @@ -253,11 +253,11 @@ private extension String { func isDirectory() -> Bool { var isDir: ObjCBool = false if FileManager.default.fileExists(atPath: self, isDirectory: &isDir) { - #if os(Linux) && (!swift(>=4.1) || (!swift(>=4.0) && swift(>=3.3))) - return isDir - #else - return isDir.boolValue - #endif +#if os(Linux) && !swift(>=4.1) + return isDir +#else + return isDir.boolValue +#endif } return false diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index f3329c10de6..47b74b03dce 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -29,6 +29,7 @@ public let masterRuleList = RuleList(rules: [ EmptyParametersRule.self, EmptyParenthesesWithTrailingClosureRule.self, EmptyStringRule.self, + EmptyXCTestMethodRule.self, ExplicitACLRule.self, ExplicitEnumRawValueRule.self, ExplicitInitRule.self, @@ -39,12 +40,14 @@ public let masterRuleList = RuleList(rules: [ FatalErrorMessageRule.self, FileHeaderRule.self, FileLengthRule.self, + FileNameRule.self, FirstWhereRule.self, ForWhereRule.self, ForceCastRule.self, ForceTryRule.self, ForceUnwrappingRule.self, FunctionBodyLengthRule.self, + FunctionDefaultParameterAtEndRule.self, FunctionParameterCountRule.self, GenericTypeNameRule.self, IdentifierNameRule.self, diff --git a/Source/SwiftLintFramework/Models/SwiftVersion.swift b/Source/SwiftLintFramework/Models/SwiftVersion.swift index 7b97a37fa4b..c43583f4f61 100644 --- a/Source/SwiftLintFramework/Models/SwiftVersion.swift +++ b/Source/SwiftLintFramework/Models/SwiftVersion.swift @@ -37,6 +37,8 @@ public extension SwiftVersion { let file = File(contents: """ #if swift(>=4.2.0) let version = "4.2.0" + #elseif swift(>=4.1.2) + let version = "4.1.2" #elseif swift(>=4.1.1) let version = "4.1.1" #elseif swift(>=4.1.0) @@ -51,6 +53,8 @@ public extension SwiftVersion { let version = "4.0.0" #elseif swift(>=3.4.0) let version = "3.4.0" + #elseif swift(>=3.3.2) + let version = "3.3.2" #elseif swift(>=3.3.1) let version = "3.3.1" #elseif swift(>=3.3.0) diff --git a/Source/SwiftLintFramework/Rules/AttributesRule.swift b/Source/SwiftLintFramework/Rules/AttributesRule.swift index 75a3dd202d9..fb05aaeca79 100644 --- a/Source/SwiftLintFramework/Rules/AttributesRule.swift +++ b/Source/SwiftLintFramework/Rules/AttributesRule.swift @@ -209,7 +209,7 @@ public struct AttributesRule: ASTRule, OptInRule, ConfigurationProviderRule { return false } - return ["func", "var", "let"].contains(keyword) + return ["func", "var", "let", "import"].contains(keyword) } guard nonAttributeTokens.isEmpty else { diff --git a/Source/SwiftLintFramework/Rules/AttributesRulesExamples.swift b/Source/SwiftLintFramework/Rules/AttributesRuleExamples.swift similarity index 94% rename from Source/SwiftLintFramework/Rules/AttributesRulesExamples.swift rename to Source/SwiftLintFramework/Rules/AttributesRuleExamples.swift index eea45ae3a5e..700092abf38 100644 --- a/Source/SwiftLintFramework/Rules/AttributesRulesExamples.swift +++ b/Source/SwiftLintFramework/Rules/AttributesRuleExamples.swift @@ -38,7 +38,15 @@ internal struct AttributesRuleExamples { "func increase(f: @autoclosure () -> Int) -> Int", "func foo(completionHandler: @escaping () -> Void)", "private struct DefaultError: Error {}", - "@testable import foo\n\nprivate let bar = 1" + "@testable import foo\n\nprivate let bar = 1", + """ + import XCTest + @testable import DeleteMe + + @available (iOS 11.0, *) + class DeleteMeTests: XCTestCase { + } + """ ] static let triggeringExamples = [ diff --git a/Source/SwiftLintFramework/Rules/ClosureEndIndentationRule.swift b/Source/SwiftLintFramework/Rules/ClosureEndIndentationRule.swift index b4ad7b043ac..e1fcb0ee46f 100644 --- a/Source/SwiftLintFramework/Rules/ClosureEndIndentationRule.swift +++ b/Source/SwiftLintFramework/Rules/ClosureEndIndentationRule.swift @@ -1,65 +1,7 @@ import Foundation import SourceKittenFramework -internal struct ClosureEndIndentationRuleExamples { - - static let nonTriggeringExamples = [ - "SignalProducer(values: [1, 2, 3])\n" + - " .startWithNext { number in\n" + - " print(number)\n" + - " }\n", - "[1, 2].map { $0 + 1 }\n", - "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + - " return Command(string: contents, range: range)\n" + - "}.flatMap { command in\n" + - " return command.expand()\n" + - "}\n", - "foo(foo: bar,\n" + - " options: baz) { _ in }\n", - "someReallyLongProperty.chainingWithAnotherProperty\n" + - " .foo { _ in }", - "foo(abc, 123)\n" + - "{ _ in }\n", - "function(\n" + - " closure: { x in\n" + - " print(x)\n" + - " },\n" + - " anotherClosure: { y in\n" + - " print(y)\n" + - " })", - "function(parameter: param,\n" + - " closure: { x in\n" + - " print(x)\n" + - "})", - "function(parameter: param, closure: { x in\n" + - " print(x)\n" + - " },\n" + - " anotherClosure: { y in\n" + - " print(y)\n" + - " })" - ] - - static let triggeringExamples = [ - "SignalProducer(values: [1, 2, 3])\n" + - " .startWithNext { number in\n" + - " print(number)\n" + - "↓}\n", - "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + - " return Command(string: contents, range: range)\n" + - " ↓}.flatMap { command in\n" + - " return command.expand()\n" + - "↓}\n", - "function(\n" + - " closure: { x in\n" + - " print(x)\n" + - "↓},\n" + - " anotherClosure: { y in\n" + - " print(y)\n" + - "↓})" - ] -} - -public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProviderRule { +public struct ClosureEndIndentationRule: Rule, OptInRule, ConfigurationProviderRule { public var configuration = SeverityConfiguration(.warning) public init() {} @@ -70,19 +12,139 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid description: "Closure end should have the same indentation as the line that started it.", kind: .style, nonTriggeringExamples: ClosureEndIndentationRuleExamples.nonTriggeringExamples, - triggeringExamples: ClosureEndIndentationRuleExamples.triggeringExamples + triggeringExamples: ClosureEndIndentationRuleExamples.triggeringExamples, + corrections: ClosureEndIndentationRuleExamples.corrections ) - private static let notWhitespace = regex("[^\\s]") + fileprivate static let notWhitespace = regex("[^\\s]") + + public func validate(file: File) -> [StyleViolation] { + return violations(in: file).map { violation in + return styleViolation(for: violation, in: file) + } + } + + private func styleViolation(for violation: Violation, in file: File) -> StyleViolation { + let reason = "Closure end should have the same indentation as the line that started it. " + + "Expected \(violation.indentationRanges.expected.length), " + + "got \(violation.indentationRanges.actual.length)." + + return StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: violation.endOffset), + reason: reason) + } + +} + +extension ClosureEndIndentationRule: CorrectableRule { + public func correct(file: File) -> [Correction] { + let allViolations = violations(in: file).reversed().filter { + !file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty + } + + guard !allViolations.isEmpty else { + return [] + } - public func validate(file: File, kind: SwiftExpressionKind, - dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + var correctedContents = file.contents + var correctedLocations: [Int] = [] + + let actualLookup = actualViolationLookup(for: allViolations) + + for violation in allViolations { + let expected = actualLookup(violation).indentationRanges.expected + let actual = violation.indentationRanges.actual + if correct(contents: &correctedContents, expected: expected, actual: actual) { + correctedLocations.append(actual.location) + } + } + + var corrections = correctedLocations.map { + return Correction(ruleDescription: type(of: self).description, + location: Location(file: file, characterOffset: $0)) + } + + file.write(correctedContents) + + // Re-correct to catch cascading indentation from the first round. + corrections += correct(file: file) + + return corrections + } + + private func correct(contents: inout String, expected: NSRange, actual: NSRange) -> Bool { + guard let actualIndices = contents.nsrangeToIndexRange(actual) else { + return false + } + + let regex = ClosureEndIndentationRule.notWhitespace + if regex.firstMatch(in: contents, options: [], range: actual) != nil { + var correction = "\n" + correction.append(contents.substring(from: expected.location, length: expected.length)) + contents.insert(contentsOf: correction, at: actualIndices.upperBound) + } else { + let correction = contents.substring(from: expected.location, length: expected.length) + contents = contents.replacingCharacters(in: actualIndices, with: correction) + } + + return true + } + + private func actualViolationLookup(for violations: [Violation]) -> (Violation) -> Violation { + let lookup = violations.reduce(into: [NSRange: Violation](), { result, violation in + result[violation.indentationRanges.actual] = violation + }) + + func actualViolation(for violation: Violation) -> Violation { + guard let actual = lookup[violation.indentationRanges.expected] else { return violation } + return actualViolation(for: actual) + } + + return actualViolation + } +} + +extension ClosureEndIndentationRule { + + fileprivate struct Violation { + var location: Location + var indentationRanges: (expected: NSRange, actual: NSRange) + var endOffset: Int + var range: NSRange + } + + fileprivate func violations(in file: File) -> [Violation] { + return violations(in: file, dictionary: file.structure.dictionary) + } + + private func violations(in file: File, + dictionary: [String: SourceKitRepresentable]) -> [Violation] { + return dictionary.substructure.flatMap { subDict -> [Violation] in + var subViolations = violations(in: file, dictionary: subDict) + + if let kindString = subDict.kind, + let kind = SwiftExpressionKind(rawValue: kindString) { + subViolations += violations(in: file, of: kind, dictionary: subDict) + } + + return subViolations + } + } + + private func violations(in file: File, of kind: SwiftExpressionKind, + dictionary: [String: SourceKitRepresentable]) -> [Violation] { guard kind == .call else { return [] } - return validateArguments(in: file, dictionary: dictionary) + - validateCall(in: file, dictionary: dictionary) + var violations = validateArguments(in: file, dictionary: dictionary) + + if let callViolation = validateCall(in: file, dictionary: dictionary) { + violations.append(callViolation) + } + + return violations } private func hasTrailingClosure(in file: File, @@ -99,7 +161,7 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid } private func validateCall(in file: File, - dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + dictionary: [String: SourceKitRepresentable]) -> Violation? { let contents = file.contents.bridge() guard let offset = dictionary.offset, let length = dictionary.length, @@ -116,7 +178,7 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), startLine != endLine, bodyOffsetLine != endLine, !containsSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { - return [] + return nil } let range = file.lines[startLine - 1].range @@ -125,21 +187,23 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, expected != actual else { - return [] + return nil } - let reason = "Closure end should have the same indentation as the line that started it. " + - "Expected \(expected), got \(actual)." - return [ - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severity, - location: Location(file: file, byteOffset: endOffset), - reason: reason) - ] + var expectedRange = range + expectedRange.length = expected + + var actualRange = file.lines[endLine - 1].range + actualRange.length = actual + + return Violation(location: Location(file: file, byteOffset: endOffset), + indentationRanges: (expected: expectedRange, actual: actualRange), + endOffset: endOffset, + range: NSRange(location: offset, length: length)) } - func validateArguments(in file: File, - dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + private func validateArguments(in file: File, + dictionary: [String: SourceKitRepresentable]) -> [Violation] { guard isFirstArgumentOnNewline(dictionary, file: file) else { return [] } @@ -150,7 +214,7 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid closureArguments.removeLast() } - let argumentViolations = closureArguments.flatMap { dictionary in + let argumentViolations = closureArguments.compactMap { dictionary in return validateClosureArgument(in: file, dictionary: dictionary) } @@ -158,7 +222,7 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid } private func validateClosureArgument(in file: File, - dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + dictionary: [String: SourceKitRepresentable]) -> Violation? { let contents = file.contents.bridge() guard let offset = dictionary.offset, let length = dictionary.length, @@ -175,7 +239,7 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid let (bodyOffsetLine, _) = contents.lineAndCharacter(forByteOffset: nameEndPosition), startLine != endLine, bodyOffsetLine != endLine, !isSingleLineClosure(dictionary: dictionary, endPosition: endOffset, file: file) else { - return [] + return nil } let range = file.lines[startLine - 1].range @@ -184,17 +248,19 @@ public struct ClosureEndIndentationRule: ASTRule, OptInRule, ConfigurationProvid guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, expected != actual else { - return [] + return nil } - let reason = "Closure end should have the same indentation as the line that started it. " + - "Expected \(expected), got \(actual)." - return [ - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severity, - location: Location(file: file, byteOffset: endOffset), - reason: reason) - ] + var expectedRange = range + expectedRange.length = expected + + var actualRange = file.lines[endLine - 1].range + actualRange.length = actual + + return Violation(location: Location(file: file, byteOffset: endOffset), + indentationRanges: (expected: expectedRange, actual: actualRange), + endOffset: endOffset, + range: NSRange(location: offset, length: length)) } private func startOffset(forDictionary dictionary: [String: SourceKitRepresentable], file: File) -> Int? { diff --git a/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRuleExamples.swift b/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionExamples.swift similarity index 100% rename from Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRuleExamples.swift rename to Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionExamples.swift diff --git a/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRule.swift b/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRule.swift index 52bed16f7b7..f8849a5258c 100644 --- a/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRule.swift +++ b/Source/SwiftLintFramework/Rules/DiscouragedOptionalCollectionRule.swift @@ -99,7 +99,7 @@ private extension String { let finalIndex = index(range.upperBound, offsetBy: 1, limitedBy: endIndex), self[range.upperBound] == "?" else { return nil } - return Range(range.lowerBound..").compactMap { range -> Range? in @@ -110,7 +110,7 @@ private extension String { self[initialIndex.. [StyleViolation] { + return testClasses(in: file).flatMap { violations(in: file, for: $0) } + } + + // MARK: - Private + + private func testClasses(in file: File) -> [[String: SourceKitRepresentable]] { + return file.structure.dictionary.substructure.filter { dictionary in + guard + let kind = dictionary.kind, + SwiftDeclarationKind(rawValue: kind) == .class else { return false } + return dictionary.inheritedTypes.contains("XCTestCase") + } + } + + private func violations(in file: File, + for dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + return dictionary.substructure.compactMap { subDictionary -> StyleViolation? in + guard + let kind = subDictionary.kind.flatMap(SwiftDeclarationKind.init), + SwiftDeclarationKind.functionKinds.contains(kind), + let name = subDictionary.name, isXCTestMethod(name), + let offset = subDictionary.offset, + subDictionary.enclosedVarParameters.isEmpty, + subDictionary.substructure.isEmpty else { return nil } + + return StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset)) + } + } + + private func isXCTestMethod(_ method: String) -> Bool { + return method.hasPrefix("test") || method == "setUp()" || method == "tearDown()" + } +} diff --git a/Source/SwiftLintFramework/Rules/EmptyXCTestMethodRuleExamples.swift b/Source/SwiftLintFramework/Rules/EmptyXCTestMethodRuleExamples.swift new file mode 100644 index 00000000000..61f756af7b2 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/EmptyXCTestMethodRuleExamples.swift @@ -0,0 +1,163 @@ +import Foundation + +internal struct EmptyXCTestMethodRuleExamples { + + static let nonTriggeringExamples = [ + + // Valid XCTestCase class + + """ + class TotoTests: XCTestCase { + var foobar: Foobar? + + override func setUp() { + super.setUp() + foobar = Foobar() + } + + override func tearDown() { + foobar = nil + super.tearDown() + } + + func testFoo() { + XCTAssertTrue(foobar?.foo) + } + + func testBar() { + // comment... + + XCTAssertFalse(foobar?.bar) + + // comment... + } + } + """, + + // Not an XCTestCase class + + """ + class Foobar { + func setUp() {} + + func tearDown() {} + + func testFoo() {} + } + """, + + // Methods with parameters + + """ + class TotoTests: XCTestCase { + func setUp(with object: Foobar) {} + + func tearDown(object: Foobar) {} + + func testFoo(_ foo: Foobar) {} + + func testBar(bar: (String) -> Int) {} + } + """, + + // Asserts in one line + + """ + class TotoTests: XCTestCase { + func testFoo() { XCTAssertTrue(foobar?.foo) } + + func testBar() { XCTAssertFalse(foobar?.bar) } + } + """ + ] + + static let triggeringExamples = [ + + // XCTestCase class with empty methods + + """ + class TotoTests: XCTestCase { + override ↓func setUp() { + } + + override ↓func tearDown() { + + } + + ↓func testFoo() { + + + } + + ↓func testBar() { + + + + } + + func helperFunction() { + } + } + """, + + """ + class TotoTests: XCTestCase { + override ↓func setUp() {} + + override ↓func tearDown() {} + + ↓func testFoo() {} + + func helperFunction() {} + } + """, + + // XCTestCase class with comments (and blank lines) + + """ + class TotoTests: XCTestCase { + override ↓func setUp() { + // comment... + } + + override ↓func tearDown() { + // comment... + // comment... + } + + ↓func testFoo() { + // comment... + + // comment... + + // comment... + } + + ↓func testBar() { + /* + * comment... + * + * comment... + * + * comment... + */ + } + + func helperFunction() { + } + } + """, + + // Two XCTestCase classes on the same file + + """ + class FooTests: XCTestCase { + override ↓func setUp() {} + } + + class BarTests: XCTestCase { + ↓func testFoo() {} + } + """ + ] +} diff --git a/Source/SwiftLintFramework/Rules/FileNameRule.swift b/Source/SwiftLintFramework/Rules/FileNameRule.swift new file mode 100644 index 00000000000..6b61804427d --- /dev/null +++ b/Source/SwiftLintFramework/Rules/FileNameRule.swift @@ -0,0 +1,47 @@ +import Foundation +import SourceKittenFramework + +private let typeAndExtensionKinds = SwiftDeclarationKind.typeKinds + [.extension, .protocol] + +private extension Dictionary where Key: ExpressibleByStringLiteral { + func recursiveDeclaredTypeNames() -> [String] { + let subNames = substructure.flatMap { $0.recursiveDeclaredTypeNames() } + if let kind = kind.flatMap(SwiftDeclarationKind.init), + typeAndExtensionKinds.contains(kind), let name = name { + return [name] + subNames + } + return subNames + } +} + +public struct FileNameRule: ConfigurationProviderRule, OptInRule { + public var configuration = FileNameConfiguration(severity: .warning, excluded: ["main.swift", "LinuxMain.swift"]) + + public init() {} + + public static let description = RuleDescription( + identifier: "file_name", + name: "File Name", + description: "File name should match a type or extension declared in the file (if any).", + kind: .idiomatic + ) + + public func validate(file: File) -> [StyleViolation] { + guard let filePath = file.path, + case let fileName = filePath.bridge().lastPathComponent, + !configuration.excluded.contains(fileName) else { + return [] + } + + let typeInFileName = fileName.components(separatedBy: CharacterSet(charactersIn: "+.")).first ?? fileName + + let allDeclaredTypeNames = file.structure.dictionary.recursiveDeclaredTypeNames() + guard !allDeclaredTypeNames.isEmpty, !allDeclaredTypeNames.contains(typeInFileName) else { + return [] + } + + return [StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity.severity, + location: Location(file: filePath, line: 1))] + } +} diff --git a/Source/SwiftLintFramework/Rules/FunctionDefaultParameterAtEndRule.swift b/Source/SwiftLintFramework/Rules/FunctionDefaultParameterAtEndRule.swift new file mode 100644 index 00000000000..094831d84cc --- /dev/null +++ b/Source/SwiftLintFramework/Rules/FunctionDefaultParameterAtEndRule.swift @@ -0,0 +1,95 @@ +import SourceKittenFramework + +public struct FunctionDefaultParameterAtEndRule: ASTRule, ConfigurationProviderRule, OptInRule { + public var configuration = SeverityConfiguration(.warning) + + public init() {} + + public static let description = RuleDescription( + identifier: "function_default_parameter_at_end", + name: "Function Default Parameter at End", + description: "Prefer to locate parameters with defaults toward the end of the parameter list.", + kind: .idiomatic, + nonTriggeringExamples: [ + "func foo(baz: String, bar: Int = 0) {}", + "func foo(x: String, y: Int = 0, z: CGFloat = 0) {}", + "func foo(bar: String, baz: Int = 0, z: () -> Void) {}", + "func foo(bar: String, z: () -> Void, baz: Int = 0) {}", + "func foo(bar: Int = 0) {}", + "func foo() {}", + """ + class A: B { + override func foo(bar: Int = 0, baz: String) {} + """, + "func foo(bar: Int = 0, completion: @escaping CompletionHandler) {}", + """ + func foo(a: Int, b: CGFloat = 0) { + let block = { (error: Error?) in } + } + """ + ], + triggeringExamples: [ + "↓func foo(bar: Int = 0, baz: String) {}" + ] + ) + + public func validate(file: File, kind: SwiftDeclarationKind, + dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + guard SwiftDeclarationKind.functionKinds.contains(kind), + let offset = dictionary.offset, + let bodyOffset = dictionary.bodyOffset, + !dictionary.enclosedSwiftAttributes.contains(.override) else { + return [] + } + + let isNotClosure = { !self.isClosureParameter(dictionary: $0) } + let params = dictionary.enclosedVarParameters.filter(isNotClosure).filter { param in + guard let paramOffset = param.offset else { + return false + } + + return paramOffset < bodyOffset + } + + guard !params.isEmpty else { + return [] + } + + let containsDefaultValue = { self.isDefaultParameter(file: file, dictionary: $0) } + let defaultParams = params.filter(containsDefaultValue) + guard !defaultParams.isEmpty else { + return [] + } + + let lastParameters = params.suffix(defaultParams.count) + let lastParametersWithDefaultValue = lastParameters.filter(containsDefaultValue) + + guard lastParameters.count != lastParametersWithDefaultValue.count else { + return [] + } + + return [ + StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset)) + ] + } + + private func isClosureParameter(dictionary: [String: SourceKitRepresentable]) -> Bool { + guard let typeName = dictionary.typeName else { + return false + } + + return typeName.contains("->") || typeName.contains("@escaping") + } + + private func isDefaultParameter(file: File, dictionary: [String: SourceKitRepresentable]) -> Bool { + let contents = file.contents.bridge() + guard let offset = dictionary.offset, let length = dictionary.length, + let range = contents.byteRangeToNSRange(start: offset, length: length) else { + return false + } + + return regex("=").firstMatch(in: file.contents, options: [], range: range) != nil + } +} diff --git a/Source/SwiftLintFramework/Rules/FunctionParameterCountRule.swift b/Source/SwiftLintFramework/Rules/FunctionParameterCountRule.swift index 2f7ff556283..f23648b74c7 100644 --- a/Source/SwiftLintFramework/Rules/FunctionParameterCountRule.swift +++ b/Source/SwiftLintFramework/Rules/FunctionParameterCountRule.swift @@ -2,7 +2,7 @@ import Foundation import SourceKittenFramework public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { - public var configuration = SeverityLevelsConfiguration(warning: 5, error: 8) + public var configuration = FunctionParameterCountConfiguration(warning: 5, error: 8) public init() {} @@ -51,7 +51,7 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { return [] } - let minThreshold = configuration.params.map({ $0.value }).min(by: <) + let minThreshold = configuration.severityConfiguration.params.map({ $0.value }).min(by: <) let allParameterCount = allFunctionParameterCount(structure: dictionary.substructure, offset: nameOffset, length: length) @@ -59,12 +59,15 @@ public struct FunctionParameterCountRule: ASTRule, ConfigurationProviderRule { return [] } - let parameterCount = allParameterCount - - defaultFunctionParameterCount(file: file, byteOffset: nameOffset, byteLength: length) + var parameterCount = allParameterCount - for parameter in configuration.params where parameterCount > parameter.value { + if configuration.ignoresDefaultParameters { + parameterCount -= defaultFunctionParameterCount(file: file, byteOffset: nameOffset, byteLength: length) + } + + for parameter in configuration.severityConfiguration.params where parameterCount > parameter.value { let offset = dictionary.offset ?? 0 - let reason = "Function should have \(configuration.warning) parameters or less: " + + let reason = "Function should have \(configuration.severityConfiguration.warning) parameters or less: " + "it currently has \(parameterCount)" return [StyleViolation(ruleDescription: type(of: self).description, severity: parameter.severity, diff --git a/Source/SwiftLintFramework/Rules/ImplicitReturnRule.swift b/Source/SwiftLintFramework/Rules/ImplicitReturnRule.swift index d32213dcbad..f12bcb3213c 100644 --- a/Source/SwiftLintFramework/Rules/ImplicitReturnRule.swift +++ b/Source/SwiftLintFramework/Rules/ImplicitReturnRule.swift @@ -21,11 +21,28 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, CorrectableRule, Op ], triggeringExamples: [ "foo.map { value in\n ↓return value + 1\n}", - "foo.map {\n ↓return $0 + 1\n}" + "foo.map {\n ↓return $0 + 1\n}", + "foo.map({ ↓return $0 + 1})", + """ + [1, 2].first(where: { + ↓return true + }) + """ ], corrections: [ "foo.map { value in\n ↓return value + 1\n}": "foo.map { value in\n value + 1\n}", - "foo.map {\n ↓return $0 + 1\n}": "foo.map {\n $0 + 1\n}" + "foo.map {\n ↓return $0 + 1\n}": "foo.map {\n $0 + 1\n}", + "foo.map({ ↓return $0 + 1})": "foo.map({ $0 + 1})", + """ + [1, 2].first(where: { + ↓return true + }) + """: + """ + [1, 2].first(where: { + true + }) + """ ] ) @@ -66,8 +83,9 @@ public struct ImplicitReturnRule: ConfigurationProviderRule, CorrectableRule, Op guard kinds == [.keyword, .keyword] || kinds == [.keyword], let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length), - let outerKind = file.structure.kinds(forByteOffset: byteRange.location).last, - SwiftExpressionKind(rawValue: outerKind.kind) == .call else { + let outerKindString = file.structure.kinds(forByteOffset: byteRange.location).last?.kind, + let outerKind = SwiftExpressionKind(rawValue: outerKindString), + [.call, .argument].contains(outerKind) else { return nil } diff --git a/Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift b/Source/SwiftLintFramework/Rules/JoinedDefaultParameterRule.swift similarity index 100% rename from Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift rename to Source/SwiftLintFramework/Rules/JoinedDefaultParameterRule.swift diff --git a/Source/SwiftLintFramework/Rules/LiteralExpressionEndIdentationRule.swift b/Source/SwiftLintFramework/Rules/LiteralExpressionEndIdentationRule.swift index d13dbaa33aa..f00615010a6 100644 --- a/Source/SwiftLintFramework/Rules/LiteralExpressionEndIdentationRule.swift +++ b/Source/SwiftLintFramework/Rules/LiteralExpressionEndIdentationRule.swift @@ -1,7 +1,7 @@ import Foundation import SourceKittenFramework -public struct LiteralExpressionEndIdentationRule: ASTRule, ConfigurationProviderRule, OptInRule { +public struct LiteralExpressionEndIdentationRule: Rule, ConfigurationProviderRule, OptInRule { public var configuration = SeverityConfiguration(.warning) public init() {} @@ -48,17 +48,162 @@ public struct LiteralExpressionEndIdentationRule: ASTRule, ConfigurationProvider "let x = [\n" + " key: value\n" + " ↓]" + ], + corrections: [ + "let x = [\n" + + " key: value\n" + + "↓ ]": + "let x = [\n" + + " key: value\n" + + "]", + " let x = [\n" + + " 1,\n" + + " 2\n" + + "↓]": + " let x = [\n" + + " 1,\n" + + " 2\n" + + " ]", + "let x = [\n" + + " 1,\n" + + " 2\n" + + "↓ ]": + "let x = [\n" + + " 1,\n" + + " 2\n" + + "]", + "let x = [\n" + + " 1,\n" + + " 2\n" + + "↓ ] + [\n" + + " 3,\n" + + " 4\n" + + "↓ ]": + "let x = [\n" + + " 1,\n" + + " 2\n" + + "] + [\n" + + " 3,\n" + + " 4\n" + + "]" ] ) - private static let notWhitespace = regex("[^\\s]") + public func validate(file: File) -> [StyleViolation] { + return violations(in: file).map { violation in + return styleViolation(for: violation, in: file) + } + } - public func validate(file: File, kind: SwiftExpressionKind, - dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { - guard kind == .dictionary || kind == .array else { + private func styleViolation(for violation: Violation, in file: File) -> StyleViolation { + let reason = "\(LiteralExpressionEndIdentationRule.description.description) " + + "Expected \(violation.indentationRanges.expected.length), " + + "got \(violation.indentationRanges.actual.length)." + + return StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: violation.endOffset), + reason: reason) + } + + fileprivate static let notWhitespace = regex("[^\\s]") + +} + +extension LiteralExpressionEndIdentationRule: CorrectableRule { + public func correct(file: File) -> [Correction] { + let allViolations = violations(in: file).reversed().filter { + !file.ruleEnabled(violatingRanges: [$0.range], for: self).isEmpty + } + + guard !allViolations.isEmpty else { return [] } + var correctedContents = file.contents + var correctedLocations: [Int] = [] + + let actualLookup = actualViolationLookup(for: allViolations) + + for violation in allViolations { + let expected = actualLookup(violation).indentationRanges.expected + let actual = violation.indentationRanges.actual + if correct(contents: &correctedContents, expected: expected, actual: actual) { + correctedLocations.append(actual.location) + } + } + + var corrections = correctedLocations.map { + return Correction(ruleDescription: type(of: self).description, + location: Location(file: file, characterOffset: $0)) + } + + file.write(correctedContents) + + // Re-correct to catch cascading indentation from the first round. + corrections += correct(file: file) + + return corrections + } + + private func correct(contents: inout String, expected: NSRange, actual: NSRange) -> Bool { + guard let actualIndices = contents.nsrangeToIndexRange(actual) else { + return false + } + + let correction = contents.substring(from: expected.location, length: expected.length) + contents = contents.replacingCharacters(in: actualIndices, with: correction) + + return true + } + + private func actualViolationLookup(for violations: [Violation]) -> (Violation) -> Violation { + let lookup = violations.reduce(into: [NSRange: Violation](), { result, violation in + result[violation.indentationRanges.actual] = violation + }) + + func actualViolation(for violation: Violation) -> Violation { + guard let actual = lookup[violation.indentationRanges.expected] else { return violation } + return actualViolation(for: actual) + } + + return actualViolation + } +} + +extension LiteralExpressionEndIdentationRule { + fileprivate struct Violation { + var location: Location + var indentationRanges: (expected: NSRange, actual: NSRange) + var endOffset: Int + var range: NSRange + } + + fileprivate func violations(in file: File) -> [Violation] { + return violations(in: file, dictionary: file.structure.dictionary) + } + + private func violations(in file: File, + dictionary: [String: SourceKitRepresentable]) -> [Violation] { + return dictionary.substructure.flatMap { subDict -> [Violation] in + var subViolations = violations(in: file, dictionary: subDict) + + if let kindString = subDict.kind, + let kind = SwiftExpressionKind(rawValue: kindString), + let violation = violation(in: file, of: kind, dictionary: subDict) { + subViolations.append(violation) + } + + return subViolations + } + } + + private func violation(in file: File, of kind: SwiftExpressionKind, + dictionary: [String: SourceKitRepresentable]) -> Violation? { + guard kind == .dictionary || kind == .array else { + return nil + } + let elements = dictionary.elements.filter { $0.kind == "source.lang.swift.structure.elem.expr" } let contents = file.contents.bridge() @@ -74,7 +219,7 @@ public struct LiteralExpressionEndIdentationRule: ASTRule, ConfigurationProvider case let endOffset = offset + length - 1, let (endLine, endPosition) = contents.lineAndCharacter(forByteOffset: endOffset), lastParamLine != endLine else { - return [] + return nil } let range = file.lines[startLine - 1].range @@ -83,16 +228,18 @@ public struct LiteralExpressionEndIdentationRule: ASTRule, ConfigurationProvider guard let match = regex.firstMatch(in: file.contents, options: [], range: range)?.range, case let expected = match.location - range.location, expected != actual else { - return [] + return nil } - let reason = "\(LiteralExpressionEndIdentationRule.description.description) " + - "Expected \(expected), got \(actual)." - return [ - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severity, - location: Location(file: file, byteOffset: endOffset), - reason: reason) - ] + var expectedRange = range + expectedRange.length = expected + + var actualRange = file.lines[endLine - 1].range + actualRange.length = actual + + return Violation(location: Location(file: file, byteOffset: endOffset), + indentationRanges: (expected: expectedRange, actual: actualRange), + endOffset: endOffset, + range: NSRange(location: offset, length: length)) } } diff --git a/Source/SwiftLintFramework/Rules/LowerACLThanBodyRule.swift b/Source/SwiftLintFramework/Rules/LowerACLThanParentRule.swift similarity index 100% rename from Source/SwiftLintFramework/Rules/LowerACLThanBodyRule.swift rename to Source/SwiftLintFramework/Rules/LowerACLThanParentRule.swift diff --git a/Source/SwiftLintFramework/Rules/MarkRule.swift b/Source/SwiftLintFramework/Rules/MarkRule.swift index 3f46704befb..085550b16d8 100644 --- a/Source/SwiftLintFramework/Rules/MarkRule.swift +++ b/Source/SwiftLintFramework/Rules/MarkRule.swift @@ -44,6 +44,10 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule { "↓// MARK bad", "↓//MARK bad", "↓// MARK - bad", + "↓//MARK : bad", + "↓// MARKL:", + "↓// MARKR ", + "↓// MARKK -", issue1029Example ], corrections: [ @@ -54,6 +58,15 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule { "↓//MARK: - comment": "// MARK: - comment", "↓// MARK:- comment": "// MARK: - comment", "↓// MARK: -comment": "// MARK: - comment", + "↓// MARK: - comment": "// MARK: - comment", + "↓// Mark: comment": "// MARK: comment", + "↓// Mark: - comment": "// MARK: - comment", + "↓// MARK - comment": "// MARK: - comment", + "↓// MARK : comment": "// MARK: comment", + "↓// MARKL:": "// MARK:", + "↓// MARKL: -": "// MARK: -", + "↓// MARKK ": "// MARK: ", + "↓// MARKK -": "// MARK: -", issue1029Example: issue1029Correction ] ) @@ -73,6 +86,10 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule { private let invalidLowercasePattern = "(?:// ?[Mm]ark:)" private let missingColonPattern = "(?:// ?MARK[^:])" + // The below patterns more specifically describe some of the above pattern's failure cases for correction. + private let oneOrMoreSpacesBeforeColonPattern = "(?:// ?MARK +:)" + private let nonWhitespaceBeforeColonPattern = "(?:// ?MARK\\S+:)" + private let nonWhitespaceNorColonBeforeSpacesPattern = "(?:// ?MARK[^\\s:]* +)" private var pattern: String { return [ @@ -117,6 +134,25 @@ public struct MarkRule: CorrectableRule, ConfigurationProviderRule { replaceString: "// MARK: - ", keepLastChar: true)) + result.append(contentsOf: correct(file: file, + pattern: oneOrMoreSpacesBeforeColonPattern, + replaceString: "// MARK:", + keepLastChar: false)) + + result.append(contentsOf: correct(file: file, + pattern: nonWhitespaceBeforeColonPattern, + replaceString: "// MARK:", + keepLastChar: false)) + + result.append(contentsOf: correct(file: file, + pattern: nonWhitespaceNorColonBeforeSpacesPattern, + replaceString: "// MARK: ", + keepLastChar: false)) + + result.append(contentsOf: correct(file: file, + pattern: invalidLowercasePattern, + replaceString: "// MARK:")) + return result.unique } diff --git a/Source/SwiftLintFramework/Rules/OpeningBraceRule.swift b/Source/SwiftLintFramework/Rules/OpeningBraceRule.swift index 8640583c7bf..acd84d62d9d 100644 --- a/Source/SwiftLintFramework/Rules/OpeningBraceRule.swift +++ b/Source/SwiftLintFramework/Rules/OpeningBraceRule.swift @@ -101,11 +101,8 @@ public struct OpeningBraceRule: CorrectableRule, ConfigurationProviderRule { guard let indexRange = contents.nsrangeToIndexRange(violatingRange) else { return contents } -#if swift(>=4.0) + let capturedString = String(contents[indexRange]) -#else - let capturedString = contents[indexRange] -#endif var adjustedRange = violatingRange var correctString = " {" diff --git a/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift b/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift index 411d227db47..6478e0ffa8c 100644 --- a/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift +++ b/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift @@ -19,6 +19,7 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura "let foo = !false\n", "let foo: Int?\n", "let foo: Array\n", + "let model = CustomView, NSAttributedString>()\n", "let foo: [String]\n", "let foo = 1 + \n 2\n", "let range = 1...3\n", @@ -28,7 +29,10 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura "array.removeAtIndex(-200)\n", "let name = \"image-1\"\n", "button.setImage(#imageLiteral(resourceName: \"image-1\"), for: .normal)\n", - "let doubleValue = -9e-11\n" + "let doubleValue = -9e-11\n", + "let foo = GenericType<(UIViewController) -> Void>()\n", + "let foo = Foo, Baz>()\n", + "let foo = SignalProducer, Error>([ self.signal, next ]).flatten(.concat)\n" ], triggeringExamples: [ "let foo = 1↓+2\n", @@ -96,7 +100,7 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura } let pattern = "(?:\(patterns.joined(separator: "|")))" - let genericPattern = "<(?:\(oneSpace)|\\S)+?>" // not using dot to avoid matching new line + let genericPattern = "<(?:\(oneSpace)|\\S)*>" // not using dot to avoid matching new line let validRangePattern = leadingVariableOrNumber + zeroSpaces + rangePattern + zeroSpaces + trailingVariableOrNumber let excludingPattern = "(?:\(genericPattern)|\(validRangePattern))" diff --git a/Source/SwiftLintFramework/Rules/ProhibitedSuperRule.swift b/Source/SwiftLintFramework/Rules/ProhibitedSuperRule.swift index f0c3c60cf7a..c7593786507 100644 --- a/Source/SwiftLintFramework/Rules/ProhibitedSuperRule.swift +++ b/Source/SwiftLintFramework/Rules/ProhibitedSuperRule.swift @@ -11,43 +11,64 @@ public struct ProhibitedSuperRule: ConfigurationProviderRule, ASTRule, OptInRule description: "Some methods should not call super", kind: .lint, nonTriggeringExamples: [ - "class VC: UIViewController {\n" + - "\toverride func loadView() {\n" + - "\t}\n" + - "}\n", - "class NSView {\n" + - "\tfunc updateLayer() {\n" + - "\t\tself.method1()" + - "\t}\n" + - "}\n" + """ + class VC: UIViewController { + override func loadView() { + } + } + """, + """ + class NSView { + func updateLayer() { + self.method1() + } + } + """, + """ + public class FileProviderExtension: NSFileProviderExtension { + override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) { + guard let identifier = persistentIdentifierForItem(at: url) else { + completionHandler(NSFileProviderError(.noSuchItem)) + return + } + } + } + """ ], triggeringExamples: [ - "class VC: UIViewController {\n" + - "\toverride func loadView() {↓\n" + - "\t\tsuper.loadView()\n" + - "\t}\n" + - "}\n", - "class VC: NSFileProviderExtension {\n" + - "\toverride func providePlaceholder(at url: URL," + - "completionHandler: @escaping (Error?) -> Void) {↓\n" + - "\t\tself.method1()\n" + - "\t\tsuper.providePlaceholder(at:url, completionHandler: completionHandler)\n" + - "\t}\n" + - "}\n", - "class VC: NSView {\n" + - "\toverride func updateLayer() {↓\n" + - "\t\tself.method1()\n" + - "\t\tsuper.updateLayer()\n" + - "\t\tself.method2()\n" + - "\t}\n" + - "}\n", - "class VC: NSView {\n" + - "\toverride func updateLayer() {↓\n" + - "\t\tdefer {\n" + - "\t\t\tsuper.updateLayer()\n" + - "\t\t}\n" + - "\t}\n" + - "}\n" + """ + class VC: UIViewController { + override func loadView() {↓ + super.loadView() + } + } + """, + """ + class VC: NSFileProviderExtension { + override func providePlaceholder(at url: URL, completionHandler: @escaping (Error?) -> Void) {↓ + self.method1() + super.providePlaceholder(at:url, completionHandler: completionHandler) + } + } + """, + """ + class VC: NSView { + override func updateLayer() {↓ + self.method1() + super.updateLayer() + self.method2() + } + } + """, + """ + class VC: NSView { + override func updateLayer() {↓ + defer { + super.updateLayer() + } + } + } + """ ] ) diff --git a/Source/SwiftLintFramework/Rules/RuleConfigurations/ClosureEndIndentationRuleExamples.swift b/Source/SwiftLintFramework/Rules/RuleConfigurations/ClosureEndIndentationRuleExamples.swift new file mode 100644 index 00000000000..57191afd48e --- /dev/null +++ b/Source/SwiftLintFramework/Rules/RuleConfigurations/ClosureEndIndentationRuleExamples.swift @@ -0,0 +1,184 @@ +internal struct ClosureEndIndentationRuleExamples { + + static let nonTriggeringExamples = [ + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + " }\n", + "[1, 2].map { $0 + 1 }\n", + "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + + " return Command(string: contents, range: range)\n" + + "}.flatMap { command in\n" + + " return command.expand()\n" + + "}\n", + "foo(foo: bar,\n" + + " options: baz) { _ in }\n", + "someReallyLongProperty.chainingWithAnotherProperty\n" + + " .foo { _ in }", + "foo(abc, 123)\n" + + "{ _ in }\n", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })", + "function(parameter: param,\n" + + " closure: { x in\n" + + " print(x)\n" + + "})", + "function(parameter: param, closure: { x in\n" + + " print(x)\n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })" + ] + + static let triggeringExamples = [ + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + "↓}\n", + "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + + " return Command(string: contents, range: range)\n" + + " ↓}.flatMap { command in\n" + + " return command.expand()\n" + + "↓}\n", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓},\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + "↓})" + ] + + static let corrections = [ + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + "↓}\n": + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + " }\n", + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + "↓}.another { x in\n" + + " print(x)\n" + + "↓}.yetAnother { y in\n" + + " print(y)\n" + + "↓})": + "SignalProducer(values: [1, 2, 3])\n" + + " .startWithNext { number in\n" + + " print(number)\n" + + " }.another { x in\n" + + " print(x)\n" + + " }.yetAnother { y in\n" + + " print(y)\n" + + " })", + "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + + " return Command(string: contents, range: range)\n" + + "↓ }.flatMap { command in\n" + + " return command.expand()\n" + + "↓}\n": + "return match(pattern: pattern, with: [.comment]).flatMap { range in\n" + + " return Command(string: contents, range: range)\n" + + "}.flatMap { command in\n" + + " return command.expand()\n" + + "}\n", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓})": + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + " })", + "function(\n" + + " closure: { x in\n" + + "↓ print(x) })": + "function(\n" + + " closure: { x in\n" + + " print(x) \n" + + " })", + "function(\n" + + " closure: { x in\n" + + "↓ab})": + "function(\n" + + " closure: { x in\n" + + "ab\n" + + " })", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓},\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })": + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓ },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })": + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓ab},\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })": + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "ab\n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })", + "function(\n" + + " closure: { x in\n" + + "↓ print(x) },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })": + "function(\n" + + " closure: { x in\n" + + " print(x) \n" + + " },\n" + + " anotherClosure: { y in\n" + + " print(y)\n" + + " })", + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + "↓}, anotherClosure: { y in\n" + + " print(y)\n" + + "↓})": + "function(\n" + + " closure: { x in\n" + + " print(x)\n" + + " }, anotherClosure: { y in\n" + + " print(y)\n" + + " })" + ] +} diff --git a/Source/SwiftLintFramework/Rules/RuleConfigurations/FileNameConfiguration.swift b/Source/SwiftLintFramework/Rules/RuleConfigurations/FileNameConfiguration.swift new file mode 100644 index 00000000000..f8dc51b72ef --- /dev/null +++ b/Source/SwiftLintFramework/Rules/RuleConfigurations/FileNameConfiguration.swift @@ -0,0 +1,32 @@ +public struct FileNameConfiguration: RuleConfiguration, Equatable { + public var consoleDescription: String { + return "(severity) \(severity.consoleDescription), " + + "excluded: \(excluded.sorted())" + } + + private(set) public var severity: SeverityConfiguration + private(set) public var excluded: Set + + public init(severity: ViolationSeverity, excluded: [String] = []) { + self.severity = SeverityConfiguration(severity) + self.excluded = Set(excluded) + } + + public mutating func apply(configuration: Any) throws { + guard let configurationDict = configuration as? [String: Any] else { + throw ConfigurationError.unknownConfiguration + } + + if let severityConfiguration = configurationDict["severity"] { + try severity.apply(configuration: severityConfiguration) + } + if let excluded = [String].array(of: configurationDict["excluded"]) { + self.excluded = Set(excluded) + } + } +} + +public func == (lhs: FileNameConfiguration, rhs: FileNameConfiguration) -> Bool { + return lhs.severity == rhs.severity && + lhs.excluded == rhs.excluded +} diff --git a/Source/SwiftLintFramework/Rules/RuleConfigurations/FunctionParameterCountConfiguration.swift b/Source/SwiftLintFramework/Rules/RuleConfigurations/FunctionParameterCountConfiguration.swift new file mode 100644 index 00000000000..bbd2d532d31 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/RuleConfigurations/FunctionParameterCountConfiguration.swift @@ -0,0 +1,55 @@ +import Foundation + +private enum ConfigurationKey: String { + case warning = "warning" + case error = "error" + case ignoresDefaultParameters = "ignores_default_parameters" +} + +public struct FunctionParameterCountConfiguration: RuleConfiguration, Equatable { + private(set) var ignoresDefaultParameters: Bool + private(set) var severityConfiguration: SeverityLevelsConfiguration + + public var consoleDescription: String { + return severityConfiguration.consoleDescription + + "\(ConfigurationKey.ignoresDefaultParameters.rawValue): \(ignoresDefaultParameters)" + } + + public init(warning: Int, error: Int?, ignoresDefaultParameters: Bool = true) { + self.ignoresDefaultParameters = ignoresDefaultParameters + self.severityConfiguration = SeverityLevelsConfiguration(warning: warning, error: error) + } + + public mutating func apply(configuration: Any) throws { + if let configurationArray = [Int].array(of: configuration), + !configurationArray.isEmpty { + let warning = configurationArray[0] + let error = (configurationArray.count > 1) ? configurationArray[1] : nil + severityConfiguration = SeverityLevelsConfiguration(warning: warning, error: error) + } else if let configDict = configuration as? [String: Any], !configDict.isEmpty { + for (string, value) in configDict { + guard let key = ConfigurationKey(rawValue: string) else { + throw ConfigurationError.unknownConfiguration + } + switch (key, value) { + case (.error, let intValue as Int): + severityConfiguration.error = intValue + case (.warning, let intValue as Int): + severityConfiguration.warning = intValue + case (.ignoresDefaultParameters, let boolValue as Bool): + ignoresDefaultParameters = boolValue + default: + throw ConfigurationError.unknownConfiguration + } + } + } else { + throw ConfigurationError.unknownConfiguration + } + } + + public static func == (lhs: FunctionParameterCountConfiguration, + rhs: FunctionParameterCountConfiguration) -> Bool { + return lhs.severityConfiguration == rhs.severityConfiguration && + lhs.ignoresDefaultParameters == rhs.ignoresDefaultParameters + } +} diff --git a/Source/SwiftLintFramework/Rules/RuleConfigurations/StatementPositionConfiguration.swift b/Source/SwiftLintFramework/Rules/RuleConfigurations/StatementModeConfiguration.swift similarity index 100% rename from Source/SwiftLintFramework/Rules/RuleConfigurations/StatementPositionConfiguration.swift rename to Source/SwiftLintFramework/Rules/RuleConfigurations/StatementModeConfiguration.swift diff --git a/Source/SwiftLintFramework/Rules/UnneededParenthesesInClosureArgumentRule.swift b/Source/SwiftLintFramework/Rules/UnneededParenthesesInClosureArgumentRule.swift index daa5ff2b827..54458e4e5bd 100644 --- a/Source/SwiftLintFramework/Rules/UnneededParenthesesInClosureArgumentRule.swift +++ b/Source/SwiftLintFramework/Rules/UnneededParenthesesInClosureArgumentRule.swift @@ -22,7 +22,31 @@ public struct UnneededParenthesesInClosureArgumentRule: ConfigurationProviderRul "call(arg: { ↓(bar, _) in })\n", "let foo = { ↓(bar) -> Bool in return true }\n", "foo.map { ($0, $0) }.forEach { ↓(x, y) in }", - "foo.bar { [weak self] ↓(x, y) in }" + "foo.bar { [weak self] ↓(x, y) in }", + """ + [].first { ↓(temp) in + [].first { ↓(temp) in + [].first { ↓(temp) in + _ = temp + return false + } + return false + } + return false + } + """, + """ + [].first { temp in + [].first { ↓(temp) in + [].first { ↓(temp) in + _ = temp + return false + } + return false + } + return false + } + """ ], corrections: [ "call(arg: { ↓(bar) in })\n": "call(arg: { bar in })\n", @@ -44,7 +68,7 @@ public struct UnneededParenthesesInClosureArgumentRule: ConfigurationProviderRul private func violationRanges(file: File) -> [NSRange] { let capturesPattern = "(?:\\[[^\\]]+\\])?" - let pattern = "\\{\\s*\(capturesPattern)\\s*(\\([^:}]+\\))\\s*(in|->)" + let pattern = "\\{\\s*\(capturesPattern)\\s*(\\([^:}]+?\\))\\s*(in|->)" let contents = file.contents.bridge() let range = NSRange(location: 0, length: contents.length) return regex(pattern).matches(in: file.contents, options: [], range: range).compactMap { match -> NSRange? in diff --git a/Source/SwiftLintFramework/Rules/UntypedErrorInCatchRule.swift b/Source/SwiftLintFramework/Rules/UntypedErrorInCatchRule.swift index 62b6f66b347..6521e2644ea 100644 --- a/Source/SwiftLintFramework/Rules/UntypedErrorInCatchRule.swift +++ b/Source/SwiftLintFramework/Rules/UntypedErrorInCatchRule.swift @@ -49,17 +49,44 @@ public struct UntypedErrorInCatchRule: OptInRule, ConfigurationProviderRule { "do {\n try foo() \n} ↓catch let e {}", "do {\n try foo() \n} ↓catch(let error) {}", "do {\n try foo() \n} ↓catch (let error) {}" + ], + corrections: [ + "do {\n try foo() \n} ↓catch let error {}": "do {\n try foo() \n} catch {}", + "do {\n try foo() \n} ↓catch(let error) {}": "do {\n try foo() \n} catch {}", + "do {\n try foo() \n} ↓catch (let error) {}": "do {\n try foo() \n} catch {}" ]) public func validate(file: File) -> [StyleViolation] { - let matches = file.match(pattern: type(of: self).regularExpression, - with: [.keyword, .keyword, .identifier]) - - return matches.map { + return violationRanges(in: file).map { return StyleViolation(ruleDescription: type(of: self).description, severity: configuration.severity, location: Location(file: file, characterOffset: $0.location), reason: configuration.consoleDescription) } } + + fileprivate func violationRanges(in file: File) -> [NSRange] { + return file.match(pattern: type(of: self).regularExpression, + with: [.keyword, .keyword, .identifier]) + } +} + +extension UntypedErrorInCatchRule: CorrectableRule { + public func correct(file: File) -> [Correction] { + let violations = violationRanges(in: file) + let matches = file.ruleEnabled(violatingRanges: violations, for: self) + if matches.isEmpty { return [] } + + var contents = file.contents.bridge() + let description = type(of: self).description + var corrections = [Correction]() + + for range in matches.reversed() where contents.substring(with: range).contains("let error") { + contents = contents.replacingCharacters(in: range, with: "catch {").bridge() + let location = Location(file: file, characterOffset: range.location) + corrections.append(Correction(ruleDescription: description, location: location)) + } + file.write(contents.bridge()) + return corrections + } } diff --git a/Source/SwiftLintFramework/Rules/UnusedClosureParameterRule.swift b/Source/SwiftLintFramework/Rules/UnusedClosureParameterRule.swift index 3836d46078c..84de4fb5acc 100644 --- a/Source/SwiftLintFramework/Rules/UnusedClosureParameterRule.swift +++ b/Source/SwiftLintFramework/Rules/UnusedClosureParameterRule.swift @@ -38,6 +38,11 @@ public struct UnusedClosureParameterRule: ASTRule, ConfigurationProviderRule, Co ({ (manager: FileManager) in print(manager) })(FileManager.default) + """, + """ + withPostSideEffect { input in + if true { print("\\(input)") } + } """ ], triggeringExamples: [ @@ -130,15 +135,18 @@ public struct UnusedClosureParameterRule: ASTRule, ConfigurationProviderRule, Co length: range.length), // if it's the parameter declaration itself, we should skip byteRange.location > paramOffset, - case let tokens = file.syntaxMap.tokens(inByteRange: byteRange), - // a parameter usage should be only one token - tokens.count == 1 else { - continue + case let tokens = file.syntaxMap.tokens(inByteRange: byteRange) else { + continue } + let token = tokens.first(where: { token -> Bool in + return SyntaxKind(rawValue: token.type) == .identifier && + token.offset == byteRange.location && + token.length == byteRange.length + }) + // found a usage, there's no violation! - if let token = tokens.first, SyntaxKind(rawValue: token.type) == .identifier, - token.offset == byteRange.location, token.length == byteRange.length { + guard token == nil else { return nil } } diff --git a/Source/swiftlint/Extensions/shim.swift b/Source/swiftlint/Extensions/shim.swift index 2b6edaa89b4..d7817537fd2 100644 --- a/Source/swiftlint/Extensions/shim.swift +++ b/Source/swiftlint/Extensions/shim.swift @@ -1,11 +1,11 @@ -#if (!swift(>=4.1) && swift(>=4.0)) || !swift(>=3.3) +#if !swift(>=4.1) - extension Sequence { - func compactMap( - _ transform: (Self.Element - ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { - return try flatMap(transform) - } +extension Sequence { + func compactMap( + _ transform: (Self.Element + ) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { + return try flatMap(transform) } +} #endif diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index c629e2c30aa..d79cbf81e82 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -41,10 +41,11 @@ 2E5761AA1C573B83003271AF /* FunctionParameterCountRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */; }; 31F1B6CC1F60BF4500A57456 /* SwitchCaseAlignmentRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31F1B6CB1F60BF4500A57456 /* SwitchCaseAlignmentRule.swift */; }; 37B3FA8B1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B3FA8A1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift */; }; + 3A915E5B20A1543700519F3A /* ClosureEndIndentationRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A915E5920A1543000519F3A /* ClosureEndIndentationRuleExamples.swift */; }; 3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */; }; 3B0B14541C505D6300BE82F7 /* SeverityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */; }; 3B12C9C11C3209CB000B423F /* test.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3B12C9BF1C3209AC000B423F /* test.yml */; }; - 3B12C9C31C320A53000B423F /* Yaml+SwiftLintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12C9C21C320A53000B423F /* Yaml+SwiftLintTests.swift */; }; + 3B12C9C31C320A53000B423F /* YamlSwiftLintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12C9C21C320A53000B423F /* YamlSwiftLintTests.swift */; }; 3B12C9C51C322032000B423F /* MasterRuleList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12C9C41C322032000B423F /* MasterRuleList.swift */; }; 3B12C9C71C3361CB000B423F /* RuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B12C9C61C3361CB000B423F /* RuleTests.swift */; }; 3B1DF0121C5148140011BCED /* CustomRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B1DF0111C5148140011BCED /* CustomRules.swift */; }; @@ -87,16 +88,18 @@ 62622F6B1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */; }; 62640152201552FD005B9C4A /* DiscouragedOptionalBooleanRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62640150201552E0005B9C4A /* DiscouragedOptionalBooleanRule.swift */; }; 6264015520155556005B9C4A /* DiscouragedOptionalBooleanRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6264015320155533005B9C4A /* DiscouragedOptionalBooleanRuleExamples.swift */; }; + 626B01B620A173F100D2C42F /* EmptyXCTestMethodRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626B01B420A1735900D2C42F /* EmptyXCTestMethodRuleExamples.swift */; }; 626C16E21F948EBC00BB7475 /* QuickDiscouragedFocusedTestRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626C16E01F948E1C00BB7475 /* QuickDiscouragedFocusedTestRuleExamples.swift */; }; 626D02971F31CBCC0054788D /* XCTFailMessageRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */; }; 627BC48D1F9405160004A6C2 /* QuickDiscouragedFocusedTestRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E54FED1F93AD57005B367B /* QuickDiscouragedFocusedTestRule.swift */; }; 629ADD062006302D0009E362 /* DiscouragedOptionalCollectionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629ADD052006302D0009E362 /* DiscouragedOptionalCollectionRule.swift */; }; 629C60D91F43906700B4AF92 /* SingleTestClassRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629C60D81F43906700B4AF92 /* SingleTestClassRule.swift */; }; + 62A3E95D209E084000547A86 /* EmptyXCTestMethodRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A3E95B209E078000547A86 /* EmptyXCTestMethodRule.swift */; }; 62A498561F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */; }; - 62A6E7931F3317E3003A0479 /* JoinedDefaultRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */; }; + 62A6E7931F3317E3003A0479 /* JoinedDefaultParameterRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A6E7911F3317E3003A0479 /* JoinedDefaultParameterRule.swift */; }; 62DADC481FFF0423002B6319 /* PrefixedTopLevelConstantRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DADC471FFF0423002B6319 /* PrefixedTopLevelConstantRule.swift */; }; 62DEA1661FB21A9E00BCCCC6 /* PrivateActionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */; }; - 62FE5D32200CABDD00F68793 /* DiscouragedOptionalCollectionRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionRuleExamples.swift */; }; + 62FE5D32200CABDD00F68793 /* DiscouragedOptionalCollectionExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionExamples.swift */; }; 67932E2D1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */; }; 67EB4DFA1E4CC111004E9ACD /* CyclomaticComplexityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */; }; 67EB4DFC1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DFB1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift */; }; @@ -114,7 +117,7 @@ 6CCFCF2D1CFEF731003239EB /* SourceKittenFramework.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E876BFBD1B07828500114ED5 /* SourceKittenFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 6CCFCF2E1CFEF73A003239EB /* SWXMLHash.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = E8C0DFCC1AD349DB007EE3D4 /* SWXMLHash.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 6CCFCF2F1CFEF73E003239EB /* SwiftyTextTable.framework in Embed Frameworks into SwiftLintFramework.framework */ = {isa = PBXBuildFile; fileRef = 3BBF2F9C1C640A0F006CD775 /* SwiftyTextTable.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7250948A1D0859260039B353 /* StatementPositionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725094881D0855760039B353 /* StatementPositionConfiguration.swift */; }; + 7250948A1D0859260039B353 /* StatementModeConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725094881D0855760039B353 /* StatementModeConfiguration.swift */; }; 72EA17B61FD31F10009D5CE6 /* ExplicitACLRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72EA17B51FD31F10009D5CE6 /* ExplicitACLRule.swift */; }; 740DF1B1203F62BB0081F694 /* EmptyStringRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 740DF1AF203F5AFC0081F694 /* EmptyStringRule.swift */; }; 787CDE39208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */; }; @@ -128,6 +131,10 @@ 83894F221B0C928A006214E1 /* RulesCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894F211B0C928A006214E1 /* RulesCommand.swift */; }; 83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D71E261B131EB5000395DE /* RuleDescription.swift */; }; 85DA81321D6B471000951BC4 /* MarkRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 856651A61D6B395F005E6B29 /* MarkRule.swift */; }; + 8B01E4FD20A41C8700C9233E /* FunctionParameterCountConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B01E4FB20A4183C00C9233E /* FunctionParameterCountConfiguration.swift */; }; + 8B01E50220A4349100C9233E /* FunctionParameterCountRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B01E4FF20A4340A00C9233E /* FunctionParameterCountRuleTests.swift */; }; + 8F2CC1CB20A6A070006ED34F /* FileNameConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2CC1CA20A6A070006ED34F /* FileNameConfiguration.swift */; }; + 8F2CC1CD20A6A189006ED34F /* FileNameRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2CC1CC20A6A189006ED34F /* FileNameRuleTests.swift */; }; 8F8050821FFE0CBB006F5B93 /* Configuration+IndentationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F8050811FFE0CBB006F5B93 /* Configuration+IndentationStyle.swift */; }; 8FC9F5111F4B8E48006826C1 /* IsDisjointRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC9F5101F4B8E48006826C1 /* IsDisjointRule.swift */; }; 8FD216CC205584AF008ED13F /* CharacterSet+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD216CB205584AF008ED13F /* CharacterSet+SwiftLint.swift */; }; @@ -155,7 +162,7 @@ B89F3BCF1FD5EE1400931E59 /* RequiredEnumCaseRuleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B89F3BC71FD5ED7D00931E59 /* RequiredEnumCaseRuleConfiguration.swift */; }; BB00B4E91F5216090079869F /* MultipleClosuresWithTrailingClosureRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB00B4E71F5216070079869F /* MultipleClosuresWithTrailingClosureRule.swift */; }; BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */; }; - C26330382073DAC500D7B4FD /* LowerACLThanBodyRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26330352073DAA200D7B4FD /* LowerACLThanBodyRule.swift */; }; + C26330382073DAC500D7B4FD /* LowerACLThanParentRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26330352073DAA200D7B4FD /* LowerACLThanParentRule.swift */; }; C328A2F71E6759AE00A9E4D7 /* ExplicitTypeInterfaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328A2F51E67595500A9E4D7 /* ExplicitTypeInterfaceRule.swift */; }; C3DE5DAC1E7DF9CA00761483 /* FatalErrorMessageRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */; }; C946FECB1EAE67EE007DD778 /* LetVarWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C946FEC91EAE5E20007DD778 /* LetVarWhitespaceRule.swift */; }; @@ -211,7 +218,7 @@ D47EF4821F69E34D0012C4CA /* ColonRule+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47EF4811F69E34D0012C4CA /* ColonRule+Dictionary.swift */; }; D47EF4841F69E3D60012C4CA /* ColonRule+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47EF4831F69E3D60012C4CA /* ColonRule+Type.swift */; }; D47F31151EC918B600E3E1CA /* ProtocolPropertyAccessorsOrderRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F31141EC918B600E3E1CA /* ProtocolPropertyAccessorsOrderRule.swift */; }; - D48AE2CC1DFB58C5001C6A4A /* AttributesRulesExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48AE2CB1DFB58C5001C6A4A /* AttributesRulesExamples.swift */; }; + D48AE2CC1DFB58C5001C6A4A /* AttributesRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48AE2CB1DFB58C5001C6A4A /* AttributesRuleExamples.swift */; }; D48B51211F4F5DEF0068AB98 /* RuleList+Documentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48B51201F4F5DEF0068AB98 /* RuleList+Documentation.swift */; }; D48B51231F4F5E4B0068AB98 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48B51221F4F5E4B0068AB98 /* DocumentationTests.swift */; }; D49896F12026B36C00814A83 /* RedundantSetAccessControlRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49896F02026B36C00814A83 /* RedundantSetAccessControlRule.swift */; }; @@ -234,6 +241,7 @@ D4C4A3521DEFBBB700E0E04C /* FileHeaderConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */; }; D4C889711E385B7B00BAE88D /* RedundantDiscardableLetRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */; }; D4CA758F1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CA758E1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift */; }; + D4CFC5D2209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4CFC5D1209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift */; }; D4D1B9BB1EAC2C910028BE6A /* AccessControlLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D1B9B91EAC2C870028BE6A /* AccessControlLevel.swift */; }; D4D5A5FF1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4D5A5FE1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift */; }; D4DA1DF41E17511D0037413D /* CompilerProtocolInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA1DF31E17511D0037413D /* CompilerProtocolInitRule.swift */; }; @@ -272,8 +280,9 @@ E81ADD741ED6052F000CD451 /* CommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81ADD731ED6052F000CD451 /* CommandTests.swift */; }; E81FB3E41C6D507B00DC988F /* CommonOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81FB3E31C6D507B00DC988F /* CommonOptions.swift */; }; E82367E01ED3BD1E0040A88E /* Configuration+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82367DF1ED3BD1E0040A88E /* Configuration+Cache.swift */; }; - E832F10B1B17E2F5003F265F /* NSFileManager+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */; }; + E832F10B1B17E2F5003F265F /* FileManager+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10A1B17E2F5003F265F /* FileManager+SwiftLint.swift */; }; E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10C1B17E725003F265F /* IntegrationTests.swift */; }; + E83530C61ED6328A00FBAF79 /* FileNameRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83530C51ED6328A00FBAF79 /* FileNameRule.swift */; }; E83A0B351A5D382B0041A60A /* VersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83A0B341A5D382B0041A60A /* VersionCommand.swift */; }; E847F0A91BFBBABD00EA9363 /* EmptyCountRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */; }; E84E07471C13F95300F11122 /* AutoCorrectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E07461C13F95300F11122 /* AutoCorrectCommand.swift */; }; @@ -412,10 +421,11 @@ 2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionParameterCountRule.swift; sourceTree = ""; }; 31F1B6CB1F60BF4500A57456 /* SwitchCaseAlignmentRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentRule.swift; sourceTree = ""; }; 37B3FA8A1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+SwiftLint.swift"; sourceTree = ""; }; + 3A915E5920A1543000519F3A /* ClosureEndIndentationRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ClosureEndIndentationRuleExamples.swift; path = RuleConfigurations/ClosureEndIndentationRuleExamples.swift; sourceTree = ""; }; 3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineLengthConfiguration.swift; sourceTree = ""; }; 3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeverityConfiguration.swift; sourceTree = ""; }; 3B12C9BF1C3209AC000B423F /* test.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = test.yml; sourceTree = ""; }; - 3B12C9C21C320A53000B423F /* Yaml+SwiftLintTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Yaml+SwiftLintTests.swift"; sourceTree = ""; }; + 3B12C9C21C320A53000B423F /* YamlSwiftLintTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YamlSwiftLintTests.swift; sourceTree = ""; }; 3B12C9C41C322032000B423F /* MasterRuleList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterRuleList.swift; sourceTree = ""; }; 3B12C9C61C3361CB000B423F /* RuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleTests.swift; sourceTree = ""; }; 3B1DF0111C5148140011BCED /* CustomRules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomRules.swift; sourceTree = ""; }; @@ -459,17 +469,19 @@ 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitRule.swift; sourceTree = ""; }; 62640150201552E0005B9C4A /* DiscouragedOptionalBooleanRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedOptionalBooleanRule.swift; sourceTree = ""; }; 6264015320155533005B9C4A /* DiscouragedOptionalBooleanRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedOptionalBooleanRuleExamples.swift; sourceTree = ""; }; + 626B01B420A1735900D2C42F /* EmptyXCTestMethodRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyXCTestMethodRuleExamples.swift; sourceTree = ""; }; 626C16E01F948E1C00BB7475 /* QuickDiscouragedFocusedTestRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickDiscouragedFocusedTestRuleExamples.swift; sourceTree = ""; }; 626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTFailMessageRule.swift; sourceTree = ""; }; 629ADD052006302D0009E362 /* DiscouragedOptionalCollectionRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedOptionalCollectionRule.swift; sourceTree = ""; }; 629C60D81F43906700B4AF92 /* SingleTestClassRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTestClassRule.swift; sourceTree = ""; }; + 62A3E95B209E078000547A86 /* EmptyXCTestMethodRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyXCTestMethodRule.swift; sourceTree = ""; }; 62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitConfiguration.swift; sourceTree = ""; }; - 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedDefaultRule.swift; sourceTree = ""; }; + 62A6E7911F3317E3003A0479 /* JoinedDefaultParameterRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedDefaultParameterRule.swift; sourceTree = ""; }; 62AF35D71F30B183009B11EE /* DiscouragedDirectInitRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitRuleTests.swift; sourceTree = ""; }; 62DADC471FFF0423002B6319 /* PrefixedTopLevelConstantRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefixedTopLevelConstantRule.swift; sourceTree = ""; }; 62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateActionRule.swift; sourceTree = ""; }; 62E54FED1F93AD57005B367B /* QuickDiscouragedFocusedTestRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickDiscouragedFocusedTestRule.swift; sourceTree = ""; }; - 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionRuleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedOptionalCollectionRuleExamples.swift; sourceTree = ""; }; + 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedOptionalCollectionExamples.swift; sourceTree = ""; }; 65454F451B14D73800319A6C /* ControlStatementRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatementRule.swift; sourceTree = ""; }; 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CyclomaticComplexityConfigurationTests.swift; sourceTree = ""; }; 67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CyclomaticComplexityConfiguration.swift; sourceTree = ""; }; @@ -484,7 +496,7 @@ 6CB514E81C760C6900FA02C4 /* Structure+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Structure+SwiftLint.swift"; sourceTree = ""; }; 6CC4259A1C77046200AEA885 /* SyntaxMap+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyntaxMap+SwiftLint.swift"; sourceTree = ""; }; 6CC898A61EA0E1EF003DC0E2 /* CannedEmojiReporterOutputNonObjC.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CannedEmojiReporterOutputNonObjC.txt; sourceTree = ""; }; - 725094881D0855760039B353 /* StatementPositionConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementPositionConfiguration.swift; sourceTree = ""; }; + 725094881D0855760039B353 /* StatementModeConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementModeConfiguration.swift; sourceTree = ""; }; 72EA17B51FD31F10009D5CE6 /* ExplicitACLRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExplicitACLRule.swift; sourceTree = ""; }; 740DF1AF203F5AFC0081F694 /* EmptyStringRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyStringRule.swift; sourceTree = ""; }; 787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchCaseAlignmentConfiguration.swift; sourceTree = ""; }; @@ -498,6 +510,10 @@ 83894F211B0C928A006214E1 /* RulesCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesCommand.swift; sourceTree = ""; }; 83D71E261B131EB5000395DE /* RuleDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleDescription.swift; sourceTree = ""; }; 856651A61D6B395F005E6B29 /* MarkRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkRule.swift; sourceTree = ""; }; + 8B01E4FB20A4183C00C9233E /* FunctionParameterCountConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionParameterCountConfiguration.swift; sourceTree = ""; }; + 8B01E4FF20A4340A00C9233E /* FunctionParameterCountRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionParameterCountRuleTests.swift; sourceTree = ""; }; + 8F2CC1CA20A6A070006ED34F /* FileNameConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileNameConfiguration.swift; sourceTree = ""; }; + 8F2CC1CC20A6A189006ED34F /* FileNameRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileNameRuleTests.swift; sourceTree = ""; }; 8F8050811FFE0CBB006F5B93 /* Configuration+IndentationStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Configuration+IndentationStyle.swift"; sourceTree = ""; }; 8FC9F5101F4B8E48006826C1 /* IsDisjointRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IsDisjointRule.swift; sourceTree = ""; }; 8FD216CB205584AF008ED13F /* CharacterSet+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CharacterSet+SwiftLint.swift"; sourceTree = ""; }; @@ -525,7 +541,7 @@ B89F3BCB1FD5EDA900931E59 /* RequiredEnumCaseRuleTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredEnumCaseRuleTestCase.swift; sourceTree = ""; }; BB00B4E71F5216070079869F /* MultipleClosuresWithTrailingClosureRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipleClosuresWithTrailingClosureRule.swift; sourceTree = ""; }; BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingWhitespaceConfiguration.swift; sourceTree = ""; }; - C26330352073DAA200D7B4FD /* LowerACLThanBodyRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerACLThanBodyRule.swift; sourceTree = ""; }; + C26330352073DAA200D7B4FD /* LowerACLThanParentRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowerACLThanParentRule.swift; sourceTree = ""; }; C328A2F51E67595500A9E4D7 /* ExplicitTypeInterfaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExplicitTypeInterfaceRule.swift; sourceTree = ""; }; C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FatalErrorMessageRule.swift; sourceTree = ""; }; C946FEC91EAE5E20007DD778 /* LetVarWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LetVarWhitespaceRule.swift; sourceTree = ""; }; @@ -601,7 +617,7 @@ D47EF4811F69E34D0012C4CA /* ColonRule+Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ColonRule+Dictionary.swift"; sourceTree = ""; }; D47EF4831F69E3D60012C4CA /* ColonRule+Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ColonRule+Type.swift"; sourceTree = ""; }; D47F31141EC918B600E3E1CA /* ProtocolPropertyAccessorsOrderRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtocolPropertyAccessorsOrderRule.swift; sourceTree = ""; }; - D48AE2CB1DFB58C5001C6A4A /* AttributesRulesExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributesRulesExamples.swift; sourceTree = ""; }; + D48AE2CB1DFB58C5001C6A4A /* AttributesRuleExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributesRuleExamples.swift; sourceTree = ""; }; D48B51201F4F5DEF0068AB98 /* RuleList+Documentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RuleList+Documentation.swift"; sourceTree = ""; }; D48B51221F4F5E4B0068AB98 /* DocumentationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentationTests.swift; sourceTree = ""; }; D49896F02026B36C00814A83 /* RedundantSetAccessControlRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedundantSetAccessControlRule.swift; sourceTree = ""; }; @@ -624,6 +640,7 @@ D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHeaderConfiguration.swift; sourceTree = ""; }; D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedundantDiscardableLetRule.swift; sourceTree = ""; }; D4CA758E1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRuleTests.swift; sourceTree = ""; }; + D4CFC5D1209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionDefaultParameterAtEndRule.swift; sourceTree = ""; }; D4D1B9B91EAC2C870028BE6A /* AccessControlLevel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessControlLevel.swift; sourceTree = ""; }; D4D5A5FE1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShorthandOperatorRule.swift; sourceTree = ""; }; D4DA1DF31E17511D0037413D /* CompilerProtocolInitRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompilerProtocolInitRule.swift; sourceTree = ""; }; @@ -663,8 +680,9 @@ E81ADD731ED6052F000CD451 /* CommandTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandTests.swift; sourceTree = ""; }; E81FB3E31C6D507B00DC988F /* CommonOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonOptions.swift; sourceTree = ""; }; E82367DF1ED3BD1E0040A88E /* Configuration+Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Configuration+Cache.swift"; sourceTree = ""; }; - E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFileManager+SwiftLint.swift"; sourceTree = ""; }; + E832F10A1B17E2F5003F265F /* FileManager+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FileManager+SwiftLint.swift"; sourceTree = ""; }; E832F10C1B17E725003F265F /* IntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = ""; }; + E83530C51ED6328A00FBAF79 /* FileNameRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileNameRule.swift; sourceTree = ""; }; E83A0B341A5D382B0041A60A /* VersionCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionCommand.swift; sourceTree = ""; }; E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyCountRule.swift; sourceTree = ""; }; E84E07461C13F95300F11122 /* AutoCorrectCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoCorrectCommand.swift; sourceTree = ""; }; @@ -782,6 +800,7 @@ 125AAC77203AA82D0004BCE0 /* ExplicitTypeInterfaceConfiguration.swift */, D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */, 29FFC3781F1574FD007E4825 /* FileLengthRuleConfiguration.swift */, + 8B01E4FB20A4183C00C9233E /* FunctionParameterCountConfiguration.swift */, 47ACC8971E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift */, 3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */, 3BCC04D01C4F56D3006073C3 /* NameConfiguration.swift */, @@ -798,12 +817,13 @@ B89F3BC71FD5ED7D00931E59 /* RequiredEnumCaseRuleConfiguration.swift */, 3B0B14531C505D6300BE82F7 /* SeverityConfiguration.swift */, 3BCC04CF1C4F56D3006073C3 /* SeverityLevelsConfiguration.swift */, - 725094881D0855760039B353 /* StatementPositionConfiguration.swift */, + 725094881D0855760039B353 /* StatementModeConfiguration.swift */, D40F83871DE9179200524C62 /* TrailingCommaConfiguration.swift */, BF48D2D61CBCCA5F0080BDAE /* TrailingWhitespaceConfiguration.swift */, CE8178EB1EAC02CD0063186E /* UnusedOptionalBindingConfiguration.swift */, 006204DA1E1E48F900FFFBE1 /* VerticalWhitespaceConfiguration.swift */, 787CDE38208E7D41005F3D2F /* SwitchCaseAlignmentConfiguration.swift */, + 8F2CC1CA20A6A070006ED34F /* FileNameConfiguration.swift */, ); path = RuleConfigurations; sourceTree = ""; @@ -981,7 +1001,9 @@ 02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */, D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */, 29FFC37B1F157BA8007E4825 /* FileLengthRuleTests.swift */, + 8F2CC1CC20A6A189006ED34F /* FileNameRuleTests.swift */, D4348EE91C46122C007707FB /* FunctionBodyLengthRuleTests.swift */, + 8B01E4FF20A4340A00C9233E /* FunctionParameterCountRuleTests.swift */, 3B20CD091EB699380069EF2E /* GenericTypeNameRuleTests.swift */, 3B3A9A321EA3DFD90075B121 /* IdentifierNameRuleTests.swift */, 47ACC8991E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift */, @@ -1011,7 +1033,7 @@ D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */, 006204DD1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift */, F480DC801F2609AB00099465 /* XCTestCase+BundlePath.swift */, - 3B12C9C21C320A53000B423F /* Yaml+SwiftLintTests.swift */, + 3B12C9C21C320A53000B423F /* YamlSwiftLintTests.swift */, 3B30C4A01C3785B300E04027 /* YamlParserTests.swift */, D0D1212219E878CC005E4BAA /* Configuration */, 3B12C9BE1C3209AC000B423F /* Resources */, @@ -1069,11 +1091,12 @@ children = ( D4E2BA841F6CD77B00E8E184 /* ArrayInitRule.swift */, D47A510F1DB2DD4800A4CC21 /* AttributesRule.swift */, - D48AE2CB1DFB58C5001C6A4A /* AttributesRulesExamples.swift */, + D48AE2CB1DFB58C5001C6A4A /* AttributesRuleExamples.swift */, D4FD4C841F2A260A00DD8AA8 /* BlockBasedKVORule.swift */, D4B0228D1E0CC608007E5297 /* ClassDelegateProtocolRule.swift */, 1F11B3CE1C252F23002E8FA8 /* ClosingBraceRule.swift */, D43B046A1E075905004016AF /* ClosureEndIndentationRule.swift */, + 3A915E5920A1543000519F3A /* ClosureEndIndentationRuleExamples.swift */, D47079A81DFDBED000027086 /* ClosureParameterPositionRule.swift */, 1E82D5581D7775C7009553D7 /* ClosureSpacingRule.swift */, E88DEA831B0990F500A66CB0 /* ColonRule.swift */, @@ -1091,16 +1114,18 @@ 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */, 6258783A1FFC458100AC34F2 /* DiscouragedObjectLiteralRule.swift */, 62640150201552E0005B9C4A /* DiscouragedOptionalBooleanRule.swift */, - C26330352073DAA200D7B4FD /* LowerACLThanBodyRule.swift */, + D4CFC5D1209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift */, 6264015320155533005B9C4A /* DiscouragedOptionalBooleanRuleExamples.swift */, 629ADD052006302D0009E362 /* DiscouragedOptionalCollectionRule.swift */, - 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionRuleExamples.swift */, + 62FE5D30200CAB6E00F68793 /* DiscouragedOptionalCollectionExamples.swift */, E315B83B1DFA4BC500621B44 /* DynamicInlineRule.swift */, E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */, - 740DF1AF203F5AFC0081F694 /* EmptyStringRule.swift */, D4470D581EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift */, D47079AC1DFE2FA700027086 /* EmptyParametersRule.swift */, D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */, + 740DF1AF203F5AFC0081F694 /* EmptyStringRule.swift */, + 62A3E95B209E078000547A86 /* EmptyXCTestMethodRule.swift */, + 626B01B420A1735900D2C42F /* EmptyXCTestMethodRuleExamples.swift */, 72EA17B51FD31F10009D5CE6 /* ExplicitACLRule.swift */, 827169B21F488181003FB9AF /* ExplicitEnumRawValueRule.swift */, 7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */, @@ -1111,6 +1136,7 @@ C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */, D4C4A34D1DEA877200E0E04C /* FileHeaderRule.swift */, E88DEA891B0992B300A66CB0 /* FileLengthRule.swift */, + E83530C51ED6328A00FBAF79 /* FileNameRule.swift */, D42D2B371E09CC0D00CD7A2E /* FirstWhereRule.swift */, E88DEA7F1B09903300A66CB0 /* ForceCastRule.swift */, E816194D1BFBFEAB00946723 /* ForceTryRule.swift */, @@ -1125,7 +1151,7 @@ 47FF3BDF1E7C745100187E6D /* ImplicitlyUnwrappedOptionalRule.swift */, D4470D561EB69225008A1B2E /* ImplicitReturnRule.swift */, 8FC9F5101F4B8E48006826C1 /* IsDisjointRule.swift */, - 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */, + 62A6E7911F3317E3003A0479 /* JoinedDefaultParameterRule.swift */, D4DA1DF91E18D6200037413D /* LargeTupleRule.swift */, E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */, 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */, @@ -1136,6 +1162,7 @@ C946FEC91EAE5E20007DD778 /* LetVarWhitespaceRule.swift */, E88DEA7B1B098D7D00A66CB0 /* LineLengthRule.swift */, D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */, + C26330352073DAA200D7B4FD /* LowerACLThanParentRule.swift */, 856651A61D6B395F005E6B29 /* MarkRule.swift */, 188B3FF1207D61040073C2D6 /* ModifierOrderRule.swift */, B25DCD0D1F7EF2280028A199 /* MultilineArgumentsConfiguration.swift */, @@ -1274,7 +1301,7 @@ 37B3FA8A1DFD45A700AD30D2 /* Dictionary+SwiftLint.swift */, 24E17F701B1481FF008195BE /* File+Cache.swift */, E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */, - E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */, + E832F10A1B17E2F5003F265F /* FileManager+SwiftLint.swift */, 3BA79C9A1C4767910057E705 /* NSRange+SwiftLint.swift */, 3BB47D841C51D80000AE6A10 /* NSRegularExpression+SwiftLint.swift */, E81619521BFC162C00946723 /* QueuedPrint.swift */, @@ -1540,6 +1567,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8B01E4FD20A41C8700C9233E /* FunctionParameterCountConfiguration.swift in Sources */, 740DF1B1203F62BB0081F694 /* EmptyStringRule.swift in Sources */, 4DB7815E1CAD72BA00BC4723 /* LegacyCGGeometryFunctionsRule.swift in Sources */, 6CC4259B1C77046200AEA885 /* SyntaxMap+SwiftLint.swift in Sources */, @@ -1550,7 +1578,7 @@ D47079A71DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift in Sources */, E881985E1BEA982100333A11 /* TypeBodyLengthRule.swift in Sources */, 69F88BF71BDA38A6005E7CAE /* OpeningBraceRule.swift in Sources */, - C26330382073DAC500D7B4FD /* LowerACLThanBodyRule.swift in Sources */, + C26330382073DAC500D7B4FD /* LowerACLThanParentRule.swift in Sources */, 78F032461D7C877E00BE709A /* OverriddenSuperCallRule.swift in Sources */, E80E018D1B92C0F60078EB70 /* Command.swift in Sources */, E88198571BEA953300333A11 /* ForceCastRule.swift in Sources */, @@ -1570,6 +1598,7 @@ 3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */, B25DCD0C1F7E9FA20028A199 /* MultilineArgumentsRule.swift in Sources */, 6258783B1FFC458100AC34F2 /* DiscouragedObjectLiteralRule.swift in Sources */, + 62A3E95D209E084000547A86 /* EmptyXCTestMethodRule.swift in Sources */, D4C4A34C1DEA4FF000E0E04C /* AttributesConfiguration.swift in Sources */, 83D71E281B131ECE000395DE /* RuleDescription.swift in Sources */, D4470D571EB69225008A1B2E /* ImplicitReturnRule.swift in Sources */, @@ -1590,8 +1619,11 @@ E88198551BEA949A00333A11 /* ControlStatementRule.swift in Sources */, E57B23C11B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift in Sources */, D4246D6D1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift in Sources */, + 3A915E5B20A1543700519F3A /* ClosureEndIndentationRuleExamples.swift in Sources */, 629ADD062006302D0009E362 /* DiscouragedOptionalCollectionRule.swift in Sources */, E816194E1BFBFEAB00946723 /* ForceTryRule.swift in Sources */, + 8F2CC1CB20A6A070006ED34F /* FileNameConfiguration.swift in Sources */, + D4CFC5D2209EC95A00668488 /* FunctionDefaultParameterAtEndRule.swift in Sources */, E88198541BEA945100333A11 /* CommaRule.swift in Sources */, D4DA1DFE1E1A10DB0037413D /* NumberSeparatorConfiguration.swift in Sources */, E88198601BEA98F000333A11 /* IdentifierNameRule.swift in Sources */, @@ -1681,7 +1713,7 @@ D4DA1DF41E17511D0037413D /* CompilerProtocolInitRule.swift in Sources */, 629C60D91F43906700B4AF92 /* SingleTestClassRule.swift in Sources */, 621061BF1ED57E640082D51E /* MultilineParametersRuleExamples.swift in Sources */, - D48AE2CC1DFB58C5001C6A4A /* AttributesRulesExamples.swift in Sources */, + D48AE2CC1DFB58C5001C6A4A /* AttributesRuleExamples.swift in Sources */, E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */, D43B046B1E075905004016AF /* ClosureEndIndentationRule.swift in Sources */, D47EF4821F69E34D0012C4CA /* ColonRule+Dictionary.swift in Sources */, @@ -1690,7 +1722,7 @@ C328A2F71E6759AE00A9E4D7 /* ExplicitTypeInterfaceRule.swift in Sources */, 93E0C3CE1D67BD7F007FA25D /* ConditionalReturnsOnNewlineRule.swift in Sources */, D43DB1081DC573DA00281215 /* ImplicitGetterRule.swift in Sources */, - 62A6E7931F3317E3003A0479 /* JoinedDefaultRule.swift in Sources */, + 62A6E7931F3317E3003A0479 /* JoinedDefaultParameterRule.swift in Sources */, D4FD4C851F2A260A00DD8AA8 /* BlockBasedKVORule.swift in Sources */, 7C0C2E7A1D2866CB0076435A /* ExplicitInitRule.swift in Sources */, E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */, @@ -1699,7 +1731,7 @@ B25DCD0E1F7EF2280028A199 /* MultilineArgumentsConfiguration.swift in Sources */, 24B4DF0D1D6DFDE90097803B /* RedundantNilCoalescingRule.swift in Sources */, D4130D971E16183F00242361 /* IdentifierNameRuleExamples.swift in Sources */, - 7250948A1D0859260039B353 /* StatementPositionConfiguration.swift in Sources */, + 7250948A1D0859260039B353 /* StatementModeConfiguration.swift in Sources */, E81619531BFC162C00946723 /* QueuedPrint.swift in Sources */, E87E4A051BFB927C00FCFE46 /* TrailingSemicolonRule.swift in Sources */, D4B472411F66486300BD6EF1 /* FallthroughRule.swift in Sources */, @@ -1735,14 +1767,16 @@ E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */, 62622F6B1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift in Sources */, B89F3BCD1FD5EDFB00931E59 /* RequiredEnumCaseRule.swift in Sources */, + E83530C61ED6328A00FBAF79 /* FileNameRule.swift in Sources */, 3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */, D401D9261ED85EF0005DA5D4 /* RuleKind.swift in Sources */, + 626B01B620A173F100D2C42F /* EmptyXCTestMethodRuleExamples.swift in Sources */, D4C889711E385B7B00BAE88D /* RedundantDiscardableLetRule.swift in Sources */, D4D1B9BB1EAC2C910028BE6A /* AccessControlLevel.swift in Sources */, 4A9A3A3A1DC1D75F00DF5183 /* HTMLReporter.swift in Sources */, D40F83881DE9179200524C62 /* TrailingCommaConfiguration.swift in Sources */, 827169B31F488181003FB9AF /* ExplicitEnumRawValueRule.swift in Sources */, - 62FE5D32200CABDD00F68793 /* DiscouragedOptionalCollectionRuleExamples.swift in Sources */, + 62FE5D32200CABDD00F68793 /* DiscouragedOptionalCollectionExamples.swift in Sources */, D49896F12026B36C00814A83 /* RedundantSetAccessControlRule.swift in Sources */, 29FFC37A1F15764D007E4825 /* FileLengthRuleConfiguration.swift in Sources */, ED641C3820AA07B400212C62 /* NoFallthroughOnlyRule.swift in Sources */, @@ -1753,7 +1787,7 @@ 29AD4C661F6EA1D5009B66E1 /* ContainsOverFirstNotNilRule.swift in Sources */, C946FECB1EAE67EE007DD778 /* LetVarWhitespaceRule.swift in Sources */, E881985D1BEA97EB00333A11 /* TrailingWhitespaceRule.swift in Sources */, - E832F10B1B17E2F5003F265F /* NSFileManager+SwiftLint.swift in Sources */, + E832F10B1B17E2F5003F265F /* FileManager+SwiftLint.swift in Sources */, E816194C1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift in Sources */, D4DABFD71E2C23B1009617B6 /* NotificationCenterDetachmentRule.swift in Sources */, 3BA79C9B1C4767910057E705 /* NSRange+SwiftLint.swift in Sources */, @@ -1784,12 +1818,13 @@ buildActionMask = 2147483647; files = ( 825F19D11EEFF19700969EF1 /* ObjectLiteralRuleTests.swift in Sources */, - 3B12C9C31C320A53000B423F /* Yaml+SwiftLintTests.swift in Sources */, + 3B12C9C31C320A53000B423F /* YamlSwiftLintTests.swift in Sources */, E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */, D4C27C001E12DFF500DF713E /* LinterCacheTests.swift in Sources */, D45255C81F0932F8003C9B56 /* RuleDescription+Examples.swift in Sources */, E81ADD721ED5ED9D000CD451 /* RegionTests.swift in Sources */, D4998DE91DF194F20006E05D /* FileHeaderRuleTests.swift in Sources */, + 8B01E50220A4349100C9233E /* FunctionParameterCountRuleTests.swift in Sources */, 47ACC89C1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift in Sources */, E81ADD741ED6052F000CD451 /* CommandTests.swift in Sources */, 29FFC37D1F157BDE007E4825 /* FileLengthRuleTests.swift in Sources */, @@ -1832,6 +1867,7 @@ 3B63D46F1E1F09DF0057BE35 /* LineLengthRuleTests.swift in Sources */, 3BCC04D41C502BAB006073C3 /* RuleConfigurationTests.swift in Sources */, E809EDA31B8A73FB00399043 /* ConfigurationTests.swift in Sources */, + 8F2CC1CD20A6A189006ED34F /* FileNameRuleTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1874,7 +1910,7 @@ baseConfigurationReference = D0D1212619E878CC005E4BAA /* Debug.xcconfig */; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.10; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1883,7 +1919,7 @@ baseConfigurationReference = D0D1212819E878CC005E4BAA /* Release.xcconfig */; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.10; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1899,7 +1935,7 @@ INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFramework; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = ( @@ -1922,7 +1958,7 @@ INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFramework; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = ( @@ -1940,7 +1976,7 @@ INFOPLIST_FILE = "Tests/SwiftLintFrameworkTests/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFrameworkTests; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1951,7 +1987,7 @@ INFOPLIST_FILE = "Tests/SwiftLintFrameworkTests/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFrameworkTests; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1960,7 +1996,7 @@ baseConfigurationReference = D0D1212719E878CC005E4BAA /* Profile.xcconfig */; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.10; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Profile; }; @@ -1976,7 +2012,7 @@ INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFramework; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = ( @@ -1994,7 +2030,7 @@ INFOPLIST_FILE = "Tests/SwiftLintFrameworkTests/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFrameworkTests; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Profile; }; @@ -2003,7 +2039,7 @@ baseConfigurationReference = D0D1212919E878CC005E4BAA /* Test.xcconfig */; buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.10; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Test; }; @@ -2019,7 +2055,7 @@ INFOPLIST_FILE = "Source/SwiftLintFramework/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFramework; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WARNING_CFLAGS = ( @@ -2037,7 +2073,7 @@ INFOPLIST_FILE = "Tests/SwiftLintFrameworkTests/Supporting Files/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftLintFrameworkTests; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Test; }; @@ -2049,7 +2085,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -2061,7 +2097,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Test; }; @@ -2073,7 +2109,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -2085,7 +2121,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks/SwiftLintFramework.framework/Versions/Current/Frameworks /Library/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "io.realm.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Profile; }; diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index bae11444312..a3b88116474 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -194,6 +194,19 @@ extension FileLengthRuleTests { ] } +extension FileNameRuleTests { + static var allTests: [(String, (FileNameRuleTests) -> () throws -> Void)] = [ + ("testMainDoesntTrigger", testMainDoesntTrigger), + ("testLinuxMainDoesntTrigger", testLinuxMainDoesntTrigger), + ("testClassNameDoesntTrigger", testClassNameDoesntTrigger), + ("testStructNameDoesntTrigger", testStructNameDoesntTrigger), + ("testExtensionNameDoesntTrigger", testExtensionNameDoesntTrigger), + ("testMisspelledNameDoesTrigger", testMisspelledNameDoesTrigger), + ("testMisspelledNameDoesntTriggerWithOverride", testMisspelledNameDoesntTriggerWithOverride), + ("testMainDoesTriggerWithoutOverride", testMainDoesTriggerWithoutOverride) + ] +} + extension FunctionBodyLengthRuleTests { static var allTests: [(String, (FunctionBodyLengthRuleTests) -> () throws -> Void)] = [ ("testFunctionBodyLengths", testFunctionBodyLengths), @@ -202,6 +215,13 @@ extension FunctionBodyLengthRuleTests { ] } +extension FunctionParameterCountRuleTests { + static var allTests: [(String, (FunctionParameterCountRuleTests) -> () throws -> Void)] = [ + ("testFunctionParameterCount", testFunctionParameterCount), + ("testDefaultFunctionParameterCount", testDefaultFunctionParameterCount) + ] +} + extension GenericTypeNameRuleTests { static var allTests: [(String, (GenericTypeNameRuleTests) -> () throws -> Void)] = [ ("testGenericTypeName", testGenericTypeName), @@ -375,8 +395,8 @@ extension RequiredEnumCaseRuleTestCase { ] } -extension RuleConfigurationsTests { - static var allTests: [(String, (RuleConfigurationsTests) -> () throws -> Void)] = [ +extension RuleConfigurationTests { + static var allTests: [(String, (RuleConfigurationTests) -> () throws -> Void)] = [ ("testNameConfigurationSetsCorrectly", testNameConfigurationSetsCorrectly), ("testNameConfigurationThrowsOnBadConfig", testNameConfigurationThrowsOnBadConfig), ("testNameConfigurationMinLengthThreshold", testNameConfigurationMinLengthThreshold), @@ -448,6 +468,7 @@ extension RulesTests { ("testLowerACLThanParent", testLowerACLThanParent), ("testEmptyParenthesesWithTrailingClosure", testEmptyParenthesesWithTrailingClosure), ("testEmptyString", testEmptyString), + ("testEmptyXCTestMethods", testEmptyXCTestMethods), ("testExplicitACL", testExplicitACL), ("testExplicitEnumRawValue", testExplicitEnumRawValue), ("testExplicitInit", testExplicitInit), @@ -461,6 +482,7 @@ extension RulesTests { ("testForceUnwrapping", testForceUnwrapping), ("testForWhere", testForWhere), ("testFunctionBodyLength", testFunctionBodyLength), + ("testFunctionDefaultParameterAtEnd", testFunctionDefaultParameterAtEnd), ("testFunctionParameterCount", testFunctionParameterCount), ("testImplicitGetter", testImplicitGetter), ("testImplicitlyUnwrappedOptional", testImplicitlyUnwrappedOptional), @@ -630,7 +652,9 @@ XCTMain([ testCase(ExtendedNSStringTests.allTests), testCase(FileHeaderRuleTests.allTests), testCase(FileLengthRuleTests.allTests), + testCase(FileNameRuleTests.allTests), testCase(FunctionBodyLengthRuleTests.allTests), + testCase(FunctionParameterCountRuleTests.allTests), testCase(GenericTypeNameRuleTests.allTests), testCase(IdentifierNameRuleTests.allTests), testCase(ImplicitlyUnwrappedOptionalConfigurationTests.allTests), @@ -647,7 +671,7 @@ XCTMain([ testCase(RegionTests.allTests), testCase(ReporterTests.allTests), testCase(RequiredEnumCaseRuleTestCase.allTests), - testCase(RuleConfigurationsTests.allTests), + testCase(RuleConfigurationTests.allTests), testCase(RuleTests.allTests), testCase(RulesTests.allTests), testCase(SourceKitCrashTests.allTests), diff --git a/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift b/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift new file mode 100644 index 00000000000..840f6fab4c3 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/FileNameRuleTests.swift @@ -0,0 +1,52 @@ +import SourceKittenFramework +import SwiftLintFramework +import XCTest + +private let fixturesDirectory = #file.bridge() + .deletingLastPathComponent.bridge() + .appendingPathComponent("Resources/FileNameRuleFixtures") + +class FileNameRuleTests: XCTestCase { + private func validate(fileName: String, excludedOverride: [String]? = nil) throws -> [StyleViolation] { + let file = File(path: fixturesDirectory.stringByAppendingPathComponent(fileName))! + let rule: FileNameRule + if let excluded = excludedOverride { + rule = try FileNameRule(configuration: ["excluded": excluded]) + } else { + rule = FileNameRule() + } + return rule.validate(file: file) + } + + func testMainDoesntTrigger() { + XCTAssert(try validate(fileName: "main.swift").isEmpty) + } + + func testLinuxMainDoesntTrigger() { + XCTAssert(try validate(fileName: "LinuxMain.swift").isEmpty) + } + + func testClassNameDoesntTrigger() { + XCTAssert(try validate(fileName: "MyClass.swift").isEmpty) + } + + func testStructNameDoesntTrigger() { + XCTAssert(try validate(fileName: "MyStruct.swift").isEmpty) + } + + func testExtensionNameDoesntTrigger() { + XCTAssert(try validate(fileName: "NSString+Extension.swift").isEmpty) + } + + func testMisspelledNameDoesTrigger() { + XCTAssertEqual(try validate(fileName: "MyStructf.swift").count, 1) + } + + func testMisspelledNameDoesntTriggerWithOverride() { + XCTAssert(try validate(fileName: "MyStructf.swift", excludedOverride: ["MyStructf.swift"]).isEmpty) + } + + func testMainDoesTriggerWithoutOverride() { + XCTAssertEqual(try validate(fileName: "main.swift", excludedOverride: []).count, 1) + } +} diff --git a/Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift b/Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift new file mode 100644 index 00000000000..388c5802bb9 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/FunctionParameterCountRuleTests.swift @@ -0,0 +1,54 @@ +import SwiftLintFramework +import XCTest + +private func funcWithParameters(_ parameters: String, violates: Bool = false) -> String { + let marker = violates ? "↓" : "" + + return "func \(marker)abc(\(parameters)) {}\n" +} + +private func violatingFuncWithParameters(_ parameters: String) -> String { + return funcWithParameters(parameters, violates: true) +} + +class FunctionParameterCountRuleTests: XCTestCase { + + func testFunctionParameterCount() { + let baseDescription = FunctionParameterCountRule.description + let nonTriggeringExamples = [ + funcWithParameters(repeatElement("x: Int, ", count: 3).joined() + "x: Int") + ] + + let triggeringExamples = [ + funcWithParameters(repeatElement("x: Int, ", count: 5).joined() + "x: Int") + ] + + let description = baseDescription.with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + + verifyRule(description) + } + + func testDefaultFunctionParameterCount() { + let baseDescription = FunctionParameterCountRule.description + let nonTriggeringExamples = [ + funcWithParameters(repeatElement("x: Int, ", count: 3).joined() + "x: Int") + ] + + let defaultParams = repeatElement("x: Int = 0, ", count: 2).joined() + "x: Int = 0" + let triggeringExamples = [ + funcWithParameters(repeatElement("x: Int, ", count: 3).joined() + defaultParams) + ] + + let description = baseDescription.with(nonTriggeringExamples: nonTriggeringExamples) + .with(triggeringExamples: triggeringExamples) + + verifyRule(description, ruleConfiguration: ["ignores_default_parameters": false]) + } + + private func violations(_ string: String) -> [StyleViolation] { + let config = makeConfig(nil, FunctionParameterCountRule.description.identifier)! + + return SwiftLintFrameworkTests.violations(string, config: config) + } +} diff --git a/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift b/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift index 479d4c4a96f..8286f54567b 100644 --- a/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift +++ b/Tests/SwiftLintFrameworkTests/LinterCacheTests.swift @@ -323,6 +323,8 @@ class LinterCacheTests: XCTestCase { func testDetectSwiftVersion() { #if swift(>=4.2.0) let version = "4.2.0" + #elseif swift(>=4.1.2) + let version = "4.1.2" #elseif swift(>=4.1.1) let version = "4.1.1" #elseif swift(>=4.1.0) @@ -337,6 +339,8 @@ class LinterCacheTests: XCTestCase { let version = "4.0.0" #elseif swift(>=3.4.0) let version = "4.2.0" // Since we can't pass SWIFT_VERSION=3 to sourcekit, it returns 4.2.0 + #elseif swift(>=3.3.2) + let version = "4.1.2" // Since we can't pass SWIFT_VERSION=3 to sourcekit, it returns 4.1.2 #elseif swift(>=3.3.1) let version = "4.1.1" // Since we can't pass SWIFT_VERSION=3 to sourcekit, it returns 4.1.1 #elseif swift(>=3.3.0) diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift new file mode 100644 index 00000000000..115660e78ef --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/LinuxMain.swift @@ -0,0 +1,10 @@ +@testable import SwiftLintFrameworkTests +import XCTest + +extension AttributesRuleTests { + static var allTests: [(String, (AttributesRuleTests) -> () throws -> Void)] = [ + ("testAttributesWithDefaultConfiguration", testAttributesWithDefaultConfiguration), + ("testAttributesWithAlwaysOnSameLine", testAttributesWithAlwaysOnSameLine), + ("testAttributesWithAlwaysOnLineAbove", testAttributesWithAlwaysOnLineAbove) + ] +} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyClass.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyClass.swift new file mode 100644 index 00000000000..0463202d419 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyClass.swift @@ -0,0 +1,2 @@ +struct MyStruct {} +class MyClass {} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStruct.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStruct.swift new file mode 100644 index 00000000000..0463202d419 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStruct.swift @@ -0,0 +1,2 @@ +struct MyStruct {} +class MyClass {} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStructf.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStructf.swift new file mode 100644 index 00000000000..0463202d419 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/MyStructf.swift @@ -0,0 +1,2 @@ +struct MyStruct {} +class MyClass {} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NSString+Extension.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NSString+Extension.swift new file mode 100644 index 00000000000..be7ae4f8230 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/NSString+Extension.swift @@ -0,0 +1,6 @@ +struct MyStruct {} +class MyClass {} + +extension NSString { + func asdf() {} +} diff --git a/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/main.swift b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/main.swift new file mode 100644 index 00000000000..990af910329 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/Resources/FileNameRuleFixtures/main.swift @@ -0,0 +1,3 @@ +struct A {} + +print("hello") diff --git a/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift index 68160949e18..21b7cdfe0aa 100644 --- a/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift +++ b/Tests/SwiftLintFrameworkTests/RuleConfigurationTests.swift @@ -4,7 +4,7 @@ import XCTest // swiftlint:disable type_body_length -class RuleConfigurationsTests: XCTestCase { +class RuleConfigurationTests: XCTestCase { func testNameConfigurationSetsCorrectly() { let config = [ "min_length": ["warning": 17, "error": 7], "max_length": ["warning": 170, "error": 700], diff --git a/Tests/SwiftLintFrameworkTests/RulesTests.swift b/Tests/SwiftLintFrameworkTests/RulesTests.swift index a462c5fc2bb..ec82b97ed74 100644 --- a/Tests/SwiftLintFrameworkTests/RulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/RulesTests.swift @@ -102,6 +102,10 @@ class RulesTests: XCTestCase { verifyRule(EmptyStringRule.description) } + func testEmptyXCTestMethods() { + verifyRule(EmptyXCTestMethodRule.description) + } + func testExplicitACL() { verifyRule(ExplicitACLRule.description) } @@ -154,6 +158,10 @@ class RulesTests: XCTestCase { verifyRule(FunctionBodyLengthRule.description) } + func testFunctionDefaultParameterAtEnd() { + verifyRule(FunctionDefaultParameterAtEndRule.description) + } + func testFunctionParameterCount() { verifyRule(FunctionParameterCountRule.description) } diff --git a/Tests/SwiftLintFrameworkTests/Yaml+SwiftLintTests.swift b/Tests/SwiftLintFrameworkTests/YamlSwiftLintTests.swift similarity index 100% rename from Tests/SwiftLintFrameworkTests/Yaml+SwiftLintTests.swift rename to Tests/SwiftLintFrameworkTests/YamlSwiftLintTests.swift