Add options to generic_type_name and type_name rules

This commit is contained in:
Javier Hernández
2017-05-01 00:22:59 +01:00
parent 9d31eaccda
commit d652cfe4ab
9 changed files with 172 additions and 20 deletions
+3 -2
View File
@@ -13,8 +13,9 @@
##### Enhancements
* Add opt-in options to `identifier_name` rule to exclude non-alphanumeric
characters and to allow names that start with uppercase.
* Add opt-in configurations to `generic_type_name`, `identifier_name` and
`type_name` rules to allow excluding non-alphanumeric characters and names
that start with uppercase.
[Javier Hernández](https://github.com/jaherhi)
[#541](https://github.com/realm/SwiftLint/issues/541)
@@ -166,14 +166,16 @@ public struct GenericTypeNameRule: ASTRule, ConfigurationProviderRule {
return []
}
if !CharacterSet.alphanumerics.isSuperset(ofCharactersIn: name) {
let containsAllowedSymbol = configuration.allowedSymbols.first(where: { name.contains($0) }) != nil
if !containsAllowedSymbol && !CharacterSet.alphanumerics.isSuperset(ofCharactersIn: name) {
return [
StyleViolation(ruleDescription: type(of: self).description,
severity: .error,
location: Location(file: file, byteOffset: offset),
reason: "Generic type name should only contain alphanumeric characters: '\(name)'")
]
} else if !name.substring(to: name.index(after: name.startIndex)).isUppercase() {
} else if configuration.validatesStartWithLowercase &&
!name.substring(to: name.index(after: name.startIndex)).isUppercase() {
return [
StyleViolation(ruleDescription: type(of: self).description,
severity: .error,
@@ -74,12 +74,14 @@ public struct TypeNameRule: ASTRule, ConfigurationProviderRule {
}
let name = name.nameStrippingLeadingUnderscoreIfPrivate(dictionary)
if !CharacterSet.alphanumerics.isSuperset(ofCharactersIn: name) {
let containsAllowedSymbol = configuration.allowedSymbols.first(where: { name.contains($0) }) != nil
if !containsAllowedSymbol && !CharacterSet.alphanumerics.isSuperset(ofCharactersIn: name) {
return [StyleViolation(ruleDescription: type(of: self).description,
severity: .error,
location: Location(file: file, byteOffset: offset),
reason: "Type name should only contain alphanumeric characters: '\(name)'")]
} else if !name.substring(to: name.index(after: name.startIndex)).isUppercase() {
} else if configuration.validatesStartWithLowercase &&
!name.substring(to: name.index(after: name.startIndex)).isUppercase() {
return [StyleViolation(ruleDescription: type(of: self).description,
severity: .error,
location: Location(file: file, byteOffset: offset),
+8
View File
@@ -33,6 +33,8 @@
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 */; };
3B20CD0A1EB699380069EF2E /* GenericTypeNameRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B20CD091EB699380069EF2E /* GenericTypeNameRuleTests.swift */; };
3B20CD0C1EB699C20069EF2E /* TypeNameRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B20CD0B1EB699C20069EF2E /* TypeNameRuleTests.swift */; };
3B30C4A11C3785B300E04027 /* YamlParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B30C4A01C3785B300E04027 /* YamlParserTests.swift */; };
3B3A9A331EA3DFD90075B121 /* IdentifierNameRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A9A321EA3DFD90075B121 /* IdentifierNameRuleTests.swift */; };
3B5B9FE11C444DA20009AD27 /* Array+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5B9FE01C444DA20009AD27 /* Array+SwiftLint.swift */; };
@@ -301,6 +303,8 @@
3B12C9C41C322032000B423F /* MasterRuleList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterRuleList.swift; sourceTree = "<group>"; };
3B12C9C61C3361CB000B423F /* RuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuleTests.swift; sourceTree = "<group>"; };
3B1DF0111C5148140011BCED /* CustomRules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomRules.swift; sourceTree = "<group>"; };
3B20CD091EB699380069EF2E /* GenericTypeNameRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenericTypeNameRuleTests.swift; sourceTree = "<group>"; };
3B20CD0B1EB699C20069EF2E /* TypeNameRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeNameRuleTests.swift; sourceTree = "<group>"; };
3B30C4A01C3785B300E04027 /* YamlParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YamlParserTests.swift; sourceTree = "<group>"; };
3B3A9A321EA3DFD90075B121 /* IdentifierNameRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifierNameRuleTests.swift; sourceTree = "<group>"; };
3B5B9FE01C444DA20009AD27 /* Array+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+SwiftLint.swift"; sourceTree = "<group>"; };
@@ -759,6 +763,7 @@
02FD8AEE1BFC18D60014BFFB /* ExtendedNSStringTests.swift */,
D4998DE81DF194F20006E05D /* FileHeaderRuleTests.swift */,
D4348EE91C46122C007707FB /* FunctionBodyLengthRuleTests.swift */,
3B20CD091EB699380069EF2E /* GenericTypeNameRuleTests.swift */,
3B3A9A321EA3DFD90075B121 /* IdentifierNameRuleTests.swift */,
47ACC8991E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift */,
47ACC89B1E7DCFA00088EEB2 /* ImplicitlyUnwrappedOptionalRuleTests.swift */,
@@ -775,6 +780,7 @@
E81224991B04F85B001783D2 /* TestHelpers.swift */,
D4DB92241E628898005DE9C1 /* TodoRuleTests.swift */,
C9802F2E1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift */,
3B20CD0B1EB699C20069EF2E /* TypeNameRuleTests.swift */,
D4470D5A1EB76F44008A1B2E /* UnusedOptionalBindingRuleTests.swift */,
006204DD1E1E4E0A00FFFBE1 /* VerticalWhitespaceRuleTests.swift */,
3B12C9C21C320A53000B423F /* Yaml+SwiftLintTests.swift */,
@@ -1368,12 +1374,14 @@
6C7045441C6ADA450003F15A /* SourceKitCrashTests.swift in Sources */,
3BB47D871C51DE6E00AE6A10 /* CustomRulesTests.swift in Sources */,
E812249A1B04F85B001783D2 /* TestHelpers.swift in Sources */,
3B20CD0C1EB699C20069EF2E /* TypeNameRuleTests.swift in Sources */,
3B3A9A331EA3DFD90075B121 /* IdentifierNameRuleTests.swift in Sources */,
E86396C71BADAFE6002C9E88 /* ReporterTests.swift in Sources */,
D43B04661E071ED3004016AF /* ColonRuleTests.swift in Sources */,
3B12C9C71C3361CB000B423F /* RuleTests.swift in Sources */,
67EB4DFC1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift in Sources */,
3B30C4A11C3785B300E04027 /* YamlParserTests.swift in Sources */,
3B20CD0A1EB699380069EF2E /* GenericTypeNameRuleTests.swift in Sources */,
D4998DE71DF191380006E05D /* AttributesRuleTests.swift in Sources */,
E88198631BEA9A5400333A11 /* RulesTests.swift in Sources */,
47ACC89A1E7DCCAD0088EEB2 /* ImplicitlyUnwrappedOptionalConfigurationTests.swift in Sources */,
+2
View File
@@ -19,6 +19,7 @@ XCTMain([
testCase(ExtendedNSStringTests.allTests),
testCase(FileHeaderRuleTests.allTests),
testCase(FunctionBodyLengthRuleTests.allTests),
testCase(GenericTypeNameRuleTests.allTests),
testCase(IdentifierNameRuleTests.allTests),
testCase(ImplicitlyUnwrappedOptionalConfigurationTests.allTests),
testCase(ImplicitlyUnwrappedOptionalRuleTests.allTests),
@@ -33,6 +34,7 @@ XCTMain([
testCase(RuleTests.allTests),
testCase(SourceKitCrashTests.allTests),
testCase(TrailingCommaRuleTests.allTests),
testCase(TypeNameRuleTests.allTests),
testCase(TodoRuleTests.allTests),
testCase(UnusedOptionalBindingRuleTests.allTests),
testCase(VerticalWhitespaceRuleTests.allTests),
@@ -0,0 +1,73 @@
//
// GenericTypeNameRuleTests.swift
// SwiftLint
//
// Created by Javier Hernandez on 30/04/17.
// Copyright © 2017 Realm. All rights reserved.
//
import SwiftLintFramework
import XCTest
class GenericTypeNameRuleTests: XCTestCase {
func testGenericTypeName() {
verifyRule(GenericTypeNameRule.description)
}
func testGenericTypeNameWithAllowedSymbols() {
let baseDescription = GenericTypeNameRule.description
let nonTriggeringExamples = baseDescription.nonTriggeringExamples + [
"func foo<T$>() {}\n",
"func foo<T$, U%>(param: U%) -> T$ {}\n",
"typealias StringDictionary<T$> = Dictionary<String, T$>\n",
"class Foo<T$%> {}\n",
"struct Foo<T$%> {}\n",
"enum Foo<T$%> {}\n"
]
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
description: baseDescription.description,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: baseDescription.triggeringExamples,
corrections: baseDescription.corrections,
deprecatedAliases: baseDescription.deprecatedAliases)
verifyRule(description, ruleConfiguration: ["allowed_symbols": ["$", "%"]])
}
func testGenericTypeNameWithIgnoreStartWithLowercase() {
let baseDescription = GenericTypeNameRule.description
let triggeringExamplesToRemove = [
"func foo<↓type>() {}\n",
"class Foo<↓type> {}\n",
"struct Foo<↓type> {}\n",
"enum Foo<↓type> {}\n"
]
let nonTriggeringExamples = baseDescription.nonTriggeringExamples +
triggeringExamplesToRemove.map { $0.replacingOccurrences(of: "", with: "") }
let triggeringExamples = baseDescription.triggeringExamples
.filter { !triggeringExamplesToRemove.contains($0) }
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
description: baseDescription.description,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: triggeringExamples,
corrections: baseDescription.corrections,
deprecatedAliases: baseDescription.deprecatedAliases)
verifyRule(description, ruleConfiguration: ["validates_start_lowercase": false])
}
}
extension GenericTypeNameRuleTests {
static var allTests: [(String, (GenericTypeNameRuleTests) -> () throws -> Void)] {
return [
("testGenericTypeName", testGenericTypeName),
("testGenericTypeNameWithAllowedSymbols", testGenericTypeNameWithAllowedSymbols),
("testGenericTypeNameWithIgnoreStartWithLowercase", testGenericTypeNameWithIgnoreStartWithLowercase)
]
}
}
@@ -36,12 +36,14 @@ class IdentifierNameRuleTests: XCTestCase {
func testIdentifierNameWithIgnoreStartWithLowercase() {
let baseDescription = IdentifierNameRule.description
let nonTriggeringExamples = baseDescription.nonTriggeringExamples + [
"let MyLet = 0",
"enum Foo { case MyEnum }"
let triggeringExamplesToRemove = [
"let MyLet = 0",
"enum Foo { case MyEnum }"
]
let nonTriggeringExamples = baseDescription.nonTriggeringExamples +
triggeringExamplesToRemove.map { $0.replacingOccurrences(of: "", with: "") }
let triggeringExamples = baseDescription.triggeringExamples
.filter { !$0.contains("MyLet") && !$0.contains("MyEnum") }
.filter { !triggeringExamplesToRemove.contains($0) }
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
@@ -121,10 +121,6 @@ class RulesTests: XCTestCase {
verifyRule(FunctionParameterCountRule.description)
}
func testGenericTypeName() {
verifyRule(GenericTypeNameRule.description)
}
func testImplicitGetter() {
verifyRule(ImplicitGetterRule.description)
}
@@ -314,10 +310,6 @@ class RulesTests: XCTestCase {
verifyRule(TypeBodyLengthRule.description)
}
func testTypeName() {
verifyRule(TypeNameRule.description)
}
func testUnusedClosureParameter() {
verifyRule(UnusedClosureParameterRule.description)
}
@@ -377,7 +369,6 @@ extension RulesTests {
("testForWhere", testForWhere),
("testFunctionBodyLength", testFunctionBodyLength),
("testFunctionParameterCount", testFunctionParameterCount),
("testGenericTypeName", testGenericTypeName),
("testImplicitGetter", testImplicitGetter),
("testImplicitlyUnwrappedOptional", testImplicitlyUnwrappedOptional),
("testImplicitReturn", testImplicitReturn),
@@ -414,7 +405,6 @@ extension RulesTests {
("testTrailingSemicolon", testTrailingSemicolon),
("testTrailingWhitespace", testTrailingWhitespace),
("testTypeBodyLength", testTypeBodyLength),
("testTypeName", testTypeName),
("testUnusedClosureParameter", testUnusedClosureParameter),
("testUnusedEnumerated", testUnusedEnumerated),
("testValidIBInspectable", testValidIBInspectable),
@@ -0,0 +1,72 @@
//
// TypeNameRuleTests.swift
// SwiftLint
//
// Created by Javier Hernandez on 30/04/17.
// Copyright © 2017 Realm. All rights reserved.
//
import SwiftLintFramework
import XCTest
class TypeNameRuleTests: XCTestCase {
func testTypeName() {
verifyRule(TypeNameRule.description)
}
func testTypeNameWithAllowedSymbols() {
let baseDescription = TypeNameRule.description
let nonTriggeringExamples = baseDescription.nonTriggeringExamples + [
"class MyType$ {}",
"struct MyType$ {}",
"enum MyType$ {}",
"typealias Foo$ = Void",
"protocol Foo {\n associatedtype Bar$\n }"
]
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
description: baseDescription.description,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: baseDescription.triggeringExamples,
corrections: baseDescription.corrections,
deprecatedAliases: baseDescription.deprecatedAliases)
verifyRule(description, ruleConfiguration: ["allowed_symbols": ["$"]])
}
func testTypeNameWithIgnoreStartWithLowercase() {
let baseDescription = TypeNameRule.description
let triggeringExamplesToRemove = [
"private typealias ↓foo = Void",
"↓class myType {}",
"↓struct myType {}",
"↓enum myType {}"
]
let nonTriggeringExamples = baseDescription.nonTriggeringExamples +
triggeringExamplesToRemove.map { $0.replacingOccurrences(of: "", with: "") }
let triggeringExamples = baseDescription.triggeringExamples
.filter { !triggeringExamplesToRemove.contains($0) }
let description = RuleDescription(identifier: baseDescription.identifier,
name: baseDescription.name,
description: baseDescription.description,
nonTriggeringExamples: nonTriggeringExamples,
triggeringExamples: triggeringExamples,
corrections: baseDescription.corrections,
deprecatedAliases: baseDescription.deprecatedAliases)
verifyRule(description, ruleConfiguration: ["validates_start_lowercase": false])
}
}
extension TypeNameRuleTests {
static var allTests: [(String, (TypeNameRuleTests) -> () throws -> Void)] {
return [
("testTypeName", testTypeName),
("testTypeNameWithAllowedSymbols", testTypeNameWithAllowedSymbols),
("testTypeNameWithIgnoreStartWithLowercase", testTypeNameWithIgnoreStartWithLowercase)
]
}
}