Skip to content

Commit

Permalink
Add opt-in prefer_self_type_over_type_of_self rule (realm#3006)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelofabri authored Jan 3, 2020
1 parent 20ba18c commit 093370c
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
details.
[Marcelo Fabri](https://github.com/marcelofabri)

* Add `prefer_self_type_over_type_of_self` opt-in rule to enforce using
`Self` instead of `type(of: self)` when using Swift 5.1 or above.
[Marcelo Fabri](https://github.com/marcelofabri)
[#3003](https://github.com/realm/SwiftLint/issues/3003)

#### Bug Fixes

* Fix crash in `unused_import` rule when unused imports have trailing
Expand Down
78 changes: 78 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
* [Overridden methods call super](#overridden-methods-call-super)
* [Override in Extension](#override-in-extension)
* [Pattern Matching Keywords](#pattern-matching-keywords)
* [Prefer Self Type Over Type of Self](#prefer-self-type-over-type-of-self)
* [Prefixed Top-Level Constant](#prefixed-top-level-constant)
* [Private Actions](#private-actions)
* [Private Outlets](#private-outlets)
Expand Down Expand Up @@ -16023,6 +16024,83 @@ switch foo {



## Prefer Self Type Over Type of Self

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`prefer_self_type_over_type_of_self` | Disabled | Yes | style | No | 5.1.0

Prefer `Self` over `type(of: self)` when accessing properties or calling methods.

### Examples

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

```swift
class Foo {
func bar() {
Self.baz()
}
}
```

```swift
class Foo {
func bar() {
print(Self.baz)
}
}
```

```swift
class A {
func foo(param: B) {
type(of: param).bar()
}
}
```

```swift
class A {
func foo() {
print(type(of: self))
}
}
```

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

```swift
class Foo {
func bar() {
↓type(of: self).baz()
}
}
```

```swift
class Foo {
func bar() {
print(↓type(of: self).baz)
}
}
```

```swift
class Foo {
func bar() {
print(↓Swift.type(of: self).baz)
}
}
```

</details>



## Prefixed Top-Level Constant

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
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 @@ -117,6 +117,7 @@ public let masterRuleList = RuleList(rules: [
OverriddenSuperCallRule.self,
OverrideInExtensionRule.self,
PatternMatchingKeywordsRule.self,
PreferSelfTypeOverTypeOfSelfRule.self,
PrefixedTopLevelConstantRule.self,
PrivateActionRule.self,
PrivateOutletRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import Foundation
import SourceKittenFramework

public struct PreferSelfTypeOverTypeOfSelfRule: OptInRule, ConfigurationProviderRule, SubstitutionCorrectableRule,
AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)

public static let description = RuleDescription(
identifier: "prefer_self_type_over_type_of_self",
name: "Prefer Self Type Over Type of Self",
description: "Prefer `Self` over `type(of: self)` when accessing properties or calling methods.",
kind: .style,
minSwiftVersion: .fiveDotOne,
nonTriggeringExamples: [
"""
class Foo {
func bar() {
Self.baz()
}
}
""",
"""
class Foo {
func bar() {
print(Self.baz)
}
}
""",
"""
class A {
func foo(param: B) {
type(of: param).bar()
}
}
""",
"""
class A {
func foo() {
print(type(of: self))
}
}
"""
],
triggeringExamples: [
"""
class Foo {
func bar() {
↓type(of: self).baz()
}
}
""",
"""
class Foo {
func bar() {
print(↓type(of: self).baz)
}
}
""",
"""
class Foo {
func bar() {
print(↓Swift.type(of: self).baz)
}
}
"""
],
corrections: [
"""
class Foo {
func bar() {
↓type(of: self).baz()
}
}
""": """
class Foo {
func bar() {
Self.baz()
}
}
""",
"""
class Foo {
func bar() {
print(↓type(of: self).baz)
}
}
""": """
class Foo {
func bar() {
print(Self.baz)
}
}
""",
"""
class Foo {
func bar() {
print(↓Swift.type(of: self).baz)
}
}
""": """
class Foo {
func bar() {
print(Self.baz)
}
}
"""
]
)

public init() {}

public func validate(file: SwiftLintFile) -> [StyleViolation] {
return violationRanges(in: file).map {
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, characterOffset: $0.location))
}
}

public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
guard SwiftVersion.current >= type(of: self).description.minSwiftVersion else {
return []
}

let pattern = "((?:Swift\\s*\\.\\s*)?type\\(\\s*of\\:\\s*self\\s*\\))\\s*\\."
return file.matchesAndSyntaxKinds(matching: pattern)
.filter {
$0.1 == [.identifier, .identifier, .identifier, .keyword] ||
$0.1 == [.identifier, .identifier, .keyword]
}
.map { $0.0.range(at: 1) }
}

public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
return (violationRange, "Self")
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
D46252541DF63FB200BE2CA1 /* NumberSeparatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */; };
D466B620233D229F0068190B /* FlatMapOverMapReduceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D466B61F233D229F0068190B /* FlatMapOverMapReduceRule.swift */; };
D46A317F1F1CEDCD00AF914A /* UnneededParenthesesInClosureArgumentRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46A317E1F1CEDCD00AF914A /* UnneededParenthesesInClosureArgumentRule.swift */; };
D46C7C3E23BF2F6A007C517F /* PreferSelfTypeOverTypeOfSelfRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46C7C3D23BF2F6A007C517F /* PreferSelfTypeOverTypeOfSelfRule.swift */; };
D46E041D1DE3712C00728374 /* TrailingCommaRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46E041C1DE3712C00728374 /* TrailingCommaRule.swift */; };
D47079A71DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */; };
D47079A91DFDBED000027086 /* ClosureParameterPositionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47079A81DFDBED000027086 /* ClosureParameterPositionRule.swift */; };
Expand Down Expand Up @@ -796,6 +797,7 @@
D46252531DF63FB200BE2CA1 /* NumberSeparatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorRule.swift; sourceTree = "<group>"; };
D466B61F233D229F0068190B /* FlatMapOverMapReduceRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlatMapOverMapReduceRule.swift; sourceTree = "<group>"; };
D46A317E1F1CEDCD00AF914A /* UnneededParenthesesInClosureArgumentRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnneededParenthesesInClosureArgumentRule.swift; sourceTree = "<group>"; };
D46C7C3D23BF2F6A007C517F /* PreferSelfTypeOverTypeOfSelfRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferSelfTypeOverTypeOfSelfRule.swift; sourceTree = "<group>"; };
D46E041C1DE3712C00728374 /* TrailingCommaRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingCommaRule.swift; sourceTree = "<group>"; };
D47079A61DFCEB2D00027086 /* EmptyParenthesesWithTrailingClosureRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyParenthesesWithTrailingClosureRule.swift; sourceTree = "<group>"; };
D47079A81DFDBED000027086 /* ClosureParameterPositionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosureParameterPositionRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1229,6 +1231,7 @@
E5A167C81B25A0B000CF2D03 /* OperatorFunctionWhitespaceRule.swift */,
D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */,
D43CDEDB23BDB8D30074F3EE /* OptionalEnumCaseMatchingRule.swift */,
D46C7C3D23BF2F6A007C517F /* PreferSelfTypeOverTypeOfSelfRule.swift */,
62DADC471FFF0423002B6319 /* PrefixedTopLevelConstantRule.swift */,
D47F31141EC918B600E3E1CA /* ProtocolPropertyAccessorsOrderRule.swift */,
D4C889701E385B7B00BAE88D /* RedundantDiscardableLetRule.swift */,
Expand Down Expand Up @@ -2162,6 +2165,7 @@
4968919223BEAC3D00AB8EF9 /* EnumCaseAssociatedValuesLengthRule.swift in Sources */,
2882895F222975D00037CF5F /* NSObjectPreferIsEqualRule.swift in Sources */,
18B90B6B21ADD99800B60749 /* RedundantObjcAttributeRule.swift in Sources */,
D46C7C3E23BF2F6A007C517F /* PreferSelfTypeOverTypeOfSelfRule.swift in Sources */,
93E0C3CE1D67BD7F007FA25D /* ConditionalReturnsOnNewlineRule.swift in Sources */,
D43DB1081DC573DA00281215 /* ImplicitGetterRule.swift in Sources */,
D450D1D121EC4A6900E60010 /* StrongIBOutletRule.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,12 @@ class PatternMatchingKeywordsRuleTests: XCTestCase {
}
}

class PreferSelfTypeOverTypeOfSelfRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferSelfTypeOverTypeOfSelfRule.description)
}
}

class PrivateActionRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(PrivateActionRule.description)
Expand Down

0 comments on commit 093370c

Please sign in to comment.