Skip to content

Commit

Permalink
Add whitelist only rules
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Beard committed Jan 29, 2016
1 parent 32b325b commit 998bed9
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
included:
- Source
enabled_rules:
opt_in_rules:
- empty_count
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

##### Enhancements

* None.
* Add `whitelist_rules` rule whitelists in config files
[Daniel Beard](https://github.com/daniel-beard)
[#256](https://github.com/realm/SwiftLint/issues/256)

##### Bug Fixes

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,18 @@ identifiers.
Configure SwiftLint by adding a `.swiftlint.yml` file from the directory you'll
run SwiftLint from. The following parameters can be configured:

Rule inclusion:
- `disabled_rules`: Disable rules from the default enabled set.
- `opt_in_rules`: Some rules are opt-in.
- `whitelist_rules`: Can not be specified alongside `disabled_rules` or
`opt_in_rules`. Acts as a whitelist, only the rules specified in this list will be enabled.

```yaml
disabled_rules: # rule identifiers to exclude from running
- colon
- comma
- control_statement
enabled_rules: # some rules are only opt-in
opt_in_rules: # some rules are only opt-in
- empty_count
- missing_docs
# Find all the available rules by running:
Expand Down
40 changes: 34 additions & 6 deletions Source/SwiftLintFramework/Models/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public struct Configuration: Equatable {
}

public init?(disabledRules: [String] = [],
enabledRules: [String] = [],
optInRules: [String] = [],
whitelistRules: [String] = [],
included: [String] = [],
excluded: [String] = [],
reporter: String = "xcode",
Expand Down Expand Up @@ -78,17 +79,44 @@ public struct Configuration: Equatable {
}
self.disabledRules = validDisabledRules

self.rules = rules.filter { rule in
let id = rule.dynamicType.description.identifier
if validDisabledRules.contains(id) { return false }
return enabledRules.contains(id) || !(rule is OptInRule)
// white_list rules take precendence over all else.
if !whitelistRules.isEmpty {

if !disabledRules.isEmpty || !optInRules.isEmpty {
queuedPrintError("'disabled_rules' or 'opt_in_rules' cannot be used in " +
"combination with 'whitelist_rules'")
return nil
}

self.rules = rules.filter { rule in
let id = rule.dynamicType.description.identifier
return whitelistRules.contains(id)
}
} else {
self.rules = rules.filter { rule in
let id = rule.dynamicType.description.identifier
if validDisabledRules.contains(id) { return false }
return optInRules.contains(id) || !(rule is OptInRule)
}
}
}

public init?(dict: [String: AnyObject]) {

// Deprecation warning for "enabled_rules"
if dict["enabled_rules"] != nil {
queuedPrint("'enabled_rules' has been renamed to 'opt_in_rules' " +
"and will be completely removed in a future release.")
}

// Use either new 'opt_in_rules' or deprecated 'enabled_rules' for now.
let optInRules = dict["opt_in_rules"] as? [String] ??
dict["enabled_rules"] as? [String] ?? []

self.init(
disabledRules: dict["disabled_rules"] as? [String] ?? [],
enabledRules: dict["enabled_rules"] as? [String] ?? [],
optInRules: optInRules,
whitelistRules: dict["whitelist_rules"] as? [String] ?? [],
included: dict["included"] as? [String] ?? [],
excluded: dict["excluded"] as? [String] ?? [],
reporter: dict["reporter"] as? String ?? XcodeReporter.identifier,
Expand Down
30 changes: 30 additions & 0 deletions Source/SwiftLintFrameworkTests/ConfigurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,36 @@ class ConfigurationTests: XCTestCase {
XCTAssertEqual(config.reporterFromString.identifier, "xcode")
}

func testWhitelistRules() {
let whitelist = ["nesting", "todo"]
let config = Configuration(dict: ["whitelist_rules": whitelist])!
let configuredIdentifiers = config.rules.map {
$0.dynamicType.description.identifier
}
XCTAssertEqual(whitelist, configuredIdentifiers)
}

func testOtherRuleConfigurationsAlongsideWhitelistRules() {
let whitelist = ["nesting", "todo"]
let enabledRulesConfigDict = [
"opt_in_rules": ["line_length"],
"whitelist_rules": whitelist
]
let disabledRulesConfigDict = [
"disabled_rules": ["variable_name"],
"whitelist_rules": whitelist
]
let combinedRulesConfigDict = enabledRulesConfigDict.reduce(disabledRulesConfigDict) {
var d = $0; d[$1.0] = $1.1; return d
}
var config = Configuration(dict: enabledRulesConfigDict)
XCTAssertNil(config)
config = Configuration(dict: disabledRulesConfigDict)
XCTAssertNil(config)
config = Configuration(dict: combinedRulesConfigDict)
XCTAssertNil(config)
}

func testDisabledRules() {
let disabledConfig = Configuration(dict: ["disabled_rules": ["nesting", "todo"]])!
XCTAssertEqual(disabledConfig.disabledRules,
Expand Down
18 changes: 6 additions & 12 deletions Source/SwiftLintFrameworkTests/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ func violations(string: String, config: Configuration = Configuration()) -> [Sty
return Linter(file: file, configuration: config).styleViolations
}

private func violations(string: String, _ description: RuleDescription) -> [StyleViolation] {
let disabledRules = allRuleIdentifiers.filter { $0 != description.identifier }
let enabledRules = allRuleIdentifiers.filter { $0 == description.identifier }
let config = Configuration(disabledRules: disabledRules, enabledRules: enabledRules)!
return violations(string, config: config)
}

private func assertCorrection(before: String, expected: String) {
guard let path = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.URLByAppendingPathComponent(NSUUID().UUIDString + ".swift").path else {
Expand Down Expand Up @@ -63,15 +56,16 @@ extension String {
extension XCTestCase {
func verifyRule(ruleDescription: RuleDescription, commentDoesntViolate: Bool = true,
stringDoesntViolate: Bool = true) {
let config = Configuration(whitelistRules: [ruleDescription.identifier])!
let triggers = ruleDescription.triggeringExamples
let nonTriggers = ruleDescription.nonTriggeringExamples

// Non-triggering examples don't violate
XCTAssert(nonTriggers.flatMap({ violations($0, ruleDescription) }).isEmpty)
XCTAssert(nonTriggers.flatMap({ violations($0, config: config) }).isEmpty)

var violationsCount = 0
for trigger in triggers {
let triggerViolations = violations(trigger, ruleDescription)
let triggerViolations = violations(trigger, config: config)
violationsCount += triggerViolations.count
// Triggering examples with violation markers violate at the marker's location
let markerLocation = (trigger as NSString).rangeOfString(violationMarker).location
Expand All @@ -86,19 +80,19 @@ extension XCTestCase {

// Comment doesn't violate
XCTAssertEqual(
triggers.flatMap({ violations("/*\n " + $0 + "\n */", ruleDescription) }).count,
triggers.flatMap({ violations("/*\n " + $0 + "\n */", config: config) }).count,
commentDoesntViolate ? 0 : triggers.count
)

// String doesn't violate
XCTAssertEqual(
triggers.flatMap({ violations($0.toStringLiteral(), ruleDescription) }).count,
triggers.flatMap({ violations($0.toStringLiteral(), config: config) }).count,
stringDoesntViolate ? 0 : triggers.count
)

// "disable" command doesn't violate
let command = "// swiftlint:disable \(ruleDescription.identifier)\n"
XCTAssert(triggers.flatMap({ violations(command + $0, ruleDescription) }).isEmpty)
XCTAssert(triggers.flatMap({ violations(command + $0, config: config) }).isEmpty)

// corrections
ruleDescription.corrections.forEach(assertCorrection)
Expand Down

0 comments on commit 998bed9

Please sign in to comment.