added fileprivateRule

This commit is contained in:
J Cheyo Jimenez
2017-06-03 18:16:56 -07:00
committed by Marcelo Fabri
parent 85cdcbeb5b
commit e72d09bc31
10 changed files with 173 additions and 5 deletions
@@ -35,6 +35,7 @@ public let masterRuleList = RuleList(rules: [
FatalErrorMessageRule.self,
FileHeaderRule.self,
FileLengthRule.self,
FileprivateRule.self,
FirstWhereRule.self,
ForWhereRule.self,
ForceCastRule.self,
@@ -0,0 +1,53 @@
//
// FileprivateRule.swift
// SwiftLint
//
// Created by Jose Cheyo Jimenez on 05/02/17.
// Copyright © 2017 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct FileprivateRule: Rule, ConfigurationProviderRule {
public var configuration = FileprivateConfiguration(strict: false)
public init() {}
public static let description = FileprivateConfiguration.fileprivateLimited
public func validate(file: File) -> [StyleViolation] {
if !configuration.strict {
let toplevel = file.structure.dictionary.substructure.flatMap({ $0.offset })
let syntaxTokens = file.syntaxMap.tokens
let violationOffsets = toplevel.flatMap { (offSet) -> Int? in
let parts = syntaxTokens.partitioned { offSet <= $0.offset }
guard let lastKind = parts.first.last,
lastKind.type == SyntaxKind.attributeBuiltin.rawValue,
// Cut the amount of name-look-ups by first checking the char count
lastKind.length == "fileprivate".bridge().length,
// Get the actual name of the attibute
let aclName = file.contents.bridge()
.substringWithByteRange(start:lastKind.offset, length: lastKind.length),
// fileprivate(set) is not possible at toplevel
aclName == "fileprivate"
else { return nil }
return offSet
}
return violationOffsets.map({ StyleViolation(
ruleDescription: FileprivateConfiguration.fileprivateLimited,
location: Location(file: file, byteOffset: $0))
})
} else { // Mark all fileprivate occurences as a violation
let fileprivates = file.match(pattern: "fileprivate", with: [.attributeBuiltin]).flatMap({
file.contents.bridge().NSRangeToByteRange(start: $0.location, length: $0.length)
}).map({ $0.location })
return fileprivates.map({ StyleViolation(
ruleDescription: FileprivateConfiguration.fileprivateDisallowed,
location: Location(file: file, byteOffset: $0))
})
}
}
}
@@ -116,7 +116,7 @@ public struct IdentifierNameRule: ASTRule, ConfigurationProviderRule {
}
}
fileprivate extension String {
private extension String {
var isViolatingCase: Bool {
let secondIndex = characters.index(after: startIndex)
let firstCharacter = substring(to: secondIndex)
@@ -0,0 +1,90 @@
//
// FileprivateConfiguration.swift
// SwiftLint
//
// Created by Jose Cheyo Jimenez on 05/02/17.
// Copyright © 2017 Realm. All rights reserved.
//
public struct FileprivateConfiguration: RuleConfiguration, Equatable {
private(set) var severityConfiguration = SeverityConfiguration(.warning)
private(set) var strict: Bool
public var consoleDescription: String {
return severityConfiguration.consoleDescription + ", strict: \(strict)"
}
public init(strict: Bool) {
self.strict = strict
}
public mutating func apply(configuration: Any) throws {
guard let configuration = configuration as? [String: Any] else {
throw ConfigurationError.unknownConfiguration
}
if let strict = configuration["strict"] as? Bool {
self.strict = strict
}
if let severityString = configuration["severity"] as? String {
try severityConfiguration.apply(configuration: severityString)
}
}
public static let fileprivateLimited = RuleDescription(
identifier: "fileprivate",
name: "Limit Fileprivate",
description: "Prefer private over fileprivate for top-level declarations",
nonTriggeringExamples: [
"extension String {}",
"private extension String {}",
"public \n enum MyEnum {}",
"open extension \n String {}",
"internal extension String {}",
"extension String {\nfileprivate func Something(){}\n}",
"class MyClass {\nfileprivate let myInt = 4\n}",
"class MyClass {\nfileprivate(set) var myInt = 4\n}",
"struct Outter {\nstruct Inter {\nfileprivate struct Inner {}\n}\n}"
],
triggeringExamples: [
"fileprivate enum MyEnum {}",
"fileprivate extension String {}",
"fileprivate \n extension String {}",
"fileprivate extension \n String {}",
"fileprivate class MyClass {\nfileprivate(set) var myInt = 4\n}",
"fileprivate extension String {}"
]
)
public static let fileprivateDisallowed = RuleDescription(
identifier: "fileprivate",
name: "Fileprivate Disallowed",
description: "Fileprivate should be rare. Consider refactoring",
nonTriggeringExamples: [
"extension String {}",
"private extension String {}",
"public \n extension String {}",
"open extension \n String {}",
"internal extension String {}",
""
],
triggeringExamples: [
"fileprivate extension String {}",
"fileprivate extension String {}",
"fileprivate \n extension String {}",
"fileprivate extension \n String {}",
"fileprivate extension String {}",
"extension String {\nfileprivate func Something(){}\n}",
"class MyClass {\nfileprivate let myInt = 4\n}",
"class MyClass {\nfileprivate(set) var myInt = 4\n}",
"struct Outter {\nstruct Inter {\nfileprivate struct Inner {}\n}\n}"
]
)
public static func == (lhs: FileprivateConfiguration,
rhs: FileprivateConfiguration) -> Bool {
return lhs.strict == rhs.strict &&
lhs.severityConfiguration == rhs.severityConfiguration
}
}