diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5a4160a6..200c2e273e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ ##### Enhancements +* Add `implicitly_unwrapped_optional` rule + that warns when using implicitly unwrapped optional, + except cases when this IUO is IBOutlet. + [Siarhei Fedartsou](https://github.com/SiarheiFedartsou/) + [#56](https://github.com/realm/SwiftLint/issues/56) + * Performance improvements to `generic_type_name`, `redundant_nil_coalescing`, `mark`, `first_where` and `vertical_whitespace` rules. @@ -274,7 +280,7 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#1109](https://github.com/realm/SwiftLint/issues/1109) -* Add `compiler_protocol_init` rule that flags usage of initializers +* Add `compiler_protocol_init` rule that flags usage of initializers declared in protocols used by the compiler such as `ExpressibleByArrayLiteral` that shouldn't be called directly. Instead, you should use a literal anywhere a concrete type conforming to the protocol is expected by the context. @@ -290,7 +296,7 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#51](https://github.com/realm/SwiftLint/issues/51) -* Update `vertical_whitespace` rule to allow configuration of the number of +* Update `vertical_whitespace` rule to allow configuration of the number of consecutive empty lines before a violation using `max_empty_lines`. The default value is still 1 line. [Aaron McTavish](https://github.com/aamctustwo) @@ -316,7 +322,7 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#973](https://github.com/realm/SwiftLint/issues/973) -* Add `unused_optional_binding` rule that will check for optional bindings +* Add `unused_optional_binding` rule that will check for optional bindings not being used. [Rafael Machado](https://github.com/rakaramos/) [#1116](https://github.com/realm/SwiftLint/issues/1116) diff --git a/Source/SwiftLintFramework/Models/MasterRuleList.swift b/Source/SwiftLintFramework/Models/MasterRuleList.swift index 34b59a1e9c..1cc5352252 100644 --- a/Source/SwiftLintFramework/Models/MasterRuleList.swift +++ b/Source/SwiftLintFramework/Models/MasterRuleList.swift @@ -106,6 +106,7 @@ public let masterRuleList = RuleList(rules: GenericTypeNameRule.self, IdentifierNameRule.self, ImplicitGetterRule.self, + ImplicitlyUnwrappedOptionalRule.self, LargeTupleRule.self, LeadingWhitespaceRule.self, LegacyCGGeometryFunctionsRule.self, diff --git a/Source/SwiftLintFramework/Rules/ImplicitlyUnwrappedOptionalRule.swift b/Source/SwiftLintFramework/Rules/ImplicitlyUnwrappedOptionalRule.swift new file mode 100644 index 0000000000..1eeac2ad80 --- /dev/null +++ b/Source/SwiftLintFramework/Rules/ImplicitlyUnwrappedOptionalRule.swift @@ -0,0 +1,77 @@ +// +// ImplicitlyUnwrappedOptionalRule.swift +// SwiftLint +// +// Created by Siarhei Fedartsou on 17/03/17. +// Copyright © 2016 Realm. All rights reserved. +// + +import Foundation +import SourceKittenFramework + +public struct ImplicitlyUnwrappedOptionalRule: ASTRule, ConfigurationProviderRule, OptInRule { + public var configuration = ImplicitlyUnwrappedOptionalConfiguration(mode: .allExceptIBOutlets, + severity: SeverityConfiguration(.warning)) + + public init() {} + + public static let description = RuleDescription( + identifier: "implicitly_unwrapped_optional", + name: "Implicitly Unwrapped Optional", + description: "Implicitly unwrapped optionals should be avoided when possible.", + nonTriggeringExamples: [ + "@IBOutlet private var label: UILabel!", + "@IBOutlet var label: UILabel!", + "@IBOutlet var label: [UILabel!]", + "if !boolean {}", + "let int: Int? = 42", + "let int: Int? = nil" + ], + triggeringExamples: [ + "let label: UILabel!", + "let IBOutlet: UILabel!", + "let labels: [UILabel!]", + "var ints: [Int!] = [42, nil, 42]", + "let label: IBOutlet!", + "let int: Int! = 42", + "let int: Int! = nil", + "var int: Int! = 42", + "let int: ImplicitlyUnwrappedOptional", + "let collection: AnyCollection", + "func foo(int: Int!) {}" + ] + ) + + private func hasImplicitlyUnwrappedOptional(_ typeName: String) -> Bool { + return typeName.range(of: "!") != nil || typeName.range(of: "ImplicitlyUnwrappedOptional<") != nil + } + + public func validate(file: File, kind: SwiftDeclarationKind, + dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { + guard SwiftDeclarationKind.variableKinds().contains(kind) else { + return [] + } + + guard let typeName = dictionary.typeName else { return [] } + guard hasImplicitlyUnwrappedOptional(typeName) else { return [] } + + if configuration.mode == .allExceptIBOutlets { + let isOutlet = dictionary.enclosedSwiftAttributes.contains("source.decl.attribute.iboutlet") + if isOutlet { return [] } + } + + let location: Location + if let offset = dictionary.offset { + location = Location(file: file, byteOffset: offset) + } else { + location = Location(file: file.path) + } + + return [ + StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity.severity, + location: location) + ] + } + +} diff --git a/Source/SwiftLintFramework/Rules/RuleConfigurations/ImplicitlyUnwrappedOptionalConfiguration.swift b/Source/SwiftLintFramework/Rules/RuleConfigurations/ImplicitlyUnwrappedOptionalConfiguration.swift new file mode 100644 index 0000000000..9d4033f6be --- /dev/null +++ b/Source/SwiftLintFramework/Rules/RuleConfigurations/ImplicitlyUnwrappedOptionalConfiguration.swift @@ -0,0 +1,59 @@ +// +// ImplicitlyUnwrappedOptionalConfiguration.swift +// SwiftLint +// +// Created by Siarhei Fedartsou on 18/03/17. +// Copyright © 2017 Realm. All rights reserved. +// + +import Foundation + +// swiftlint:disable:next type_name +public enum ImplicitlyUnwrappedOptionalModeConfiguration: String { + case all = "all" + case allExceptIBOutlets = "all_except_iboutlets" + + init(value: Any) throws { + if let string = (value as? String)?.lowercased(), + let value = ImplicitlyUnwrappedOptionalModeConfiguration(rawValue: string) { + self = value + } else { + throw ConfigurationError.unknownConfiguration + } + } +} + +public struct ImplicitlyUnwrappedOptionalConfiguration: RuleConfiguration, Equatable { + private(set) var severity: SeverityConfiguration + private(set) var mode: ImplicitlyUnwrappedOptionalModeConfiguration + + init(mode: ImplicitlyUnwrappedOptionalModeConfiguration, severity: SeverityConfiguration) { + self.mode = mode + self.severity = severity + } + + public var consoleDescription: String { + return severity.consoleDescription + + ", mode: \(mode)" + } + + public mutating func apply(configuration: Any) throws { + guard let configuration = configuration as? [String: Any] else { + throw ConfigurationError.unknownConfiguration + } + + if let modeString = configuration["mode"] { + try mode = ImplicitlyUnwrappedOptionalModeConfiguration(value: modeString) + } + + if let severityString = configuration["severity"] as? String { + try severity.apply(configuration: severityString) + } + } + + public static func == (lhs: ImplicitlyUnwrappedOptionalConfiguration, + rhs: ImplicitlyUnwrappedOptionalConfiguration) -> Bool { + return lhs.severity == rhs.severity && + lhs.mode == rhs.mode + } +} diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index faa915a05c..6c08153f90 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -49,6 +49,10 @@ 3BCC04D41C502BAB006073C3 /* RuleConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCC04D31C502BAB006073C3 /* RuleConfigurationTests.swift */; }; 3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BD9CD3C1C37175B009A5D25 /* YamlParser.swift */; }; 3BDB224B1C345B4900473680 /* ProjectMock in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB224A1C345B4900473680 /* ProjectMock */; }; + 47ACC8981E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ACC8971E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift */; }; + 47ACC89A1E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ACC8991E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift */; }; + 47ACC89C1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47ACC89B1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift */; }; + 47FF3BE11E7C75B600187E6D /* ImplicitlyUnwrappedOptionalRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47FF3BDF1E7C745100187E6D /* ImplicitlyUnwrappedOptionalRule.swift */; }; 4A9A3A3A1DC1D75F00DF5183 /* HTMLReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9A3A391DC1D75F00DF5183 /* HTMLReporter.swift */; }; 4DB7815E1CAD72BA00BC4723 /* LegacyCGGeometryFunctionsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */; }; 4DCB8E7F1CBE494E0070FCF0 /* RegexHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCB8E7D1CBE43640070FCF0 /* RegexHelpers.swift */; }; @@ -312,6 +316,10 @@ 3BCC04D31C502BAB006073C3 /* RuleConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleConfigurationTests.swift; sourceTree = ""; }; 3BD9CD3C1C37175B009A5D25 /* YamlParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YamlParser.swift; sourceTree = ""; }; 3BDB224A1C345B4900473680 /* ProjectMock */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ProjectMock; sourceTree = ""; }; + 47ACC8971E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitlyUnwrappedOptionalConfiguration.swift; sourceTree = ""; }; + 47ACC8991E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitlyUnwrappedOptionalConfigurationTests.swift; sourceTree = ""; }; + 47ACC89B1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitlyUnwrappedOptionalRuleTests.swift; sourceTree = ""; }; + 47FF3BDF1E7C745100187E6D /* ImplicitlyUnwrappedOptionalRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImplicitlyUnwrappedOptionalRule.swift; sourceTree = ""; }; 4A9A3A391DC1D75F00DF5183 /* HTMLReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLReporter.swift; sourceTree = ""; }; 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyCGGeometryFunctionsRule.swift; sourceTree = ""; }; 4DCB8E7D1CBE43640070FCF0 /* RegexHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexHelpers.swift; sourceTree = ""; }; @@ -559,6 +567,7 @@ D43B04671E07228D004016AF /* ColonConfiguration.swift */, 67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */, D4C4A3511DEFBBB700E0E04C /* FileHeaderConfiguration.swift */, + 47ACC8971E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift */, 3B034B6C1E0BE544005D49A9 /* LineLengthConfiguration.swift */, 3BCC04D01C4F56D3006073C3 /* NameConfiguration.swift */, D93DA3CF1E699E4E00809827 /* NestingConfiguration.swift */, @@ -761,6 +770,8 @@ 006204DD1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift */, 67EB4DFB1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift */, 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */, + 47ACC8991E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift */, + 47ACC89B1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift */, ); name = SwiftLintFrameworkTests; path = Tests/SwiftLintFrameworkTests; @@ -845,6 +856,7 @@ E88DEA931B099C0900A66CB0 /* IdentifierNameRule.swift */, D4130D961E16183F00242361 /* IdentifierNameRuleExamples.swift */, D43DB1071DC573DA00281215 /* ImplicitGetterRule.swift */, + 47FF3BDF1E7C745100187E6D /* ImplicitlyUnwrappedOptionalRule.swift */, D4DA1DF91E18D6200037413D /* LargeTupleRule.swift */, E88DEA7D1B098F2A00A66CB0 /* LeadingWhitespaceRule.swift */, 4DB7815C1CAD690100BC4723 /* LegacyCGGeometryFunctionsRule.swift */, @@ -1188,6 +1200,7 @@ 3BCC04CD1C4F5694006073C3 /* ConfigurationError.swift in Sources */, D4C4A34E1DEA877200E0E04C /* FileHeaderRule.swift in Sources */, 009E092A1DFEE4DD00B588A7 /* ProhibitedSuperConfiguration.swift in Sources */, + 47FF3BE11E7C75B600187E6D /* ImplicitlyUnwrappedOptionalRule.swift in Sources */, BFF028AE1CBCF8A500B38A9D /* TrailingWhitespaceConfiguration.swift in Sources */, 3B034B6E1E0BE549005D49A9 /* LineLengthConfiguration.swift in Sources */, D4C4A34C1DEA4FF000E0E04C /* AttributesConfiguration.swift in Sources */, @@ -1242,6 +1255,7 @@ D43B04691E072291004016AF /* ColonConfiguration.swift in Sources */, D4130D991E16CC1300242361 /* TypeNameRuleExamples.swift in Sources */, 24E17F721B14BB3F008195BE /* File+Cache.swift in Sources */, + 47ACC8981E7DC74E0088EEB2 /* ImplicitlyUnwrappedOptionalConfiguration.swift in Sources */, 009E09281DFEE4C200B588A7 /* ProhibitedSuperRule.swift in Sources */, E80E018F1B92C1350078EB70 /* Region.swift in Sources */, E88198581BEA956C00333A11 /* FunctionBodyLengthRule.swift in Sources */, @@ -1333,6 +1347,7 @@ E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */, D4C27C001E12DFF500DF713E /* LinterCacheTests.swift in Sources */, D4998DE91DF194F20006E05D /* FileHeaderRuleTests.swift in Sources */, + 47ACC89C1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift in Sources */, 006204DE1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift in Sources */, 02FD8AEF1BFC18D60014BFFB /* ExtendedNSStringTests.swift in Sources */, D4CA758F1E2DEEA500A40E8A /* NumberSeparatorRuleTests.swift in Sources */, @@ -1349,6 +1364,7 @@ 3B30C4A11C3785B300E04027 /* YamlParserTests.swift in Sources */, D4998DE71DF191380006E05D /* AttributesRuleTests.swift in Sources */, E88198631BEA9A5400333A11 /* RulesTests.swift in Sources */, + 47ACC89A1E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift in Sources */, D46202211E16002A0027AAD1 /* Swift2RulesTests.swift in Sources */, 67932E2D1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift in Sources */, C9802F2F1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift in Sources */, diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 5aebabeb25..92feea3fde 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -19,6 +19,8 @@ XCTMain([ testCase(ExtendedNSStringTests.allTests), testCase(FileHeaderRuleTests.allTests), testCase(FunctionBodyLengthRuleTests.allTests), + testCase(ImplicitlyUnwrappedOptionalConfigurationTests.allTests), + testCase(ImplicitlyUnwrappedOptionalRuleTests.allTests), testCase(IntegrationTests.allTests), testCase(LineLengthConfigurationTests.allTests), testCase(LineLengthRuleTests.allTests), diff --git a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift new file mode 100644 index 0000000000..d85094f040 --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalConfigurationTests.swift @@ -0,0 +1,64 @@ +// +// ImplicitlyUnwrappedOptionalConfigurationTests.swift +// SwiftLint +// +// Created by Siarhei Fedartsou on 18/03/17. +// Copyright © 2017 Realm. All rights reserved. +// + +import SourceKittenFramework +@testable import SwiftLintFramework +import XCTest + +// swiftlint:disable:next type_name +class ImplicitlyUnwrappedOptionalConfigurationTests: XCTestCase { + + func testImplicitlyUnwrappedOptionalConfigurationProperlyAppliesConfigurationFromDictionary() throws { + var configuration = ImplicitlyUnwrappedOptionalConfiguration(mode: .allExceptIBOutlets, + severity: SeverityConfiguration(.warning)) + + try configuration.apply(configuration: ["mode": "all", "severity": "error"]) + XCTAssertEqual(configuration.mode, .all) + XCTAssertEqual(configuration.severity.severity, .error) + + try configuration.apply(configuration: ["mode": "all_except_iboutlets"]) + XCTAssertEqual(configuration.mode, .allExceptIBOutlets) + XCTAssertEqual(configuration.severity.severity, .error) + + try configuration.apply(configuration: ["severity": "warning"]) + XCTAssertEqual(configuration.mode, .allExceptIBOutlets) + XCTAssertEqual(configuration.severity.severity, .warning) + + try configuration.apply(configuration: ["mode": "all", "severity": "warning"]) + XCTAssertEqual(configuration.mode, .all) + XCTAssertEqual(configuration.severity.severity, .warning) + } + + func testImplicitlyUnwrappedOptionalConfigurationThrowsOnBadConfig() { + let badConfigs: [[String: Any]] = [ + ["mode": "everything"], + ["mode": false], + ["mode": 42] + ] + + for badConfig in badConfigs { + var configuration = ImplicitlyUnwrappedOptionalConfiguration(mode: .allExceptIBOutlets, + severity: SeverityConfiguration(.warning)) + checkError(ConfigurationError.unknownConfiguration) { + try configuration.apply(configuration: badConfig) + } + } + } + +} + +extension ImplicitlyUnwrappedOptionalConfigurationTests { + static var allTests: [(String, (ImplicitlyUnwrappedOptionalConfigurationTests) -> () throws -> Void)] { + return [ + ("testImplicitlyUnwrappedOptionalConfigurationProperlyAppliesConfigurationFromDictionary", + testImplicitlyUnwrappedOptionalConfigurationProperlyAppliesConfigurationFromDictionary), + ("testImplicitlyUnwrappedOptionalConfigurationThrowsOnBadConfig", + testImplicitlyUnwrappedOptionalConfigurationThrowsOnBadConfig) + ] + } +} diff --git a/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift new file mode 100644 index 0000000000..2011ff450c --- /dev/null +++ b/Tests/SwiftLintFrameworkTests/ImplicitlyUnwrappedOptionalRuleTests.swift @@ -0,0 +1,50 @@ +// +// ImplicitlyUnwrappedOptionalRuleTests.swift +// SwiftLint +// +// Created by Siarhei Fedartsou on 18/03/17. +// Copyright © 2017 Realm. All rights reserved. +// + +import Foundation +@testable import SwiftLintFramework +import XCTest + +class ImplicitlyUnwrappedOptionalRuleTests: XCTestCase { + + func testImplicitlyUnwrappedOptionalRuleDefaultConfiguration() { + let rule = ImplicitlyUnwrappedOptionalRule() + XCTAssertEqual(rule.configuration.mode, .allExceptIBOutlets) + XCTAssertEqual(rule.configuration.severity.severity, .warning) + } + + func testImplicitlyUnwrappedOptionalRuleWarnsOnOutletsInAllMode() { + let baseDescription = ImplicitlyUnwrappedOptionalRule.description + let triggeringExamples = [ + "@IBOutlet private var label: UILabel!", + "@IBOutlet var label: UILabel!", + "let int: Int!" + ] + + let nonTriggeringExamples = ["if !boolean {}"] + let description = RuleDescription(identifier: baseDescription.identifier, + name: baseDescription.name, + description: baseDescription.description, + nonTriggeringExamples: nonTriggeringExamples, + triggeringExamples: triggeringExamples, + corrections: baseDescription.corrections) + verifyRule(description, ruleConfiguration: ["mode": "all"], + commentDoesntViolate: true, stringDoesntViolate: true) + } +} + +extension ImplicitlyUnwrappedOptionalRuleTests { + static var allTests: [(String, (ImplicitlyUnwrappedOptionalRuleTests) -> () throws -> Void)] { + return [ + ("testImplicitlyUnwrappedOptionalRuleDefaultConfiguration", + testImplicitlyUnwrappedOptionalRuleDefaultConfiguration), + ("testImplicitlyUnwrappedOptionalRuleWarnsOnOutletsInAllMode", + testImplicitlyUnwrappedOptionalRuleWarnsOnOutletsInAllMode) + ] + } +} diff --git a/Tests/SwiftLintFrameworkTests/RulesTests.swift b/Tests/SwiftLintFrameworkTests/RulesTests.swift index d7dd6accd0..ad13c57a52 100644 --- a/Tests/SwiftLintFrameworkTests/RulesTests.swift +++ b/Tests/SwiftLintFrameworkTests/RulesTests.swift @@ -125,6 +125,10 @@ class RulesTests: XCTestCase { verifyRule(ImplicitGetterRule.description) } + func testImplicitlyUnwrappedOptional() { + verifyRule(ImplicitlyUnwrappedOptionalRule.description) + } + func testLargeTuple() { verifyRule(LargeTupleRule.description) } @@ -382,6 +386,7 @@ extension RulesTests { ("testGenericTypeName", testGenericTypeName), ("testIdentifierName", testIdentifierName), ("testImplicitGetter", testImplicitGetter), + ("testImplicitlyUnwrappedOptional", testImplicitlyUnwrappedOptional), ("testLargeTuple", testLargeTuple), ("testLeadingWhitespace", testLeadingWhitespace), ("testLegacyCGGeometryFunctions", testLegacyCGGeometryFunctions),