Separated nesting level counting for types and functions in nesting rule (fixes #1151). Enhanced nesting rule to search for nested types and functions within closures and statements. Enhanced nesting rule to allow for one nested type within a function even if breaking a maximum type level nesting (fixes #1151).

This commit is contained in:
Skoti
2017-06-26 23:04:19 +02:00
committed by JP Simard
parent 474cb2bd29
commit b9841595a5
10 changed files with 993 additions and 96 deletions
+16
View File
@@ -17,6 +17,19 @@
[JP Simard](https://github.com/jpsim)
[#3412](https://github.com/realm/SwiftLint/issues/3412)
* Renamed `statement_level` to `function_level` in `nesting` rule configuration.
[Skoti](https://github.com/Skoti)
* Separated `type_level` and `function_level` counting in `nesting` rule.
[Skoti](https://github.com/Skoti)
[#1151](https://github.com/realm/SwiftLint/issues/1151)
* `function_level` in `nesting` rule defaults to 2 levels.
[Skoti](https://github.com/Skoti)
* Added `check_nesting_in_closures_and_statements` in `nesting` rule to search for nested types and functions within closures and statements. Defaults to `true`.
[Skoti](https://github.com/Skoti)
#### Experimental
* None.
@@ -27,6 +40,9 @@
`Never`.
[Artem Garmash](https://github.com/agarmash)
[#3286](https://github.com/realm/SwiftLint/issues/3286)
* Added `always_allow_one_type_in_functions` option in `nesting` rule configuration. Defaults to `false`. This allows to nest one type within a function even if breaking the maximum `type_level`.
[Skoti](https://github.com/Skoti)
[#1151](https://github.com/realm/SwiftLint/issues/1151)
#### Bug Fixes
@@ -34,4 +34,12 @@ extension SwiftDeclarationKind {
.associatedtype,
.enum
]
internal static let extensionKinds: Set<SwiftDeclarationKind> = [
.extension,
.extensionClass,
.extensionEnum,
.extensionProtocol,
.extensionStruct
]
}
@@ -1,10 +1,10 @@
import SourceKittenFramework
public struct NestingRule: ASTRule, ConfigurationProviderRule, AutomaticTestableRule {
public struct NestingRule: ConfigurationProviderRule {
public var configuration = NestingConfiguration(typeLevelWarning: 1,
typeLevelError: nil,
statementLevelWarning: 5,
statementLevelError: nil)
functionLevelWarning: 2,
functionLevelError: nil)
public init() {}
@@ -12,79 +12,151 @@ public struct NestingRule: ASTRule, ConfigurationProviderRule, AutomaticTestable
identifier: "nesting",
name: "Nesting",
description: "Types should be nested at most 1 level deep, " +
"and statements should be nested at most 5 levels deep.",
kind: .metrics,
nonTriggeringExamples: ["class", "struct", "enum"].flatMap { kind -> [Example] in
[
Example("\(kind) Class0 { \(kind) Class1 {} }\n"),
Example("""
func func0() {
func func1() {
func func2() {
func func3() {
func func4() {
func func5() {
}
}
}
}
}
}
""")
]
} + [Example("enum Enum0 { enum Enum1 { case Case } }")],
triggeringExamples: ["class", "struct", "enum"].map { kind -> Example in
return Example("\(kind) A { \(kind) B { ↓\(kind) C {} } }\n")
} + [
Example("""
func func0() {
func func1() {
func func2() {
func func3() {
func func4() {
func func5() {
↓func func6() {
}
}
}
}
}
}
}
""")
]
"and functions should be nested at most 2 levels deep.",
kind: .metrics,
nonTriggeringExamples: NestingRuleExamples.nonTriggeringExamples,
triggeringExamples: NestingRuleExamples.triggeringExamples
)
public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary) -> [StyleViolation] {
return validate(file: file, kind: kind, dictionary: dictionary, level: 0)
private let omittedStructureKinds: [SwiftStructureKind] =
[.declaration(.enumcase), .declaration(.enumelement)]
+ SwiftDeclarationKind.variableKinds.map { .declaration($0) }
private struct ValidationArgs {
var typeLevel: Int = -1
var functionLevel: Int = -1
var previousKind: SwiftStructureKind?
var violations: [StyleViolation] = []
func with(previousKind: SwiftStructureKind?) -> ValidationArgs {
var args = self
args.previousKind = previousKind
return args
}
}
private func validate(file: SwiftLintFile, kind: SwiftDeclarationKind, dictionary: SourceKittenDictionary,
level: Int) -> [StyleViolation] {
var violations = [StyleViolation]()
let typeKinds = SwiftDeclarationKind.typeKinds
if let offset = dictionary.offset {
let (targetName, targetLevel) = typeKinds.contains(kind)
? ("Types", configuration.typeLevel) : ("Statements", configuration.statementLevel)
if let severity = configuration.severity(with: targetLevel, for: level) {
let threshold = configuration.threshold(with: targetLevel, for: severity)
let pluralSuffix = threshold > 1 ? "s" : ""
violations.append(StyleViolation(
ruleDescription: Self.description,
severity: severity,
location: Location(file: file, byteOffset: offset),
reason: "\(targetName) should be nested at most \(threshold) level\(pluralSuffix) deep"))
public func validate(file: SwiftLintFile) -> [StyleViolation] {
return validate(file: file, substructure: file.structureDictionary.substructure, args: ValidationArgs())
}
private func validate(file: SwiftLintFile, substructure: [SourceKittenDictionary],
args: ValidationArgs) -> [StyleViolation] {
return args.violations + substructure.flatMap { dictionary -> [StyleViolation] in
guard let kindString = dictionary.kind, let structureKind = SwiftStructureKind(kindString) else {
return validate(file: file, substructure: dictionary.substructure, args: args.with(previousKind: nil))
}
guard !omittedStructureKinds.contains(structureKind) else {
return args.violations
}
switch structureKind {
case let .declaration(declarationKind):
return validate(file: file, structureKind: structureKind,
declarationKind: declarationKind, dictionary: dictionary, args: args)
case .expression, .statement:
guard configuration.checkNestingInClosuresAndStatements else {
return args.violations
}
return validate(file: file, substructure: dictionary.substructure,
args: args.with(previousKind: structureKind))
}
}
violations.append(contentsOf: dictionary.substructure.compactMap { subDict in
if let kind = subDict.declarationKind {
return (kind, subDict)
}
}
private func validate(file: SwiftLintFile, structureKind: SwiftStructureKind, declarationKind: SwiftDeclarationKind,
dictionary: SourceKittenDictionary, args: ValidationArgs) -> [StyleViolation] {
let isTypeOrExtension = SwiftDeclarationKind.typeKinds.contains(declarationKind)
|| SwiftDeclarationKind.extensionKinds.contains(declarationKind)
let isFunction = SwiftDeclarationKind.functionKinds.contains(declarationKind)
guard isTypeOrExtension || isFunction else {
return validate(file: file, substructure: dictionary.substructure,
args: args.with(previousKind: structureKind))
}
let currentTypeLevel = isTypeOrExtension ? args.typeLevel + 1 : args.typeLevel
let currentFunctionLevel = isFunction ? args.functionLevel + 1 : args.functionLevel
var violations = args.violations
if let violation = levelViolation(file: file, dictionary: dictionary,
previousKind: args.previousKind,
level: isFunction ? currentFunctionLevel : currentTypeLevel,
forFunction: isFunction) {
violations.append(violation)
}
return validate(file: file, substructure: dictionary.substructure,
args: ValidationArgs(
typeLevel: currentTypeLevel,
functionLevel: currentFunctionLevel,
previousKind: structureKind,
violations: violations
)
)
}
private func levelViolation(file: SwiftLintFile, dictionary: SourceKittenDictionary,
previousKind: SwiftStructureKind?, level: Int, forFunction: Bool) -> StyleViolation? {
guard let offset = dictionary.offset else {
return nil
}.flatMap { kind, subDict in
return validate(file: file, kind: kind, dictionary: subDict, level: level + 1)
})
return violations
}
let targetLevel = forFunction ? configuration.functionLevel : configuration.typeLevel
var violatingSeverity: ViolationSeverity?
if configuration.alwaysAllowOneTypeInFunctions,
case let .declaration(previousDeclarationKind)? = previousKind,
!SwiftDeclarationKind.functionKinds.contains(previousDeclarationKind) {
violatingSeverity = configuration.severity(with: targetLevel, for: level)
} else if forFunction || !configuration.alwaysAllowOneTypeInFunctions || previousKind == nil {
violatingSeverity = configuration.severity(with: targetLevel, for: level)
} else {
violatingSeverity = nil
}
guard let severity = violatingSeverity else {
return nil
}
let targetName = forFunction ? "Functions" : "Types"
let threshold = configuration.threshold(with: targetLevel, for: severity)
let pluralSuffix = threshold > 1 ? "s" : ""
return StyleViolation(
ruleDescription: Self.description,
severity: severity,
location: Location(file: file, byteOffset: offset),
reason: "\(targetName) should be nested at most \(threshold) level\(pluralSuffix) deep"
)
}
}
private enum SwiftStructureKind: Equatable {
case declaration(SwiftDeclarationKind)
case expression(SwiftExpressionKind)
case statement(StatementKind)
init?(_ structureKind: String) {
if let declarationKind = SwiftDeclarationKind(rawValue: structureKind) {
self = .declaration(declarationKind)
} else if let expressionKind = SwiftExpressionKind(rawValue: structureKind) {
self = .expression(expressionKind)
} else if let statementKind = StatementKind(rawValue: structureKind) {
self = .statement(statementKind)
} else {
return nil
}
}
static func == (lhs: SwiftStructureKind, rhs: SwiftStructureKind) -> Bool {
switch (lhs, rhs) {
case let (.declaration(lhsKind), .declaration(rhsKind)):
return lhsKind == rhsKind
case let (.expression(lhsKind), .expression(rhsKind)):
return lhsKind == rhsKind
case let (.statement(lhsKind), .statement(rhsKind)):
return lhsKind == rhsKind
default:
return false
}
}
}
@@ -0,0 +1,415 @@
// swiftlint:disable file_length type_body_length
internal struct NestingRuleExamples {
static let nonTriggeringExamples = nonTriggeringTypeExamples
+ nonTriggeringFunctionExamples
+ nonTriggeringClosureAndStatementExamples
+ nonTriggeringMixedExamples
private static let nonTriggeringTypeExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// default maximum type nesting level
.init("""
\(type) Example_0 {
\(type) Example_1 {}
}
"""),
/*
all variableKinds of SwiftDeclarationKind (except .varParameter which is a function parameter)
are flattend in a file structure so limits do not change
*/
.init("""
var example: Int {
\(type) Example_0 {
\(type) Example_1 {}
}
return 5
}
"""),
// didSet is not present in file structure although there is such a swift declaration kind
.init("""
var example: Int = 5 {
didSet {
\(type) Example_0 {
\(type) Example_1 {}
}
}
}
"""),
// extensions are counted as a type level
.init("""
extension Example_0 {
\(type) Example_1 {}
}
""")
]
}
private static let nonTriggeringFunctionExamples: [Example] = [
// default maximum function nesting level
.init("""
func f_0() {
func f_1() {
func f_2() {}
}
}
"""),
/*
all variableKinds of SwiftDeclarationKind (except .varParameter which is a function parameter)
are flattend in a file structure so level limits do not change
*/
.init("""
var example: Int {
func f_0() {
func f_1() {
func f_2() {}
}
}
return 5
}
"""),
// didSet is not present in file structure although there is such a swift declaration kind
.init("""
var example: Int = 5 {
didSet {
func f_0() {
func f_1() {
func f_2() {}
}
}
}
}
"""),
// extensions are counted as a type level
.init("""
extension Example_0 {
func f_0() {
func f_1() {
func f_2() {}
}
}
}
""")
]
private static let nonTriggeringClosureAndStatementExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// swich statement example
.init("""
switch example {
case .exampleCase:
\(type) Example_0 {
\(type) Example_1 {}
}
default:
func f_0() {
func f_1() {
func f_2() {}
}
}
}
"""),
// closure var example
.init("""
var exampleClosure: () -> Void = {
\(type) Example_0 {
\(type) Example_1 {}
}
func f_0() {
func f_1() {
func f_2() {}
}
}
}
"""),
// function closure parameter example
.init("""
exampleFunc(closure: {
\(type) Example_0 {
\(type) Example_1 {}
}
func f_0() {
func f_1() {
func f_2() {}
}
}
})
""")
]
}
private static let nonTriggeringMixedExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// default maximum nesting level for both type and function (nesting order is arbitrary)
.init("""
\(type) Example_0 {
func f_0() {
\(type) Example_1 {
func f_1() {
func f_2() {}
}
}
}
}
"""),
// default maximum nesting level for both type and function within closures and statements
.init("""
\(type) Example_0 {
func f_0() {
switch example {
case .exampleCase:
\(type) Example_1 {
func f_1() {
func f_2() {}
}
}
default:
exampleFunc(closure: {
\(type) Example_1 {
func f_1() {
func f_2() {}
}
}
})
}
}
}
""")
]
}
static let triggeringExamples = triggeringTypeExamples
+ triggeringFunctionExamples
+ triggeringClosureAndStatementExamples
+ triggeringMixedExamples
private static let triggeringTypeExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// violation of default maximum type nesting level
.init("""
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
"""),
/*
all variableKinds of SwiftDeclarationKind (except .varParameter which is a function parameter)
are flattend in a file structure so limits do not change
*/
.init("""
var example: Int {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
return 5
}
"""),
// didSet is not present in file structure although there is such a swift declaration kind
.init("""
var example: Int = 5 {
didSet {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
}
}
"""),
// extensions are counted as a type level, violation of default maximum type nesting level
.init("""
extension Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
""")
]
}
private static let triggeringFunctionExamples: [Example] = [
// violation of default maximum function nesting level
.init("""
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
"""),
/*
all variableKinds of SwiftDeclarationKind (except .varParameter which is a function parameter)
are flattend in a file structure so level limits do not change
*/
.init("""
var example: Int {
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
return 5
}
"""),
// didSet is not present in file structure although there is such a swift declaration kind
.init("""
var example: Int = 5 {
didSet {
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
}
}
"""),
// extensions are counted as a type level, violation of default maximum function nesting level
.init("""
extension Example_0 {
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
}
""")
]
private static let triggeringClosureAndStatementExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// swich statement example
.init("""
switch example {
case .exampleCase:
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
default:
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
}
"""),
// closure var example
.init("""
var exampleClosure: () -> Void = {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
}
"""),
// function closure parameter example
.init("""
exampleFunc(closure: {
\(type) Example_0 {
\(type) Example_1 {}
}
func f_0() {
func f_1() {
func f_2() {
↓func f_3() {}
}
}
}
})
""")
]
}
private static let triggeringMixedExamples =
["class", "struct", "enum"].flatMap { type -> [Example] in
[
// violation of default maximum nesting level for both type and function (nesting order is arbitrary)
.init("""
\(type) Example_0 {
func f_0() {
\(type) Example_1 {
func f_1() {
func f_2() {
\(type) Example_2 {}
↓func f_3() {}
}
}
}
}
}
"""),
// violation of default maximum nesting level for both type and function within closures and statements
.init("""
\(type) Example_0 {
func f_0() {
switch example {
case .exampleCase:
\(type) Example_1 {
func f_1() {
func f_2() {
\(type) Example_2 {}
↓func f_3() {}
}
}
}
default:
exampleFunc(closure: {
\(type) Example_1 {
func f_1() {
func f_2() {
\(type) Example_2 {}
↓func f_3() {}
}
}
}
})
}
}
}
""")
]
}
}
@@ -1,18 +1,26 @@
public struct NestingConfiguration: RuleConfiguration, Equatable {
public var consoleDescription: String {
return "(type_level) \(typeLevel.shortConsoleDescription), " +
"(statement_level) \(statementLevel.shortConsoleDescription)"
return "(type_level) \(typeLevel.shortConsoleDescription)"
+ ", (function_level) \(functionLevel.shortConsoleDescription)"
+ ", (check_nesting_in_closures_and_statements) \(checkNestingInClosuresAndStatements)"
+ ", (always_allow_one_type_in_functions) \(alwaysAllowOneTypeInFunctions)"
}
var typeLevel: SeverityLevelsConfiguration
var statementLevel: SeverityLevelsConfiguration
var functionLevel: SeverityLevelsConfiguration
var checkNestingInClosuresAndStatements: Bool
var alwaysAllowOneTypeInFunctions: Bool
public init(typeLevelWarning: Int,
typeLevelError: Int?,
statementLevelWarning: Int,
statementLevelError: Int?) {
typeLevel = SeverityLevelsConfiguration(warning: typeLevelWarning, error: typeLevelError)
statementLevel = SeverityLevelsConfiguration(warning: statementLevelWarning, error: statementLevelError)
functionLevelWarning: Int,
functionLevelError: Int?,
checkNestingInClosuresAndStatements: Bool = true,
alwaysAllowOneTypeInFunctions: Bool = false) {
self.typeLevel = SeverityLevelsConfiguration(warning: typeLevelWarning, error: typeLevelError)
self.functionLevel = SeverityLevelsConfiguration(warning: functionLevelWarning, error: functionLevelError)
self.checkNestingInClosuresAndStatements = checkNestingInClosuresAndStatements
self.alwaysAllowOneTypeInFunctions = alwaysAllowOneTypeInFunctions
}
public mutating func apply(configuration: Any) throws {
@@ -23,9 +31,12 @@ public struct NestingConfiguration: RuleConfiguration, Equatable {
if let typeLevelConfiguration = configurationDict["type_level"] {
try typeLevel.apply(configuration: typeLevelConfiguration)
}
if let statementLevelConfiguration = configurationDict["statement_level"] {
try statementLevel.apply(configuration: statementLevelConfiguration)
if let functionLevelConfiguration = configurationDict["function_level"] {
try functionLevel.apply(configuration: functionLevelConfiguration)
}
// swiftlint:disable:next line_length
checkNestingInClosuresAndStatements = configurationDict["check_nesting_in_closures_and_statements"] as? Bool ?? true
alwaysAllowOneTypeInFunctions = configurationDict["always_allow_one_type_in_functions"] as? Bool ?? false
}
func severity(with config: SeverityLevelsConfiguration, for level: Int) -> ViolationSeverity? {
+3 -1
View File
@@ -1011,7 +1011,9 @@ extension NSObjectPreferIsEqualRuleTests {
extension NestingRuleTests {
static var allTests: [(String, (NestingRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
("testNestingWithDefaultConfiguration", testNestingWithDefaultConfiguration),
("testNestingWithAlwaysAllowOneTypeInFunctions", testNestingWithAlwaysAllowOneTypeInFunctions),
("testNestingWithoutCheckNestingInClosuresAndStatements", testNestingWithoutCheckNestingInClosuresAndStatements)
]
}
@@ -432,12 +432,6 @@ class NSObjectPreferIsEqualRuleTests: XCTestCase {
}
}
class NestingRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(NestingRule.description)
}
}
class NimbleOperatorRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(NimbleOperatorRule.description)
@@ -1,8 +1,6 @@
@testable import SwiftLintFramework
import XCTest
// swiftlint:disable nesting
class CollectingRuleTests: XCTestCase {
func testCollectsIntoStorage() {
struct Spec: MockCollectingRule {
@@ -0,0 +1,377 @@
import SwiftLintFramework
import XCTest
// swiftlint:disable:next type_body_length
class NestingRuleTests: XCTestCase {
func testNestingWithDefaultConfiguration() {
verifyRule(NestingRule.description)
}
// swiftlint:disable:next function_body_length
func testNestingWithAlwaysAllowOneTypeInFunctions() {
var nonTriggeringExamples = NestingRule.description.nonTriggeringExamples
nonTriggeringExamples.append(contentsOf: ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {}
}
}
}
"""),
.init("""
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {
func f_1() {
\(type) Example_3 {}
}
}
}
}
}
"""),
.init("""
func f_0() {
\(type) Example_0 {
\(type) Example_1 {}
}
}
""")
]
})
nonTriggeringExamples.append(contentsOf: ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
exampleFunc(closure: {
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {}
}
}
}
func f_0() {
\(type) Example_0 {
func f_1() {
\(type) Example_1 {
func f_2() {
\(type) Example_2 {}
}
}
}
}
}
})
"""),
.init("""
switch example {
case .exampleCase:
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {}
}
}
}
default:
func f_0() {
\(type) Example_0 {
func f_1() {
\(type) Example_1 {
func f_2() {
\(type) Example_2 {}
}
}
}
}
}
}
""")
]
})
var triggeringExamples = ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {
\(type) Example_3 {}
}
}
}
}
"""),
.init("""
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {
func f_1() {
\(type) Example_3 {
\(type) Example_4 {}
}
}
}
}
}
}
"""),
.init("""
func f_0() {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
}
""")
]
}
triggeringExamples.append(contentsOf: ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
exampleFunc(closure: {
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {
\(type) Example_3 {}
}
}
}
}
func f_0() {
\(type) Example_0 {
func f_1() {
\(type) Example_1 {
func f_2() {
\(type) Example_2 {
\(type) Example_3 {}
}
}
}
}
}
}
})
"""),
.init("""
switch example {
case .exampleCase:
\(type) Example_0 {
\(type) Example_1 {
func f_0() {
\(type) Example_2 {
\(type) Example_3 {}
}
}
}
}
default:
func f_0() {
\(type) Example_0 {
func f_1() {
\(type) Example_1 {
func f_2() {
\(type) Example_2 {
\(type) Example_3 {}
}
}
}
}
}
}
}
""")
]
})
let description = RuleDescription(
identifier: NestingRule.description.identifier,
name: NestingRule.description.name,
description: NestingRule.description.description,
kind: .metrics,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: triggeringExamples
)
verifyRule(description, ruleConfiguration: ["always_allow_one_type_in_functions": true])
}
// swiftlint:disable:next function_body_length
func testNestingWithoutCheckNestingInClosuresAndStatements() {
var nonTriggeringExamples = NestingRule.description.nonTriggeringExamples
nonTriggeringExamples.append(contentsOf: ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
exampleFunc(closure: {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
})
"""),
.init("""
switch example {
case .exampleCase:
\(type) Example_0 {
\(type) Example_1 {
\(type) Example 2 {}
}
}
default:
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
}
""")
]
})
var triggeringExamples = ["class", "struct", "enum"].flatMap { type -> [Example] in
[
.init("""
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
"""),
.init("""
var example: Int {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
return 5
}
"""),
.init("""
var example: Int = 5 {
didSet {
\(type) Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
}
}
"""),
.init("""
extension Example_0 {
\(type) Example_1 {
\(type) Example_2 {}
}
}
"""),
.init("""
\(type) Example_0 {
func f_0() {
\(type) Example_1 {
func f_1() {
func f_2() {
\(type) Example_2 {}
func f_3() {}
}
}
}
}
}
""")
]
}
triggeringExamples.append(contentsOf: [
.init("""
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
"""),
.init("""
var example: Int {
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
return 5
}
"""),
.init("""
var example: Int = 5 {
didSet {
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
}
}
"""),
.init("""
extension Example_0 {
func f_0() {
func f_1() {
func f_2() {
func f_3() {}
}
}
}
}
""")
])
let description = RuleDescription(
identifier: NestingRule.description.identifier,
name: NestingRule.description.name,
description: NestingRule.description.description,
kind: .metrics,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: triggeringExamples
)
verifyRule(description, ruleConfiguration: ["check_nesting_in_closures_and_statements": false])
}
}
@@ -70,20 +70,24 @@ class RuleConfigurationTests: XCTestCase {
"type_level": [
"warning": 7, "error": 17
],
"statement_level": [
"function_level": [
"warning": 8, "error": 18
]
],
"check_nesting_in_closures_and_statements": false,
"always_allow_one_type_in_functions": true
] as [String: Any]
var nestingConfig = NestingConfiguration(typeLevelWarning: 0,
typeLevelError: nil,
statementLevelWarning: 0,
statementLevelError: nil)
functionLevelWarning: 0,
functionLevelError: nil)
do {
try nestingConfig.apply(configuration: config)
XCTAssertEqual(nestingConfig.typeLevel.warning, 7)
XCTAssertEqual(nestingConfig.statementLevel.warning, 8)
XCTAssertEqual(nestingConfig.functionLevel.warning, 8)
XCTAssertEqual(nestingConfig.typeLevel.error, 17)
XCTAssertEqual(nestingConfig.statementLevel.error, 18)
XCTAssertEqual(nestingConfig.functionLevel.error, 18)
XCTAssert(nestingConfig.alwaysAllowOneTypeInFunctions)
XCTAssert(!nestingConfig.checkNestingInClosuresAndStatements)
} catch {
XCTFail("Failed to configure nested configurations")
}
@@ -93,8 +97,8 @@ class RuleConfigurationTests: XCTestCase {
let config = 17
var nestingConfig = NestingConfiguration(typeLevelWarning: 0,
typeLevelError: nil,
statementLevelWarning: 0,
statementLevelError: nil)
functionLevelWarning: 0,
functionLevelError: nil)
checkError(ConfigurationError.unknownConfiguration) {
try nestingConfig.apply(configuration: config)
}