Compare commits

...

5 Commits

Author SHA1 Message Date
JP Simard 8881a8d3da
improve recursiveDeclaredTypeNames() 2017-05-24 16:53:55 -07:00
JP Simard 0402ae7fab
add missing 'import Foundation' 2017-05-24 16:50:29 -07:00
JP Simard fa3d48a97e
match exact type name, not just string contains 2017-05-24 16:39:47 -07:00
JP Simard d37ff39613
allow protocol names to appear in file names 2017-05-24 16:29:06 -07:00
JP Simard 17256e4fcf
add `file_name` opt-in rule
validating that file names contain the name of a
type or extension declared in the file (if any).
2017-05-24 15:50:54 -07:00
4 changed files with 97 additions and 0 deletions

View File

@ -101,6 +101,11 @@
* Make `closure_spacing` a `CorrectableRule`.
[J. Cheyo Jimenez](https://github.com/masters3d)
* Add `file_name` opt-in rule validating that file names contain the name of a
type or extension declared in the file (if any).
[JP Simard](https://github.com/jpsim)
[#1420](https://github.com/realm/SwiftLint/issues/1420)
##### Bug Fixes
* `emoji` and `checkstyle` reporter output report sorted by file name.

View File

@ -99,6 +99,7 @@ public let masterRuleList = RuleList(rules:
FatalErrorMessageRule.self,
FileHeaderRule.self,
FileLengthRule.self,
FileNameRule.self,
FirstWhereRule.self,
ForceCastRule.self,
ForceTryRule.self,

View File

@ -0,0 +1,87 @@
//
// FileNameRule.swift
// SwiftLint
//
// Created by JP Simard on 5/24/17.
// Copyright © 2017 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
private let typeAndExtensionKinds = SwiftDeclarationKind.typeKinds() + [.extension, .protocol]
extension Dictionary where Key: ExpressibleByStringLiteral {
fileprivate func recursiveDeclaredTypeNames() -> [String] {
let subNames = substructure.flatMap { $0.recursiveDeclaredTypeNames() }
if let kind = kind.flatMap(SwiftDeclarationKind.init),
typeAndExtensionKinds.contains(kind), let name = name {
return [name] + subNames
}
return subNames
}
}
public struct FileNameRule: ConfigurationProviderRule, OptInRule {
public var configuration = FileNameConfiguration(severity: .warning, excluded: ["main.swift"])
public init() {}
public static let description = RuleDescription(
identifier: "file_name",
name: "File Name",
description: "File name should match a type declared in the file (if any)."
)
public func validate(file: File) -> [StyleViolation] {
guard let filePath = file.path,
case let fileName = filePath.bridge().lastPathComponent,
!configuration.excluded.contains(fileName) else {
return []
}
let typeInFileName = fileName.components(separatedBy: CharacterSet(charactersIn: "+.")).first ?? fileName
let allDeclaredTypeNames = file.structure.dictionary.recursiveDeclaredTypeNames()
guard !allDeclaredTypeNames.isEmpty, !allDeclaredTypeNames.contains(typeInFileName) else {
return []
}
return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity.severity,
location: Location(file: filePath, line: 1))]
}
}
public struct FileNameConfiguration: RuleConfiguration, Equatable {
public var consoleDescription: String {
return "(severity) \(severity.consoleDescription), " +
"excluded: \(excluded.sorted())"
}
private(set) public var severity: SeverityConfiguration
private(set) public var excluded: Set<String>
public init(severity: ViolationSeverity, excluded: [String] = []) {
self.severity = SeverityConfiguration(severity)
self.excluded = Set(excluded)
}
public mutating func apply(configuration: Any) throws {
guard let configurationDict = configuration as? [String: Any] else {
throw ConfigurationError.unknownConfiguration
}
if let severityConfiguration = configurationDict["severity"] {
try severity.apply(configuration: severityConfiguration)
}
if let excluded = [String].array(of: configurationDict["excluded"]) {
self.excluded = Set(excluded)
}
}
}
public func == (lhs: FileNameConfiguration, rhs: FileNameConfiguration) -> Bool {
return lhs.severity == rhs.severity &&
lhs.excluded == rhs.excluded
}

View File

@ -187,6 +187,7 @@
E82367E01ED3BD1E0040A88E /* Configuration+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82367DF1ED3BD1E0040A88E /* Configuration+Cache.swift */; };
E832F10B1B17E2F5003F265F /* NSFileManager+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */; };
E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10C1B17E725003F265F /* IntegrationTests.swift */; };
E83530C61ED6328A00FBAF79 /* FileNameRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83530C51ED6328A00FBAF79 /* FileNameRule.swift */; };
E83A0B351A5D382B0041A60A /* VersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83A0B341A5D382B0041A60A /* VersionCommand.swift */; };
E847F0A91BFBBABD00EA9363 /* EmptyCountRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */; };
E84E07471C13F95300F11122 /* AutoCorrectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E07461C13F95300F11122 /* AutoCorrectCommand.swift */; };
@ -482,6 +483,7 @@
E82367DF1ED3BD1E0040A88E /* Configuration+Cache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Configuration+Cache.swift"; sourceTree = "<group>"; };
E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFileManager+SwiftLint.swift"; sourceTree = "<group>"; };
E832F10C1B17E725003F265F /* IntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = "<group>"; };
E83530C51ED6328A00FBAF79 /* FileNameRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileNameRule.swift; sourceTree = "<group>"; };
E83A0B341A5D382B0041A60A /* VersionCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionCommand.swift; sourceTree = "<group>"; };
E847F0A81BFBBABD00EA9363 /* EmptyCountRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyCountRule.swift; sourceTree = "<group>"; };
E84E07461C13F95300F11122 /* AutoCorrectCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutoCorrectCommand.swift; sourceTree = "<group>"; };
@ -875,6 +877,7 @@
C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */,
D4C4A34D1DEA877200E0E04C /* FileHeaderRule.swift */,
E88DEA891B0992B300A66CB0 /* FileLengthRule.swift */,
E83530C51ED6328A00FBAF79 /* FileNameRule.swift */,
D42D2B371E09CC0D00CD7A2E /* FirstWhereRule.swift */,
E88DEA7F1B09903300A66CB0 /* ForceCastRule.swift */,
E816194D1BFBFEAB00946723 /* ForceTryRule.swift */,
@ -1351,6 +1354,7 @@
E80746F61ECB722F00548D31 /* CacheDescriptionProvider.swift in Sources */,
094385041D5D4F7C009168CF /* PrivateOutletRule.swift in Sources */,
E88DEA6B1B0983FE00A66CB0 /* StyleViolation.swift in Sources */,
E83530C61ED6328A00FBAF79 /* FileNameRule.swift in Sources */,
3BB47D831C514E8100AE6A10 /* RegexConfiguration.swift in Sources */,
D4C889711E385B7B00BAE88D /* RedundantDiscardableLetRule.swift in Sources */,
D4D1B9BB1EAC2C910028BE6A /* AccessControlLevel.swift in Sources */,