Skip to content

Commit

Permalink
Merge pull request #1192 from marcelofabri/discarded_notification_cen…
Browse files Browse the repository at this point in the history
…ter_observer

Add discarded_notification_center_observer rule
  • Loading branch information
marcelofabri authored Jan 25, 2017
2 parents 962a036 + c891efc commit 6772ef6
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
`vertical_whitespace` rules.
[Marcelo Fabri](https://github.com/marcelofabri)

* Add `discarded_notification_center_observer` rule
that warns when the result of
`NotificationCenter.addObserver(forName:object:queue:using:)`
is not stored so it can be removed later.
[Marcelo Fabri](https://github.com/marcelofabri)
[#1062](https://github.com/realm/SwiftLint/issues/1062)

##### Bug Fixes

* Fix a false positive on `large_tuple` rule when using closures.
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 @@ -83,6 +83,7 @@ public let masterRuleList = RuleList(rules:
ControlStatementRule.self,
CustomRules.self,
CyclomaticComplexityRule.self,
DiscardedNotificationCenterObserverRule.self,
DynamicInlineRule.self,
EmptyCountRule.self,
EmptyParametersRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// DiscardedNotificationCenterObserverRule.swift
// SwiftLint
//
// Created by Marcelo Fabri on 01/13/17.
// Copyright © 2017 Realm. All rights reserved.
//

import Foundation
import SourceKittenFramework

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

public init() {}

public static let description = RuleDescription(
identifier: "discarded_notification_center_observer",
name: "Discarded Notification Center Observer",
description: "When registing for a notification using a block, the opaque observer that is " +
"returned should be stored so it can be removed later.",
nonTriggeringExamples: [
"let foo = nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }\n",
"let foo = nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n"
],
triggeringExamples: [
"↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }\n",
"↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n"
]
)

public func validate(file: File, kind: SwiftExpressionKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
return violationOffsets(in: file, dictionary: dictionary, kind: kind).map { location in
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: location))
}
}

private func violationOffsets(in file: File, dictionary: [String: SourceKitRepresentable],
kind: SwiftExpressionKind) -> [Int] {
guard kind == .call,
let name = dictionary.name,
name.hasSuffix(".addObserver"),
case let arguments = dictionary.enclosedArguments,
case let argumentsNames = arguments.flatMap({ $0.name }),
argumentsNames == ["forName", "object", "queue"] ||
argumentsNames == ["forName", "object", "queue", "using"],
let offset = dictionary.offset,
let range = file.contents.bridge().byteRangeToNSRange(start: 0, length: offset) else {
return []
}

if let lastMatch = regex("\\s?=\\s*").matches(in: file.contents, options: [], range: range).last?.range,
lastMatch.location == range.length - lastMatch.length {
return []
}

return [offset]
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
D4DA1DF81E175E8A0037413D /* LinterCache+CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA1DF71E175E8A0037413D /* LinterCache+CommandLine.swift */; };
D4DA1DFA1E18D6200037413D /* LargeTupleRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA1DF91E18D6200037413D /* LargeTupleRule.swift */; };
D4DA1DFE1E1A10DB0037413D /* NumberSeparatorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA1DFD1E1A10DB0037413D /* NumberSeparatorConfiguration.swift */; };
D4DABFD31E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DABFD21E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift */; };
D4DAE8BC1DE14E8F00B0AE7A /* NimbleOperatorRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */; };
D4FBADD01E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */; };
D4FD58B21E12A0200019503C /* LinterCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FD58B11E12A0200019503C /* LinterCache.swift */; };
Expand Down Expand Up @@ -407,6 +408,7 @@
D4DA1DF71E175E8A0037413D /* LinterCache+CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LinterCache+CommandLine.swift"; sourceTree = "<group>"; };
D4DA1DF91E18D6200037413D /* LargeTupleRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LargeTupleRule.swift; sourceTree = "<group>"; };
D4DA1DFD1E1A10DB0037413D /* NumberSeparatorConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberSeparatorConfiguration.swift; sourceTree = "<group>"; };
D4DABFD21E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscardedNotificationCenterObserverRule.swift; sourceTree = "<group>"; };
D4DAE8BB1DE14E8F00B0AE7A /* NimbleOperatorRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NimbleOperatorRule.swift; sourceTree = "<group>"; };
D4FBADCF1E00DA0400669C73 /* OperatorUsageWhitespaceRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorUsageWhitespaceRule.swift; sourceTree = "<group>"; };
D4FD58B11E12A0200019503C /* LinterCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinterCache.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -792,6 +794,7 @@
E88DEA831B0990F500A66CB0 /* ColonRule.swift */,
695BE9CE1BDFD92B0071E985 /* CommaRule.swift */,
D4DA1DF31E17511D0037413D /* CompilerProtocolInitRule.swift */,
D4DABFD21E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift */,
7C0C2E791D2866CB0076435A /* ExplicitInitRule.swift */,
93E0C3CD1D67BD7F007FA25D /* ConditionalReturnsOnNewline.swift */,
65454F451B14D73800319A6C /* ControlStatementRule.swift */,
Expand Down Expand Up @@ -1221,6 +1224,7 @@
D44254201DB87CA200492EA4 /* ValidIBInspectableRule.swift in Sources */,
85DA81321D6B471000951BC4 /* MarkRule.swift in Sources */,
D4A893351E15824100BF954D /* SwiftVersion.swift in Sources */,
D4DABFD31E29B4A5009617B6 /* DiscardedNotificationCenterObserverRule.swift in Sources */,
D4B022B21E10B613007E5297 /* RedundantVoidReturnRule.swift in Sources */,
3BCC04D21C4F56D3006073C3 /* NameConfiguration.swift in Sources */,
D4C27BFE1E12D53F00DF713E /* Version.swift in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions Tests/SwiftLintFrameworkTests/RulesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ class RulesTests: XCTestCase {
verifyRule(CyclomaticComplexityRule.description)
}

func testDiscardedNotificationCenterObserver() {
verifyRule(DiscardedNotificationCenterObserverRule.description)
}

func testDynamicInline() {
verifyRule(DynamicInlineRule.description)
}
Expand Down Expand Up @@ -349,6 +353,7 @@ extension RulesTests {
("testConditionalReturnsOnNewline", testConditionalReturnsOnNewline),
("testControlStatement", testControlStatement),
("testCyclomaticComplexity", testCyclomaticComplexity),
("testDiscardedNotificationCenterObserver", testDiscardedNotificationCenterObserver),
("testDynamicInline", testDynamicInline),
("testEmptyCount", testEmptyCount),
("testEmptyParameters", testEmptyParameters),
Expand Down

0 comments on commit 6772ef6

Please sign in to comment.