Skip to content

Commit

Permalink
Adds new opt-in rule, private_action
Browse files Browse the repository at this point in the history
Implements realm#1931.
  • Loading branch information
ornithocoder committed Nov 17, 2017
1 parent a03f8f8 commit 9d2c113
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
[JP Simard](https://github.com/jpsim)
[#1822](https://github.com/realm/SwiftLint/issues/1822)

* Add `private_action` opt-in rule which warns agaist public @IBAction methods.
[Ornithologist Coder](https://github.com/ornithocoder)
[#1931](https://github.com/realm/SwiftLint/issues/1931)

##### Bug Fixes

* Extend `first_where` and `contains_over_first_not_nil` rules to also detect
Expand Down
141 changes: 141 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* [Overridden methods call super](#overridden-methods-call-super)
* [Override in Extension](#override-in-extension)
* [Pattern Matching Keywords](#pattern-matching-keywords)
* [Private Actions](#private-actions)
* [Private Outlets](#private-outlets)
* [Private over fileprivate](#private-over-fileprivate)
* [Private Unit Test](#private-unit-test)
Expand Down Expand Up @@ -8790,6 +8791,146 @@ switch foo {



## Private Actions

Identifier | Enabled by default | Supports autocorrection | Kind
--- | --- | --- | ---
`private_action` | Disabled | No | lint

IBActions should be private.

### Examples

<details>
<summary>Non Triggering Examples</summary>

```swift
class Foo {
@IBAction private func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
struct Foo {
@IBAction private func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
class Foo {
@IBAction fileprivate func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
struct Foo {
@IBAction fileprivate func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
private extension Foo {
@IBAction func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
fileprivate extension Foo {
@IBAction func barButtonTapped(_ sender: UIButton) {}
}

```

</details>
<details>
<summary>Triggering Examples</summary>

```swift
class Foo {
@IBAction ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
struct Foo {
@IBAction ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
class Foo {
@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
struct Foo {
@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
class Foo {
@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
struct Foo {
@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
extension Foo {
@IBAction ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
extension Foo {
@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
extension Foo {
@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
public extension Foo {
@IBAction ↓func barButtonTapped(_ sender: UIButton) {}
}

```

```swift
internal extension Foo {
@IBAction ↓func barButtonTapped(_ sender: UIButton) {}
}

```

</details>



## Private Outlets

Identifier | Enabled by default | Supports autocorrection | Kind
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public let masterRuleList = RuleList(rules: [
OverriddenSuperCallRule.self,
OverrideInExtensionRule.self,
PatternMatchingKeywordsRule.self,
PrivateActionRule.self,
PrivateOutletRule.self,
PrivateOverFilePrivateRule.self,
PrivateUnitTestRule.self,
Expand Down
64 changes: 64 additions & 0 deletions Source/SwiftLintFramework/Rules/PrivateActionRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// PrivateActionRule.swift
// SwiftLint
//
// Created by Ornithologist Coder on 11/7/17.
// Copyright © 2016 Realm. All rights reserved.
//

import Foundation
import SourceKittenFramework

public struct PrivateActionRule: ASTRule, OptInRule, ConfigurationProviderRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}

public static let description = RuleDescription(
identifier: "private_action",
name: "Private Actions",
description: "IBActions should be private.",
kind: .lint,
nonTriggeringExamples: [
"class Foo {\n\t@IBAction private func barButtonTapped(_ sender: UIButton) {}\n}\n",
"struct Foo {\n\t@IBAction private func barButtonTapped(_ sender: UIButton) {}\n}\n",
"class Foo {\n\t@IBAction fileprivate func barButtonTapped(_ sender: UIButton) {}\n}\n",
"struct Foo {\n\t@IBAction fileprivate func barButtonTapped(_ sender: UIButton) {}\n}\n",
"private extension Foo {\n\t@IBAction func barButtonTapped(_ sender: UIButton) {}\n}\n",
"fileprivate extension Foo {\n\t@IBAction func barButtonTapped(_ sender: UIButton) {}\n}\n"
],
triggeringExamples: [
"class Foo {\n\t@IBAction ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"struct Foo {\n\t@IBAction ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"class Foo {\n\t@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"struct Foo {\n\t@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"class Foo {\n\t@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"struct Foo {\n\t@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"extension Foo {\n\t@IBAction ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"extension Foo {\n\t@IBAction public ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"extension Foo {\n\t@IBAction internal ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"public extension Foo {\n\t@IBAction ↓func barButtonTapped(_ sender: UIButton) {}\n}\n",
"internal extension Foo {\n\t@IBAction ↓func barButtonTapped(_ sender: UIButton) {}\n}\n"
]
)

public func validate(file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard
let offset = dictionary.offset,
kind == .functionMethodInstance,
dictionary.enclosedSwiftAttributes.contains("source.decl.attribute.ibaction"),
let controlLevel = dictionary.accessibility.flatMap(AccessControlLevel.init(identifier:)),
controlLevel.isPrivate == false
else {
return []
}

return [
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: offset))
]
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
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 */; };
62DEA1661FB21A9E00BCCCC6 /* PrivateActionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */; };
67932E2D1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67932E2C1E54AF4B00CB0629 /* CyclomaticComplexityConfigurationTests.swift */; };
67EB4DFA1E4CC111004E9ACD /* CyclomaticComplexityConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DF81E4CC101004E9ACD /* CyclomaticComplexityConfiguration.swift */; };
67EB4DFC1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67EB4DFB1E4CD7F5004E9ACD /* CyclomaticComplexityRuleTests.swift */; };
Expand Down Expand Up @@ -420,6 +421,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>"; };
62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateActionRule.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>"; };
Expand Down Expand Up @@ -1072,6 +1074,7 @@
78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */,
D40FE89C1F867BFF006433E2 /* OverrideInExtensionRule.swift */,
D403A4A21F4DB5510020CA02 /* PatternMatchingKeywordsRule.swift */,
62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */,
094385021D5D4F78009168CF /* PrivateOutletRule.swift */,
1E3C2D701EE36C6F00C8386D /* PrivateOverFilePrivateRule.swift */,
B2902A0B1D66815600BFCCF7 /* PrivateUnitTestRule.swift */,
Expand Down Expand Up @@ -1583,6 +1586,7 @@
B2902A0C1D66815600BFCCF7 /* PrivateUnitTestRule.swift in Sources */,
D47A51101DB2DD4800A4CC21 /* AttributesRule.swift in Sources */,
CE8178ED1EAC039D0063186E /* UnusedOptionalBindingConfiguration.swift in Sources */,
62DEA1661FB21A9E00BCCCC6 /* PrivateActionRule.swift in Sources */,
D4FD58B21E12A0200019503C /* LinterCache.swift in Sources */,
3BD9CD3D1C37175B009A5D25 /* YamlParser.swift in Sources */,
F22314B01D4FA4D7009AD165 /* LegacyNSGeometryFunctionsRule.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ extension RulesTests {
("testOperatorUsageWhitespace", testOperatorUsageWhitespace),
("testOverrideInExtension", testOverrideInExtension),
("testPatternMatchingKeywords", testPatternMatchingKeywords),
("testPrivateAction", testPrivateAction),
("testPrivateOutlet", testPrivateOutlet),
("testPrivateUnitTest", testPrivateUnitTest),
("testProhibitedSuper", testProhibitedSuper),
Expand Down
4 changes: 4 additions & 0 deletions Tests/SwiftLintFrameworkTests/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ class RulesTests: XCTestCase {
verifyRule(PatternMatchingKeywordsRule.description)
}

func testPrivateAction() {
verifyRule(PrivateActionRule.description)
}

func testPrivateOutlet() {
verifyRule(PrivateOutletRule.description)

Expand Down

0 comments on commit 9d2c113

Please sign in to comment.