added variable name validation

This commit is contained in:
JP Simard
2015-05-17 11:02:08 +02:00
parent 3dc66c429c
commit 036087ada3
3 changed files with 109 additions and 23 deletions
+72 -20
View File
@@ -234,30 +234,82 @@ extension File {
// swiftlint:enable_rule:force_cast
var violations = self.astViolationsInDictionary(subDict)
if let kindString = subDict["key.kind"] as? String,
let kind = flatMap(kindString, { SwiftDeclarationKind(rawValue: $0) })
where contains([.Class, .Struct, .Typealias, .Enum, .Enumelement], kind),
let name = subDict["key.name"] as? String,
let offset = flatMap(subDict["key.offset"] as? Int64, { Int($0) }) {
let location = Location(file: self, offset: offset)
let nameCharacterSet = NSCharacterSet(charactersInString: name)
if !NSCharacterSet.alphanumericCharacterSet().isSupersetOfSet(nameCharacterSet) {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should only contain alphanumeric characters: '\(name)'"))
} else if !name.substringToIndex(name.startIndex.successor()).isUppercase() {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should start with an uppercase character: '\(name)'"))
} else if count(name) < 3 || count(name) > 40 {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should be between 3 and 40 characters in length: " +
"'\(name)'"))
}
let kind = flatMap(kindString, { SwiftDeclarationKind(rawValue: $0) }) {
violations.extend(self.validateTypeName(kind, dict: subDict))
violations.extend(self.validateVariableName(kind, dict: subDict))
}
return violations
}, [], +)
}
func validateTypeName(kind: SwiftDeclarationKind, dict: XPCDictionary) -> [StyleViolation] {
let typeKinds: [SwiftDeclarationKind] = [
.Class,
.Struct,
.Typealias,
.Enum,
.Enumelement
]
if !contains(typeKinds, kind) {
return []
}
var violations = [StyleViolation]()
if let name = dict["key.name"] as? String,
let offset = flatMap(dict["key.offset"] as? Int64, { Int($0) }) {
let location = Location(file: self, offset: offset)
let nameCharacterSet = NSCharacterSet(charactersInString: name)
if !NSCharacterSet.alphanumericCharacterSet().isSupersetOfSet(nameCharacterSet) {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should only contain alphanumeric characters: '\(name)'"))
} else if !name.substringToIndex(name.startIndex.successor()).isUppercase() {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should start with an uppercase character: '\(name)'"))
} else if count(name) < 3 || count(name) > 40 {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Type name should be between 3 and 40 characters in length: " +
"'\(name)'"))
}
}
return violations
}
func validateVariableName(kind: SwiftDeclarationKind, dict: XPCDictionary) -> [StyleViolation] {
let variableKinds: [SwiftDeclarationKind] = [
.VarClass,
.VarGlobal,
.VarInstance,
.VarLocal,
.VarParameter,
.VarStatic
]
if !contains(variableKinds, kind) {
return []
}
var violations = [StyleViolation]()
if let name = dict["key.name"] as? String,
let offset = flatMap(dict["key.offset"] as? Int64, { Int($0) }) {
let location = Location(file: self, offset: offset)
let nameCharacterSet = NSCharacterSet(charactersInString: name)
if !NSCharacterSet.alphanumericCharacterSet().isSupersetOfSet(nameCharacterSet) {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Variable name should only contain alphanumeric characters: '\(name)'"))
} else if name.substringToIndex(name.startIndex.successor()).isUppercase() {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Variable name should start with a lowercase character: '\(name)'"))
} else if count(name) < 3 || count(name) > 40 {
violations.append(StyleViolation(type: .NameFormat,
location: location,
reason: "Variable name should be between 3 and 40 characters in length: " +
"'\(name)'"))
}
}
return violations
}
}
extension String {
@@ -40,7 +40,6 @@ class LinterTests: XCTestCase {
XCTAssertEqual(violations("\(kind) Ab {}\n"), [StyleViolation(type: .NameFormat,
location: Location(file: nil, line: 1),
reason: "Type name should be between 3 and 40 characters in length: 'Ab'")])
XCTAssertEqual(violations("\(kind) Abc {}\n"), [])
let longName = join("", Array(count: 40, repeatedValue: "A"))
XCTAssertEqual(violations("\(kind) \(longName) {}\n"), [])
@@ -80,7 +79,40 @@ class LinterTests: XCTestCase {
}
func testVariableNames() {
// TODO: Variable names should contain between 3 and 20 characters.
for kind in ["class", "struct"] {
for varType in ["var", "let"] {
XCTAssertEqual(violations("\(kind) Abc { \(varType) def: Void }\n"), [])
XCTAssertEqual(violations("\(kind) Abc { \(varType) de_: Void }\n"), [
StyleViolation(type: .NameFormat,
location: Location(file: nil, line: 1),
reason: "Variable name should only contain alphanumeric characters: 'de_'")
])
XCTAssertEqual(violations("\(kind) Abc { \(varType) Def: Void }\n"), [
StyleViolation(type: .NameFormat,
location: Location(file: nil, line: 1),
reason: "Variable name should start with a lowercase character: 'Def'")
])
XCTAssertEqual(violations("\(kind) Abc { \(varType) de: Void }\n"), [
StyleViolation(type: .NameFormat,
location: Location(file: nil, line: 1),
reason: "Variable name should be between 3 and 40 characters in length: " +
"'de'")
])
let longName = join("", Array(count: 40, repeatedValue: "d"))
XCTAssertEqual(violations("\(kind) Abc { \(varType) \(longName): Void }\n"), [])
let longerName = longName + "d"
XCTAssertEqual(violations("\(kind) Abc { \(varType) \(longerName): Void }\n"), [
StyleViolation(type: .NameFormat,
location: Location(file: nil, line: 1),
reason: "Variable name should be between 3 and 40 characters in length: " +
"'\(longerName)'")
])
}
}
}
func testClosureLengths() {
@@ -207,6 +239,7 @@ class LinterTests: XCTestCase {
func testForceCasting() {
XCTAssertEqual(violations("NSNumber() as? Int\n"), [])
XCTAssertEqual(violations("// NSNumber() as! Int\n"), [])
XCTAssertEqual(violations("NSNumber() as! Int\n"),
[StyleViolation(type: .ForceCast,
location: Location(file: nil, line: 1),
+2 -1
View File
@@ -16,7 +16,8 @@ let fileManager = NSFileManager.defaultManager()
struct LintCommand: CommandType {
let verb = "lint"
let function = "Print lint warnings and errors for the Swift files in the current directory (default command)"
let function = "Print lint warnings and errors for the Swift files in the current directory " +
"(default command)"
func run(mode: CommandMode) -> Result<(), CommandantError<()>> {
println("Finding Swift files in current directory...")