From 72a18e4aec97fe879c1a9c5a72bcd670fa2eea2f Mon Sep 17 00:00:00 2001 From: Almaz Ibragimov Date: Thu, 14 Feb 2019 03:05:46 +0300 Subject: [PATCH] Fix false positives on no_grouping_extension rule when using where clause --- CHANGELOG.md | 3 ++ Rules.md | 6 +++ .../Idiomatic/NoGroupingExtensionRule.swift | 40 +++++++++++++++---- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9befa9f2..f2d84d547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,9 @@ #### Bug Fixes +* Fix false positives on `no_grouping_extension` rule when using `where` clause. + [Almaz Ibragimov](https://github.com/almazrafi) + * Fix `explicit_type_interface` when used in statements. [Daniel Metzing](https://github.com/dirtydanee) [#2154](https://github.com/realm/SwiftLint/issues/2154) diff --git a/Rules.md b/Rules.md index 072f34702..623b2d41e 100644 --- a/Rules.md +++ b/Rules.md @@ -13143,6 +13143,12 @@ extension Oranges {} ``` +```swift +class Box {} +extension Box where T: Vegetable {} + +``` +
Triggering Examples diff --git a/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift b/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift index ed0c6c1c5..c9c9090df 100644 --- a/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift +++ b/Source/SwiftLintFramework/Rules/Idiomatic/NoGroupingExtensionRule.swift @@ -12,7 +12,8 @@ public struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule, Aut kind: .idiomatic, nonTriggeringExamples: [ "protocol Food {}\nextension Food {}\n", - "class Apples {}\nextension Oranges {}\n" + "class Apples {}\nextension Oranges {}\n", + "class Box {}\nextension Box where T: Vegetable {}\n" ], triggeringExamples: [ "enum Fruit {}\n↓extension Fruit {}\n", @@ -28,12 +29,37 @@ public struct NoGroupingExtensionRule: OptInRule, ConfigurationProviderRule, Aut let susceptibleNames = Set(elements.compactMap { $0.kind != .extension ? $0.name : nil }) - return elements - .filter { $0.kind == .extension && susceptibleNames.contains($0.name) } - .map { - StyleViolation(ruleDescription: type(of: self).description, - severity: configuration.severity, - location: Location(file: file, byteOffset: $0.offset)) + return elements.compactMap { element in + guard element.kind == .extension, susceptibleNames.contains(element.name) else { + return nil } + + guard !hasWhereClause(element: element, file: file) else { + return nil + } + + return StyleViolation(ruleDescription: type(of: self).description, + severity: configuration.severity, + location: Location(file: file, byteOffset: element.offset)) + } + } + + private func hasWhereClause(element: NamespaceCollector.Element, file: File) -> Bool { + let contents = file.contents.bridge() + + guard let nameOffset = element.dictionary.nameOffset, + let nameLength = element.dictionary.nameLength, + let bodyOffset = element.dictionary.bodyOffset else { + return false + } + + let rangeStart = nameOffset + nameLength + let rangeLength = bodyOffset - rangeStart + + guard let range = contents.byteRangeToNSRange(start: rangeStart, length: rangeLength) else { + return false + } + + return (regex(" where ").firstMatch(in: file.contents, options: [], range: range) != nil) } }