diff --git a/CHANGELOG.md b/CHANGELOG.md index c696c8fb3..fa9d5ba22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,12 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#1532](https://github.com/realm/SwiftLint/issues/1532) +* Don't trigger violations from `discarded_notification_center_observer` rule + when the observer is being returned from a function that is not marked + as `@discardableResult`. + [Marcelo Fabri](https://github.com/marcelofabri) + [#1525](https://github.com/realm/SwiftLint/issues/1525) + ##### Bug Fixes * None. diff --git a/Source/SwiftLintFramework/Rules/DiscardedNotificationCenterObserverRule.swift b/Source/SwiftLintFramework/Rules/DiscardedNotificationCenterObserverRule.swift index 8ee2ac05b..a5c10047f 100644 --- a/Source/SwiftLintFramework/Rules/DiscardedNotificationCenterObserverRule.swift +++ b/Source/SwiftLintFramework/Rules/DiscardedNotificationCenterObserverRule.swift @@ -21,11 +21,17 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro "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" + "let foo = nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n", + "func foo() -> Any {\n" + + " return nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n" + + "}\n" ], triggeringExamples: [ "↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }\n", - "↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n" + "↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n", + "@discardableResult func foo() -> Any {\n" + + " return ↓nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil, using: { })\n" + + "}\n" ] ) @@ -57,6 +63,35 @@ public struct DiscardedNotificationCenterObserverRule: ASTRule, ConfigurationPro return [] } + if let lastMatch = file.match(pattern: "\\breturn\\s+", with: [.keyword], range: range).last, + lastMatch.location == range.length - lastMatch.length, + let lastFunction = file.structure.functions(forByteOffset: offset).last, + !lastFunction.enclosedSwiftAttributes.contains("source.decl.attribute.discardableResult") { + return [] + } + return [offset] } } + +private extension Structure { + func functions(forByteOffset byteOffset: Int) -> [[String: SourceKitRepresentable]] { + var results = [[String: SourceKitRepresentable]]() + + func parse(_ dictionary: [String: SourceKitRepresentable]) { + guard let offset = dictionary.offset, + let byteRange = dictionary.length.map({ NSRange(location: offset, length: $0) }), + NSLocationInRange(byteOffset, byteRange) else { + return + } + + if let kind = dictionary.kind.flatMap(SwiftDeclarationKind.init), + SwiftDeclarationKind.functionKinds().contains(kind) { + results.append(dictionary) + } + dictionary.substructure.forEach(parse) + } + parse(dictionary) + return results + } +}