diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index d3d2b82214..0b94be60b8 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -21,6 +21,7 @@ public let builtInRules: [any Rule.Type] = [ ClosureSpacingRule.self, CollectionAlignmentRule.self, ColonRule.self, + CombineFlatMapRule.self, CommaInheritanceRule.self, CommaRule.self, CommentSpacingRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/CombineFlatMapRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/CombineFlatMapRule.swift new file mode 100644 index 0000000000..70831b1fcc --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/CombineFlatMapRule.swift @@ -0,0 +1,58 @@ +import SwiftSyntax + +@SwiftSyntaxRule +struct CombineFlatMapRule: Rule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "combine_flatmap", + name: "Combine FlatMap", + description: "Avoid using Combine's flatMap operator.", + kind: .lint, + nonTriggeringExamples: [ + Example("[1, 2, 3].flatMap { $0 }"), + Example("array.flatMap { $0 }"), + Example("Combine.map { $0 }") + ], + triggeringExamples: [ + Example("Combine.flatMap { $0 }"), + Example("let transform = Combine.flatMap") + ] + ) +} + +private extension CombineFlatMapRule { + final class Visitor: ViolationsSyntaxVisitor { + override func visitPost(_ node: FunctionCallExprSyntax) { + guard let memberAccess = node.calledExpression.as(MemberAccessExprSyntax.self), + memberAccess.isCombineFlatMap else { + return + } + + violations.append(node.positionAfterSkippingLeadingTrivia) + } + + override func visitPost(_ node: MemberAccessExprSyntax) { + guard node.isCombineFlatMap else { + return + } + + if let parent = node.parent?.as(FunctionCallExprSyntax.self), + parent.calledExpression.id == node.id { + return + } + + violations.append(node.positionAfterSkippingLeadingTrivia) + } + } +} + +private extension MemberAccessExprSyntax { + var isCombineFlatMap: Bool { + guard declName.baseName.text == "flatMap" else { + return false + } + + return base?.as(DeclReferenceExprSyntax.self)?.baseName.text == "Combine" + } +} diff --git a/Tests/GeneratedTests/GeneratedTests_01.swift b/Tests/GeneratedTests/GeneratedTests_01.swift index 39903d1af5..bb307b5c87 100644 --- a/Tests/GeneratedTests/GeneratedTests_01.swift +++ b/Tests/GeneratedTests/GeneratedTests_01.swift @@ -121,6 +121,12 @@ final class ColonRuleGeneratedTests: SwiftLintTestCase { } } +final class CombineFlatMapRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(CombineFlatMapRule.description) + } +} + final class CommaInheritanceRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(CommaInheritanceRule.description) @@ -150,9 +156,3 @@ final class ComputedAccessorsOrderRuleGeneratedTests: SwiftLintTestCase { verifyRule(ComputedAccessorsOrderRule.description) } } - -final class ConditionalReturnsOnNewlineRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(ConditionalReturnsOnNewlineRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_02.swift b/Tests/GeneratedTests/GeneratedTests_02.swift index c8d05b2749..7898a87c2d 100644 --- a/Tests/GeneratedTests/GeneratedTests_02.swift +++ b/Tests/GeneratedTests/GeneratedTests_02.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class ConditionalReturnsOnNewlineRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(ConditionalReturnsOnNewlineRule.description) + } +} + final class ContainsOverFilterCountRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(ContainsOverFilterCountRule.description) @@ -150,9 +156,3 @@ final class EmptyCountRuleGeneratedTests: SwiftLintTestCase { verifyRule(EmptyCountRule.description) } } - -final class EmptyEnumArgumentsRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(EmptyEnumArgumentsRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_03.swift b/Tests/GeneratedTests/GeneratedTests_03.swift index d90a7c1169..0ee3ec83be 100644 --- a/Tests/GeneratedTests/GeneratedTests_03.swift +++ b/Tests/GeneratedTests/GeneratedTests_03.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class EmptyEnumArgumentsRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(EmptyEnumArgumentsRule.description) + } +} + final class EmptyParametersRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(EmptyParametersRule.description) @@ -150,9 +156,3 @@ final class ForWhereRuleGeneratedTests: SwiftLintTestCase { verifyRule(ForWhereRule.description) } } - -final class ForceCastRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(ForceCastRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_04.swift b/Tests/GeneratedTests/GeneratedTests_04.swift index 20a6b1a48b..65ac6b2d38 100644 --- a/Tests/GeneratedTests/GeneratedTests_04.swift +++ b/Tests/GeneratedTests/GeneratedTests_04.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class ForceCastRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(ForceCastRule.description) + } +} + final class ForceTryRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(ForceTryRule.description) @@ -150,9 +156,3 @@ final class LegacyCGGeometryFunctionsRuleGeneratedTests: SwiftLintTestCase { verifyRule(LegacyCGGeometryFunctionsRule.description) } } - -final class LegacyConstantRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(LegacyConstantRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_05.swift b/Tests/GeneratedTests/GeneratedTests_05.swift index 748d2cf3bc..4d375ad870 100644 --- a/Tests/GeneratedTests/GeneratedTests_05.swift +++ b/Tests/GeneratedTests/GeneratedTests_05.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class LegacyConstantRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(LegacyConstantRule.description) + } +} + final class LegacyConstructorRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(LegacyConstructorRule.description) @@ -150,9 +156,3 @@ final class NSLocalizedStringRequireBundleRuleGeneratedTests: SwiftLintTestCase verifyRule(NSLocalizedStringRequireBundleRule.description) } } - -final class NSNumberInitAsFunctionReferenceRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(NSNumberInitAsFunctionReferenceRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_06.swift b/Tests/GeneratedTests/GeneratedTests_06.swift index 4e16ca2299..3b33529a2e 100644 --- a/Tests/GeneratedTests/GeneratedTests_06.swift +++ b/Tests/GeneratedTests/GeneratedTests_06.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class NSNumberInitAsFunctionReferenceRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(NSNumberInitAsFunctionReferenceRule.description) + } +} + final class NSObjectPreferIsEqualRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(NSObjectPreferIsEqualRule.description) @@ -150,9 +156,3 @@ final class PeriodSpacingRuleGeneratedTests: SwiftLintTestCase { verifyRule(PeriodSpacingRule.description) } } - -final class PreferAssetSymbolsRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(PreferAssetSymbolsRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_07.swift b/Tests/GeneratedTests/GeneratedTests_07.swift index 257050f83f..488b716472 100644 --- a/Tests/GeneratedTests/GeneratedTests_07.swift +++ b/Tests/GeneratedTests/GeneratedTests_07.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class PreferAssetSymbolsRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(PreferAssetSymbolsRule.description) + } +} + final class PreferConditionListRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(PreferConditionListRule.description) @@ -150,9 +156,3 @@ final class RedundantDiscardableLetRuleGeneratedTests: SwiftLintTestCase { verifyRule(RedundantDiscardableLetRule.description) } } - -final class RedundantNilCoalescingRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(RedundantNilCoalescingRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_08.swift b/Tests/GeneratedTests/GeneratedTests_08.swift index bb3481ea86..a18c6bdb96 100644 --- a/Tests/GeneratedTests/GeneratedTests_08.swift +++ b/Tests/GeneratedTests/GeneratedTests_08.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class RedundantNilCoalescingRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(RedundantNilCoalescingRule.description) + } +} + final class RedundantObjcAttributeRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(RedundantObjcAttributeRule.description) @@ -150,9 +156,3 @@ final class StrictFilePrivateRuleGeneratedTests: SwiftLintTestCase { verifyRule(StrictFilePrivateRule.description) } } - -final class StrongIBOutletRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(StrongIBOutletRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_09.swift b/Tests/GeneratedTests/GeneratedTests_09.swift index 73827e5186..8195d79e64 100644 --- a/Tests/GeneratedTests/GeneratedTests_09.swift +++ b/Tests/GeneratedTests/GeneratedTests_09.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class StrongIBOutletRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(StrongIBOutletRule.description) + } +} + final class SuperfluousElseRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(SuperfluousElseRule.description) @@ -150,9 +156,3 @@ final class UnneededSynthesizedInitializerRuleGeneratedTests: SwiftLintTestCase verifyRule(UnneededSynthesizedInitializerRule.description) } } - -final class UnneededThrowsRuleGeneratedTests: SwiftLintTestCase { - func testWithDefaultConfiguration() { - verifyRule(UnneededThrowsRule.description) - } -} diff --git a/Tests/GeneratedTests/GeneratedTests_10.swift b/Tests/GeneratedTests/GeneratedTests_10.swift index 002ad994e7..b647b6a303 100644 --- a/Tests/GeneratedTests/GeneratedTests_10.swift +++ b/Tests/GeneratedTests/GeneratedTests_10.swift @@ -7,6 +7,12 @@ @testable import SwiftLintCore import TestHelpers +final class UnneededThrowsRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(UnneededThrowsRule.description) + } +} + final class UnownedVariableCaptureRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(UnownedVariableCaptureRule.description)