Adds opt-in quick_discouraged_focused_test rule

Implements #1905.
This commit is contained in:
Ornithologist Coder
2017-10-15 22:49:54 +02:00
parent dfff3a59b9
commit b12280cfc2
6 changed files with 248 additions and 3 deletions
+5
View File
@@ -10,6 +10,11 @@
##### Enhancements
* Add `quick_discouraged_focused_test` opt-in rule which warns against
focused tests in Quick tests.
[Ornithologist Coder](https://github.com/ornithocoder)
[#1905](https://github.com/realm/SwiftLint/issues/1905)
* Add `override_in_extension` opt-in rule that warns against overriding
declarations in an `extension`.
[Marcelo Fabri](https://github.com/marcelofabri)
+101
View File
@@ -79,6 +79,7 @@
* [Prohibited calls to super](#prohibited-calls-to-super)
* [Protocol Property Accessors Order](#protocol-property-accessors-order)
* [Quick Discouraged Call](#quick-discouraged-call)
* [Quick Discouraged Focused Test](#quick-discouraged-focused-test)
* [Redundant Discardable Let](#redundant-discardable-let)
* [Redundant Nil Coalescing](#redundant-nil-coalescing)
* [Redundant Optional Initialization](#redundant-optional-initialization)
@@ -9272,6 +9273,106 @@ class TotoTests: QuickSpec {
## Quick Discouraged Focused Test
Identifier | Enabled by default | Supports autocorrection | Kind
--- | --- | --- | ---
`quick_discouraged_focused_test` | Disabled | No | lint
Discouraged focused test. Other tests won't run while this one is focused.
### Examples
<details>
<summary>Non Triggering Examples</summary>
```swift
class TotoTests: QuickSpec {
override func spec() {
describe("foo") {
describe("bar") { }
context("bar") {
it("bar") { }
}
it("bar") { }
}
}
}
```
</details>
<details>
<summary>Triggering Examples</summary>
```swift
class TotoTests: QuickSpec {
override func spec() {
fdescribe("foo") {
}
}
}
```
```swift
class TotoTests: QuickSpec {
override func spec() {
fcontext("foo") {
}
}
}
```
```swift
class TotoTests: QuickSpec {
override func spec() {
fit("foo") {
}
}
}
```
```swift
class TotoTests: QuickSpec {
override func spec() {
describe("foo") {
fit("bar") { }
}
}
}
```
```swift
class TotoTests: QuickSpec {
override func spec() {
context("foo") {
fit("bar") { }
}
}
}
```
```swift
class TotoTests: QuickSpec {
override func spec() {
describe("foo") {
context("bar") {
fit("toto") { }
}
}
}
```
</details>
## Redundant Discardable Let
Identifier | Enabled by default | Supports autocorrection | Kind
@@ -87,6 +87,7 @@ public let masterRuleList = RuleList(rules: [
ProhibitedSuperRule.self,
ProtocolPropertyAccessorsOrderRule.self,
QuickDiscouragedCallRule.self,
QuickDiscouragedFocusedTestRule.self,
RedundantDiscardableLetRule.self,
RedundantNilCoalescingRule.self,
RedundantOptionalInitializationRule.self,
@@ -0,0 +1,130 @@
//
// QuickDiscouragedCallRule.swift
// SwiftLint
//
// Created by Ornithologist Coder on 10/15/17.
// Copyright © 2017 Realm. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct QuickDiscouragedFocusedTestRule: OptInRule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.warning)
public init() {}
public static let description = RuleDescription(
identifier: "quick_discouraged_focused_test",
name: "Quick Discouraged Focused Test",
description: "Discouraged focused test. Other tests won't run while this one is focused.",
kind: .lint,
nonTriggeringExamples: [
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" describe(\"foo\") {\n" +
" describe(\"bar\") { } \n" +
" context(\"bar\") {\n" +
" it(\"bar\") { }\n" +
" }\n" +
" it(\"bar\") { }\n" +
" }\n" +
" }\n" +
"}\n"
],
triggeringExamples: [
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" ↓fdescribe(\"foo\") {\n" +
" }\n" +
" }\n" +
"}\n",
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" ↓fcontext(\"foo\") {\n" +
" }\n" +
" }\n" +
"}\n",
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" ↓fit(\"foo\") {\n" +
" }\n" +
" }\n" +
"}\n",
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" describe(\"foo\") {\n" +
" ↓fit(\"bar\") { }\n" +
" }\n" +
" }\n" +
"}\n",
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" context(\"foo\") {\n" +
" ↓fit(\"bar\") { }\n" +
" }\n" +
" }\n" +
"}\n",
"class TotoTests: QuickSpec {\n" +
" override func spec() {\n" +
" describe(\"foo\") {\n" +
" context(\"bar\") {\n" +
" ↓fit(\"toto\") { }\n" +
" }\n" +
" }\n" +
"}\n"
]
)
public func validate(file: File) -> [StyleViolation] {
let testClasses = file.structure.dictionary.substructure.filter {
return $0.inheritedTypes.contains("QuickSpec") &&
$0.kind.flatMap(SwiftDeclarationKind.init) == .class
}
let specDeclarations = testClasses.flatMap { classDict in
return classDict.substructure.filter {
return $0.name == "spec()" && $0.enclosedVarParameters.isEmpty &&
$0.kind.flatMap(SwiftDeclarationKind.init) == .functionMethodInstance &&
$0.enclosedSwiftAttributes.contains("source.decl.attribute.override")
}
}
return specDeclarations.flatMap {
validate(file: file, dictionary: $0)
}
}
private func validate(file: File, dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
return dictionary.substructure.flatMap { subDict -> [StyleViolation] in
var violations = validate(file: file, dictionary: subDict)
if let kindString = subDict.kind,
let kind = SwiftExpressionKind(rawValue: kindString) {
violations += validate(file: file, kind: kind, dictionary: subDict)
}
return violations
}
}
private func validate(file: File,
kind: SwiftExpressionKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard
kind == .call,
let name = dictionary.name,
let offset = dictionary.offset,
QuickFocusedCallKind(rawValue: name) != nil else { return [] }
return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: offset))]
}
}
private enum QuickFocusedCallKind: String {
case fdescribe
case fcontext
case fit
}
+7 -3
View File
@@ -73,6 +73,7 @@
6250D32A1ED4DFEB00735129 /* MultilineParametersRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6238AE411ED4D734006C3601 /* MultilineParametersRule.swift */; };
62622F6B1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62622F6A1F2F2E3500D5D099 /* DiscouragedDirectInitRule.swift */; };
626D02971F31CBCC0054788D /* XCTFailMessageRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */; };
627BC48D1F9405160004A6C2 /* QuickDiscouragedFocusedTestRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E54FED1F93AD57005B367B /* QuickDiscouragedFocusedTestRule.swift */; };
629C60D91F43906700B4AF92 /* SingleTestClassRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629C60D81F43906700B4AF92 /* SingleTestClassRule.swift */; };
62A498561F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */; };
62A6E7931F3317E3003A0479 /* JoinedDefaultRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */; };
@@ -410,6 +411,7 @@
62A498551F306A7700D766E4 /* DiscouragedDirectInitConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitConfiguration.swift; sourceTree = "<group>"; };
62A6E7911F3317E3003A0479 /* JoinedDefaultRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedDefaultRule.swift; sourceTree = "<group>"; };
62AF35D71F30B183009B11EE /* DiscouragedDirectInitRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedDirectInitRuleTests.swift; sourceTree = "<group>"; };
62E54FED1F93AD57005B367B /* QuickDiscouragedFocusedTestRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickDiscouragedFocusedTestRule.swift; sourceTree = "<group>"; };
65454F451B14D73800319A6C /* ControlStatementRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatementRule.swift; sourceTree = "<group>"; };
67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CyclomaticComplexityConfigurationTests.swift; sourceTree = "<group>"; };
67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CyclomaticComplexityConfiguration.swift; sourceTree = "<group>"; };
@@ -1020,7 +1022,6 @@
D4C0E46E1E3D973600C560F2 /* ForWhereRule.swift */,
E88DEA8F1B099A3100A66CB0 /* FunctionBodyLengthRule.swift */,
2E5761A91C573B83003271AF /* FunctionParameterCountRule.swift */,
827169B41F48D712003FB9AF /* NoGroupingExtensionRule.swift */,
D4B022A31E105636007E5297 /* GenericTypeNameRule.swift */,
E88DEA931B099C0900A66CB0 /* IdentifierNameRule.swift */,
D4130D961E16183F00242361 /* IdentifierNameRuleExamples.swift */,
@@ -1049,6 +1050,7 @@
E88DEA951B099CF200A66CB0 /* NestingRule.swift */,
D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */,
1E18574A1EADBA51004F89F7 /* NoExtensionAccessModifierRule.swift */,
827169B41F48D712003FB9AF /* NoGroupingExtensionRule.swift */,
D4DABFD61E2C23B1009617B6 /* NotificationCenterDetachmentRule.swift */,
D4DABFD81E2C59BC009617B6 /* NotificationCenterDetachmentRuleExamples.swift */,
D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */,
@@ -1057,8 +1059,8 @@
692B1EB11BD7E00F00EAABFF /* OpeningBraceRule.swift */,
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */,
D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */,
D40FE89C1F867BFF006433E2 /* OverrideInExtensionRule.swift */,
78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */,
D40FE89C1F867BFF006433E2 /* OverrideInExtensionRule.swift */,
D403A4A21F4DB5510020CA02 /* PatternMatchingKeywordsRule.swift */,
094385021D5D4F78009168CF /* PrivateOutletRule.swift */,
1E3C2D701EE36C6F00C8386D /* PrivateOverFilePrivateRule.swift */,
@@ -1067,12 +1069,14 @@
D47F31141EC918B600E3E1CA /* ProtocolPropertyAccessorsOrderRule.swift */,
623E36EF1F3DB1B1002E5B71 /* QuickDiscouragedCallRule.swift */,
623E36F11F3DB988002E5B71 /* QuickDiscouragedCallRuleExamples.swift */,
62E54FED1F93AD57005B367B /* QuickDiscouragedFocusedTestRule.swift */,
D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */,
24B4DF0B1D6DFA370097803B /* RedundantNilCoalescingRule.swift */,
D4B022951E0EF80C007E5297 /* RedundantOptionalInitializationRule.swift */,
D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */,
D4B022B11E10B613007E5297 /* RedundantVoidReturnRule.swift */,
E57B23C01B1D8BF000DEA512 /* ReturnArrowWhitespaceRule.swift */,
3BCC04CE1C4F56D3006073C3 /* RuleConfigurations */,
D4D5A5FE1E1F3A1C00D15E0C /* ShorthandOperatorRule.swift */,
629C60D81F43906700B4AF92 /* SingleTestClassRule.swift */,
D286EC001E02DA190003CF72 /* SortedImportsRule.swift */,
@@ -1103,7 +1107,6 @@
D47079AE1DFE520000027086 /* VoidReturnRule.swift */,
094384FF1D5D2382009168CF /* WeakDelegateRule.swift */,
626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */,
3BCC04CE1C4F56D3006073C3 /* RuleConfigurations */,
);
path = Rules;
sourceTree = "<group>";
@@ -1550,6 +1553,7 @@
E88198421BEA929F00333A11 /* NestingRule.swift in Sources */,
D46A317F1F1CEDCD00AF914A /* UnneededParenthesesInClosureArgumentRule.swift in Sources */,
D4470D591EB6B4D1008A1B2E /* EmptyEnumArgumentsRule.swift in Sources */,
627BC48D1F9405160004A6C2 /* QuickDiscouragedFocusedTestRule.swift in Sources */,
3BB47D851C51D80000AE6A10 /* NSRegularExpression+SwiftLint.swift in Sources */,
E881985B1BEA974E00333A11 /* StatementPositionRule.swift in Sources */,
B58AEED61C492C7B00E901FD /* ForceUnwrappingRule.swift in Sources */,
@@ -282,6 +282,10 @@ class RulesTests: XCTestCase {
verifyRule(QuickDiscouragedCallRule.description)
}
func testQuickDiscouragedFocusedTest() {
verifyRule(QuickDiscouragedFocusedTestRule.description)
}
func testRedundantDiscardableLet() {
verifyRule(RedundantDiscardableLetRule.description)
}