From 49456c26f5f017526998af7f14816928408c4eff Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Mon, 11 Dec 2023 19:10:38 +0500 Subject: [PATCH] - rule added for avoiding redundant extensions resolve #5359 --- .swiftlint.yml | 1 + CHANGELOG.md | 4 ++ .../Models/BuiltInRules.swift | 1 + .../Idiomatic/RedundantExtensionRule.swift | 62 +++++++++++++++++++ Tests/GeneratedTests/GeneratedTests.swift | 6 ++ 5 files changed, 74 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index d58e739045..dfab57497c 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -42,6 +42,7 @@ disabled_rules: - prefer_nimble - prefer_self_in_static_references - prefixed_toplevel_constant + - redundant_extension - redundant_self_in_closure - required_deinit - self_binding diff --git a/CHANGELOG.md b/CHANGELOG.md index 6502094eb5..7b0e6860c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ #### Enhancements +* Add new `redundant_extension` rule that detect redundant extensions. + [Muhammad Zeeshan](https://github.com/mzeeshanid) + [#5359](https://github.com/realm/SwiftLint/issues/5359) + * Add new `one_declaration_per_file` rule that allows only a single class/struct/enum/protocol declaration per file. Extensions are an exception; more than one is allowed. diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 4615c17caf..301211a247 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -166,6 +166,7 @@ public let builtInRules: [any Rule.Type] = [ ReduceBooleanRule.self, ReduceIntoRule.self, RedundantDiscardableLetRule.self, + RedundantExtensionRule.self, RedundantNilCoalescingRule.self, RedundantObjcAttributeRule.self, RedundantOptionalInitializationRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift new file mode 100644 index 0000000000..8a882b1a33 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift @@ -0,0 +1,62 @@ +import SwiftSyntax + +@SwiftSyntaxRule +struct RedundantExtensionRule: OptInRule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "redundant_extension", + name: "Redundant Extension", + description: "Avoid redundant extensions", + kind: .idiomatic, + nonTriggeringExamples: [ + Example(""" + extension Foo { + func something() {} + } + """), + Example(""" + extension Foo { + var a: Int { 1 } + } + """) + ], + triggeringExamples: [ + Example(""" + ↓extension Bar {} + """) + ] + ) +} + +private extension RedundantExtensionRule { + final class Visitor: ViolationsSyntaxVisitor { + private var isRedundantExtension = false + override var skippableDeclarations: [any DeclSyntaxProtocol.Type] { + return .allExcept(VariableDeclSyntax.self, FunctionDeclSyntax.self) + } + + override func visitPost(_ node: VariableDeclSyntax) { + isRedundantExtension = false + } + + override func visitPost(_ node: FunctionDeclSyntax) { + isRedundantExtension = false + } + + override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { + isRedundantExtension = true + return .visitChildren + } + + override func visitPost(_ node: ExtensionDeclSyntax) { + appendViolationIfNeeded(node: node.extensionKeyword) + } + + func appendViolationIfNeeded(node: TokenSyntax) { + if isRedundantExtension { + violations.append(node.positionAfterSkippingLeadingTrivia) + } + } + } +} diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 078c6f614e..2f919c2d73 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -986,6 +986,12 @@ class RedundantDiscardableLetRuleGeneratedTests: SwiftLintTestCase { } } +class RedundantExtensionRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(RedundantExtensionRule.description) + } +} + class RedundantNilCoalescingRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(RedundantNilCoalescingRule.description)