From 49456c26f5f017526998af7f14816928408c4eff Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Mon, 11 Dec 2023 19:10:38 +0500 Subject: [PATCH 01/10] - 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) From 5583cf0c09ffaca6411026d526cd5be3a988fa41 Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Tue, 12 Dec 2023 12:05:52 +0500 Subject: [PATCH 02/10] - simplified logic - new case added in non triggering example --- .../Idiomatic/RedundantExtensionRule.swift | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift index 8a882b1a33..e78c22933a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift @@ -19,6 +19,11 @@ struct RedundantExtensionRule: OptInRule { extension Foo { var a: Int { 1 } } + """), + Example(""" + extension Foo { + final class Bar {} + } """) ], triggeringExamples: [ @@ -31,31 +36,9 @@ struct RedundantExtensionRule: OptInRule { 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) + if node.memberBlock.members.isEmpty { + violations.append(node.extensionKeyword.positionAfterSkippingLeadingTrivia) } } } From 83ddd292242094fb9842b8b5a146730f27d6d2ca Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Tue, 12 Dec 2023 13:47:12 +0500 Subject: [PATCH 03/10] - new example added --- .../Rules/Idiomatic/RedundantExtensionRule.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift index e78c22933a..81c5bb64e7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift @@ -24,6 +24,11 @@ struct RedundantExtensionRule: OptInRule { extension Foo { final class Bar {} } + """), + Example(""" + extension Foo { + struct Bar {} + } """) ], triggeringExamples: [ From 8f2fca1133fbd284fa3c880cc3d7265d5b21ac3c Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Fri, 15 Dec 2023 21:36:00 +0500 Subject: [PATCH 04/10] - change log updated --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0e6860c6..e2e5ccfd39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ #### Enhancements -* Add new `redundant_extension` rule that detect redundant extensions. +* Add new `redundant_extension` rule that detects redundant extensions. + An extension is considered redundant if it does not define any + members, but only conformances. [Muhammad Zeeshan](https://github.com/mzeeshanid) [#5359](https://github.com/realm/SwiftLint/issues/5359) From ee386d60e10c145f4d35657c4404aaba31c95a85 Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Fri, 15 Dec 2023 22:57:47 +0500 Subject: [PATCH 05/10] - change log updated --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e5ccfd39..66b9e19316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * Add new `redundant_extension` rule that detects redundant extensions. An extension is considered redundant if it does not define any - members, but only conformances. + members, but only conformances. [Muhammad Zeeshan](https://github.com/mzeeshanid) [#5359](https://github.com/realm/SwiftLint/issues/5359) From 66b44ab9644b3ff8e9df360161aba3fcce5fd9ff Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Mon, 11 Dec 2023 19:10:38 +0500 Subject: [PATCH 06/10] - 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) From d4406aaa599216c696f2eebc918da468e1307487 Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Tue, 12 Dec 2023 12:05:52 +0500 Subject: [PATCH 07/10] - simplified logic - new case added in non triggering example --- .../Idiomatic/RedundantExtensionRule.swift | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift index 8a882b1a33..e78c22933a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift @@ -19,6 +19,11 @@ struct RedundantExtensionRule: OptInRule { extension Foo { var a: Int { 1 } } + """), + Example(""" + extension Foo { + final class Bar {} + } """) ], triggeringExamples: [ @@ -31,31 +36,9 @@ struct RedundantExtensionRule: OptInRule { 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) + if node.memberBlock.members.isEmpty { + violations.append(node.extensionKeyword.positionAfterSkippingLeadingTrivia) } } } From 5ed627402cddc7d9b1ebc190e6840f05f76c48db Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Tue, 12 Dec 2023 13:47:12 +0500 Subject: [PATCH 08/10] - new example added --- .../Rules/Idiomatic/RedundantExtensionRule.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift index e78c22933a..81c5bb64e7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantExtensionRule.swift @@ -24,6 +24,11 @@ struct RedundantExtensionRule: OptInRule { extension Foo { final class Bar {} } + """), + Example(""" + extension Foo { + struct Bar {} + } """) ], triggeringExamples: [ From ce002432730ffa8b61ab634c43b072dd72c0d5e8 Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Fri, 15 Dec 2023 21:36:00 +0500 Subject: [PATCH 09/10] - change log updated --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0e6860c6..e2e5ccfd39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ #### Enhancements -* Add new `redundant_extension` rule that detect redundant extensions. +* Add new `redundant_extension` rule that detects redundant extensions. + An extension is considered redundant if it does not define any + members, but only conformances. [Muhammad Zeeshan](https://github.com/mzeeshanid) [#5359](https://github.com/realm/SwiftLint/issues/5359) From 95ca6fd2e94a8742d261216cc9ad436aa316b57d Mon Sep 17 00:00:00 2001 From: Muhammad Zeeshan Date: Fri, 15 Dec 2023 22:57:47 +0500 Subject: [PATCH 10/10] - change log updated --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e5ccfd39..66b9e19316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * Add new `redundant_extension` rule that detects redundant extensions. An extension is considered redundant if it does not define any - members, but only conformances. + members, but only conformances. [Muhammad Zeeshan](https://github.com/mzeeshanid) [#5359](https://github.com/realm/SwiftLint/issues/5359)