Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added hash_value_overflow rule #2496

Merged
merged 14 commits into from
Dec 5, 2018
Merged
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
for large files.
[Niil Öhlin](https://github.com/niilohlin)

* Add new `legacy_hashing` rule to encourage the use of Swift 4.2's new hashing
interface.
[Kim de Vos](https://github.com/kimdv)
[#2108](https://github.com/realm/SwiftLint/issues/2108)

#### Bug Fixes

* Fix false positives in `redundant_objc_attribute` for private declarations
Expand Down
89 changes: 89 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
* [Legacy CGGeometry Functions](#legacy-cggeometry-functions)
* [Legacy Constant](#legacy-constant)
* [Legacy Constructor](#legacy-constructor)
* [Legacy Hashing](#legacy-hashing)
* [Legacy NSGeometry Functions](#legacy-nsgeometry-functions)
* [Legacy Random](#legacy-random)
* [Variable Declaration Whitespace](#variable-declaration-whitespace)
Expand Down Expand Up @@ -9872,6 +9873,94 @@ UIOffset(horizontal: horizontal, vertical: vertical)



## Legacy Hashing

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`legacy_hashing` | Enabled | No | idiomatic | No | 4.2.0

Prefer using the `hash(into:)` function instead of overriding `hashValue`

### Examples

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

```swift
struct Foo: Hashable {
let bar: Int = 10

func hash(into hasher: inout Hasher) {
hasher.combine(bar)
}
}
```

```swift
class Foo: Hashable {
let bar: Int = 10

func hash(into hasher: inout Hasher) {
hasher.combine(bar)
}
}
```

```swift
var hashValue: Int { return 1 }
class Foo: Hashable {
}
```

```swift
class Foo: Hashable {
let bar: String = "Foo"

public var hashValue: String {
return bar
}
}
```

```swift
class Foo: Hashable {
let bar: String = "Foo"

public var hashValue: String {
get { return bar }
set { bar = newValue }
}
}
```

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

```swift
struct Foo: Hashable {
let bar: Int = 10

public ↓var hashValue: Int {
return bar
}
}
```

```swift
class Foo: Hashable {
let bar: Int = 10

public ↓var hashValue: Int {
return bar
}
}
```

</details>



## Legacy NSGeometry Functions

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ extension Configuration {
return type(of: lhs.rule).description.identifier == type(of: rhs.rule).description.identifier
}

fileprivate var hashValue: Int {
return type(of: rule).description.identifier.hashValue
fileprivate func hash(into hasher: inout Hasher) {
hasher.combine(type(of: rule).description.identifier)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ private struct RegexCacheKey: Hashable {
}

extension NSRegularExpression.Options: Hashable {
public var hashValue: Int {
return rawValue.hashValue
public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}
}

Expand Down
13 changes: 8 additions & 5 deletions Source/SwiftLintFramework/Models/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ public struct Configuration: Hashable {
public private(set) var configurationPath: String? // if successfully loaded from a path
public let cachePath: String?

public var hashValue: Int {
public func hash(into hasher: inout Hasher) {
if let configurationPath = configurationPath {
return configurationPath.hashValue
hasher.combine(configurationPath)
} else if let rootPath = rootPath {
return rootPath.hashValue
hasher.combine(rootPath)
} else if let cachePath = cachePath {
return cachePath.hashValue
hasher.combine(cachePath)
} else {
hasher.combine(included)
hasher.combine(excluded)
hasher.combine(reporter)
}
return (included + excluded + [reporter]).reduce(0, { $0 ^ $1.hashValue })
}

internal var computedCacheDescription: String?
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 @@ -68,6 +68,7 @@ public let masterRuleList = RuleList(rules: [
LegacyCGGeometryFunctionsRule.self,
LegacyConstantRule.self,
LegacyConstructorRule.self,
LegacyHashingRule.self,
LegacyNSGeometryFunctionsRule.self,
LegacyRandomRule.self,
LetVarWhitespaceRule.self,
Expand Down
97 changes: 97 additions & 0 deletions Source/SwiftLintFramework/Rules/Idiomatic/LegacyHashingRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Foundation
import SourceKittenFramework

public struct LegacyHashingRule: ASTRule, ConfigurationProviderRule, AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}

public static let description = RuleDescription(
identifier: "legacy_hashing",
name: "Legacy Hashing",
description: "Prefer using the `hash(into:)` function instead of overriding `hashValue`",
kind: .idiomatic,
minSwiftVersion: .fourDotTwo,
nonTriggeringExamples: [
"""
struct Foo: Hashable {
let bar: Int = 10

func hash(into hasher: inout Hasher) {
hasher.combine(bar)
}
}
""",
"""
class Foo: Hashable {
let bar: Int = 10

func hash(into hasher: inout Hasher) {
hasher.combine(bar)
}
}
""",
"""
var hashValue: Int { return 1 }
class Foo: Hashable { \n }
""",
"""
class Foo: Hashable {
let bar: String = "Foo"

public var hashValue: String {
return bar
}
}
""",
"""
class Foo: Hashable {
let bar: String = "Foo"

public var hashValue: String {
get { return bar }
set { bar = newValue }
}
}
"""
],
triggeringExamples: [
"""
struct Foo: Hashable {
let bar: Int = 10

public ↓var hashValue: Int {
return bar
}
}
""",
"""
class Foo: Hashable {
let bar: Int = 10

public ↓var hashValue: Int {
return bar
}
}
"""
]
)

// MARK: - ASTRule

public func validate(file: File,
kind: SwiftDeclarationKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard kind == .varInstance,
dictionary.setterAccessibility == nil,
dictionary.typeName == "Int",
dictionary.name == "hashValue",
let offset = dictionary.offset else {
return []
}

return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: offset))]
}
}
20 changes: 12 additions & 8 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@
78F032481D7D614300BE709A /* OverridenSuperCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F032471D7D614300BE709A /* OverridenSuperCallConfiguration.swift */; };
7C0C2E7A1D2866CB0076435A /* ExplicitInitRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */; };
820F451E21073D7200AA056A /* ConditionalReturnsOnNewlineRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820F451D21073D7200AA056A /* ConditionalReturnsOnNewlineRuleTests.swift */; };
824AB64D2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 824AB64C2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift */; };
823EDC6221020D850070B7CD /* MultilineLiteralBracketsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */; };
82144ACC20F640F200B06695 /* VerticalWhitespaceBetweenCasesRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82144ACB20F640F200B06695 /* VerticalWhitespaceBetweenCasesRule.swift */; };
823EDC6221020D850070B7CD /* MultilineLiteralBracketsRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */; };
824AB64D2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 824AB64C2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift */; };
825F19D11EEFF19700969EF1 /* ObjectLiteralRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825F19D01EEFF19700969EF1 /* ObjectLiteralRuleTests.swift */; };
827169B31F488181003FB9AF /* ExplicitEnumRawValueRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 827169B21F488181003FB9AF /* ExplicitEnumRawValueRule.swift */; };
827169B51F48D712003FB9AF /* NoGroupingExtensionRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 827169B41F48D712003FB9AF /* NoGroupingExtensionRule.swift */; };
Expand Down Expand Up @@ -200,6 +200,7 @@
C2B3C1612106F78C00088928 /* ConfigurationAliasesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B3C15F2106F78100088928 /* ConfigurationAliasesTests.swift */; };
C328A2F71E6759AE00A9E4D7 /* ExplicitTypeInterfaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328A2F51E67595500A9E4D7 /* ExplicitTypeInterfaceRule.swift */; };
C3DE5DAC1E7DF9CA00761483 /* FatalErrorMessageRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */; };
C3EF547821B5A4000009262F /* LegacyHashingRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3EF547521B5A2190009262F /* LegacyHashingRule.swift */; };
C946FECB1EAE67EE007DD778 /* LetVarWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C946FEC91EAE5E20007DD778 /* LetVarWhitespaceRule.swift */; };
C9802F2F1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9802F2E1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift */; };
CC26ED07204DEB510013BBBC /* RuleIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC26ED05204DE86E0013BBBC /* RuleIdentifier.swift */; };
Expand Down Expand Up @@ -566,9 +567,9 @@
78F032471D7D614300BE709A /* OverridenSuperCallConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverridenSuperCallConfiguration.swift; sourceTree = "<group>"; };
7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExplicitInitRule.swift; sourceTree = "<group>"; };
820F451D21073D7200AA056A /* ConditionalReturnsOnNewlineRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalReturnsOnNewlineRuleTests.swift; sourceTree = "<group>"; };
824AB64C2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalReturnsOnNewlineConfiguration.swift; sourceTree = "<group>"; };
823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLiteralBracketsRule.swift; sourceTree = "<group>"; };
82144ACB20F640F200B06695 /* VerticalWhitespaceBetweenCasesRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalWhitespaceBetweenCasesRule.swift; sourceTree = "<group>"; };
823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLiteralBracketsRule.swift; sourceTree = "<group>"; };
824AB64C2105C39F004B5A8F /* ConditionalReturnsOnNewlineConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalReturnsOnNewlineConfiguration.swift; sourceTree = "<group>"; };
825F19D01EEFF19700969EF1 /* ObjectLiteralRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectLiteralRuleTests.swift; sourceTree = "<group>"; };
827169B21F488181003FB9AF /* ExplicitEnumRawValueRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExplicitEnumRawValueRule.swift; sourceTree = "<group>"; };
827169B41F48D712003FB9AF /* NoGroupingExtensionRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoGroupingExtensionRule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -629,6 +630,7 @@
C2B3C15F2106F78100088928 /* ConfigurationAliasesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationAliasesTests.swift; sourceTree = "<group>"; };
C328A2F51E67595500A9E4D7 /* ExplicitTypeInterfaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExplicitTypeInterfaceRule.swift; sourceTree = "<group>"; };
C3DE5DAA1E7DF99B00761483 /* FatalErrorMessageRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FatalErrorMessageRule.swift; sourceTree = "<group>"; };
C3EF547521B5A2190009262F /* LegacyHashingRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyHashingRule.swift; sourceTree = "<group>"; };
C946FEC91EAE5E20007DD778 /* LetVarWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LetVarWhitespaceRule.swift; sourceTree = "<group>"; };
C9802F2E1E0C8AEE008AB27F /* TrailingCommaRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrailingCommaRuleTests.swift; sourceTree = "<group>"; };
CC26ED05204DE86E0013BBBC /* RuleIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleIdentifier.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1063,12 +1065,12 @@
D4EA77C91F81FACC00C315FB /* LiteralExpressionEndIdentationRule.swift */,
188B3FF1207D61040073C2D6 /* ModifierOrderRule.swift */,
BC8757392195CDD500CA7A74 /* ModifierOrderRuleExamples.swift */,
82F614F32106015100D23904 /* MultilineArgumentsBracketsRule.swift */,
82F614F32106015100D23904 /* MultilineArgumentsBracketsRule.swift */,
B25DCD071F7E9B5F0028A199 /* MultilineArgumentsRule.swift */,
B25DCD091F7E9BB50028A199 /* MultilineArgumentsRuleExamples.swift */,
3ABE19CD20B7CDE0009C2EC2 /* MultilineFunctionChainsRule.swift */,
823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */,
82F614F12106014500D23904 /* MultilineParametersBracketsRule.swift */,
823EDC6121020D850070B7CD /* MultilineLiteralBracketsRule.swift */,
82F614F12106014500D23904 /* MultilineParametersBracketsRule.swift */,
6238AE411ED4D734006C3601 /* MultilineParametersRule.swift */,
621061BE1ED57E640082D51E /* MultilineParametersRuleExamples.swift */,
BB00B4E71F5216070079869F /* MultipleClosuresWithTrailingClosureRule.swift */,
Expand Down Expand Up @@ -1136,7 +1138,9 @@
006ECFC31C44E99E00EF6364 /* LegacyConstantRule.swift */,
00B8D9771E2D0FBD004E0EEC /* LegacyConstantRuleExamples.swift */,
D44AD2741C0AA3730048F7B0 /* LegacyConstructorRule.swift */,
C3EF547521B5A2190009262F /* LegacyHashingRule.swift */,
F22314AE1D4F7C77009AD165 /* LegacyNSGeometryFunctionsRule.swift */,
A3184D55215BCEFF00621EA2 /* LegacyRandomRule.swift */,
D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */,
1E18574A1EADBA51004F89F7 /* NoExtensionAccessModifierRule.swift */,
ED641C3620AA070700212C62 /* NoFallthroughOnlyRule.swift */,
Expand All @@ -1163,7 +1167,6 @@
181D9E162038343D001F6887 /* UntypedErrorInCatchRule.swift */,
D43B04631E0620AB004016AF /* UnusedEnumeratedRule.swift */,
626D02961F31CBCC0054788D /* XCTFailMessageRule.swift */,
A3184D55215BCEFF00621EA2 /* LegacyRandomRule.swift */,
);
path = Idiomatic;
sourceTree = "<group>";
Expand Down Expand Up @@ -1895,6 +1898,7 @@
006ECFC41C44E99E00EF6364 /* LegacyConstantRule.swift in Sources */,
82FE254120F604CB00295958 /* VerticalWhitespaceClosingBracesRule.swift in Sources */,
429644B61FB0A9B400D75128 /* SortedFirstLastRule.swift in Sources */,
C3EF547821B5A4000009262F /* LegacyHashingRule.swift in Sources */,
31F1B6CC1F60BF4500A57456 /* SwitchCaseAlignmentRule.swift in Sources */,
E88DEA731B0984C400A66CB0 /* String+SwiftLint.swift in Sources */,
E88198591BEA95F100333A11 /* LeadingWhitespaceRule.swift in Sources */,
Expand Down
7 changes: 7 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,12 @@ extension LegacyConstructorRuleTests {
]
}

extension LegacyHashingRuleTests {
static var allTests: [(String, (LegacyHashingRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
]
}

extension LegacyNSGeometryFunctionsRuleTests {
static var allTests: [(String, (LegacyNSGeometryFunctionsRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1420,6 +1426,7 @@ XCTMain([
testCase(LegacyCGGeometryFunctionsRuleTests.allTests),
testCase(LegacyConstantRuleTests.allTests),
testCase(LegacyConstructorRuleTests.allTests),
testCase(LegacyHashingRuleTests.allTests),
testCase(LegacyNSGeometryFunctionsRuleTests.allTests),
testCase(LegacyRandomRuleTests.allTests),
testCase(LetVarWhitespaceRuleTests.allTests),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ class LegacyConstructorRuleTests: XCTestCase {
}
}

class LegacyHashingRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(LegacyHashingRule.description)
}
}

class LegacyNSGeometryFunctionsRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(LegacyNSGeometryFunctionsRule.description)
Expand Down