From f36187b12557ad8d30d94734984e46e78c240a64 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 24 Nov 2024 15:27:24 +0000 Subject: [PATCH] Refactor `EquatableMacroInfo` type --- Sources/OptionDescriptor.swift | 4 +-- Sources/Options.swift | 37 +++++++++++++++-------- Sources/Rules/RedundantEquatable.swift | 8 ++--- Tests/Rules/RedundantEquatableTests.swift | 15 +++++---- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/Sources/OptionDescriptor.swift b/Sources/OptionDescriptor.swift index 09e914887..2e93ef57d 100644 --- a/Sources/OptionDescriptor.swift +++ b/Sources/OptionDescriptor.swift @@ -1247,11 +1247,11 @@ struct _Descriptors { help: "Sort SwiftUI props: none, alphabetize, first-appearance-sort", keyPath: \.swiftUIPropertiesSortMode ) - let equatableMacroInfo = OptionDescriptor( + let equatableMacro = OptionDescriptor( argumentName: "equatablemacro", displayName: "The name and module of an Equatable conformance macro", help: "For example: \"@Equatable,EquatableMacroLib\"", - keyPath: \.equatableMacroInfo + keyPath: \.equatableMacro ) let preferFileMacro = OptionDescriptor( argumentName: "filemacro", diff --git a/Sources/Options.swift b/Sources/Options.swift index 3f8b0bddc..c5ed54c6a 100644 --- a/Sources/Options.swift +++ b/Sources/Options.swift @@ -590,21 +590,34 @@ public enum SwiftUIPropertiesSortMode: String, CaseIterable { case firstAppearanceSort = "first-appearance-sort" } -public struct EquatableMacroInfo: RawRepresentable { - /// The name of this macro, e.g. `@Equatable` - let macro: String - /// The name of the module defining this macro, e.g. `EquatableMacroLib` - let moduleName: String +public enum EquatableMacro: Equatable, RawRepresentable, CustomStringConvertible { + /// No equatable macro + case none + /// The name and the module for the macro, e.g. `@Equatable,EquatableMacroLib` + case macro(String, module: String) public init?(rawValue: String) { let components = rawValue.components(separatedBy: ",") - guard components.count == 2 else { return nil } - macro = components[0] - moduleName = components[1] + if components.count == 2 { + self = .macro(components[0], module: components[1]) + } else if rawValue == "none" { + self = .none + } else { + return nil + } } public var rawValue: String { - "\(macro),\(moduleName)" + switch self { + case .none: + return "none" + case let .macro(name, module: module): + return "\(name),\(module)" + } + } + + public var description: String { + rawValue } } @@ -718,7 +731,7 @@ public struct FormatOptions: CustomStringConvertible { public var timeZone: FormatTimeZone public var nilInit: NilInitType public var preservedPrivateDeclarations: Set - public var equatableMacroInfo: EquatableMacroInfo? + public var equatableMacro: EquatableMacro public var preferFileMacro: Bool /// Deprecated @@ -846,7 +859,7 @@ public struct FormatOptions: CustomStringConvertible { timeZone: FormatTimeZone = .system, nilInit: NilInitType = .remove, preservedPrivateDeclarations: Set = [], - equatableMacroInfo: EquatableMacroInfo? = nil, + equatableMacro: EquatableMacro = .none, preferFileMacro: Bool = true, // Doesn't really belong here, but hard to put elsewhere fragment: Bool = false, @@ -964,7 +977,7 @@ public struct FormatOptions: CustomStringConvertible { self.timeZone = timeZone self.nilInit = nilInit self.preservedPrivateDeclarations = preservedPrivateDeclarations - self.equatableMacroInfo = equatableMacroInfo + self.equatableMacro = equatableMacro self.preferFileMacro = preferFileMacro // Doesn't really belong here, but hard to put elsewhere self.fragment = fragment diff --git a/Sources/Rules/RedundantEquatable.swift b/Sources/Rules/RedundantEquatable.swift index 804373f02..d2259fa24 100644 --- a/Sources/Rules/RedundantEquatable.swift +++ b/Sources/Rules/RedundantEquatable.swift @@ -19,7 +19,7 @@ public extension FormatRule { isEligibleForAutoEquatableConformance = true case "class": // Projects can define an `@Equatable` macro that generates the Equatable implementation for classes - isEligibleForAutoEquatableConformance = formatter.options.equatableMacroInfo != nil + isEligibleForAutoEquatableConformance = formatter.options.equatableMacro != .none default: // This rule doesn't support other kinds of types. isEligibleForAutoEquatableConformance = false @@ -49,7 +49,7 @@ public extension FormatRule { // In projects using an `@Equatable` macro, the Equatable implementation // can be generated by that macro instead of written manually. - else if let equatableMacroInfo = formatter.options.equatableMacroInfo { + else if case let .macro(macro, module: module) = formatter.options.equatableMacro { let declarationWithEquatableConformance = equatableType.declarationWithEquatableConformance guard let equatableConformance = formatter.parseConformancesOfType(atKeywordIndex: declarationWithEquatableConformance.keywordIndex).first(where: { $0.conformance == "Equatable" || $0.conformance == "Hashable" }) @@ -74,12 +74,12 @@ public extension FormatRule { // Add the `@Equatable` macro formatter.insert( - [.keyword(equatableMacroInfo.macro), .space(" ")], + [.keyword(macro), .space(" ")], at: equatableType.typeDeclaration.startOfModifiersIndex ) // Import the module that defines the `@Equatable` macro if needed - formatter.addImports([equatableMacroInfo.moduleName]) + formatter.addImports([module]) } } } examples: { diff --git a/Tests/Rules/RedundantEquatableTests.swift b/Tests/Rules/RedundantEquatableTests.swift index 074ebc1e1..212c6b91e 100644 --- a/Tests/Rules/RedundantEquatableTests.swift +++ b/Tests/Rules/RedundantEquatableTests.swift @@ -199,7 +199,7 @@ final class RedundantEquatableTests: XCTestCase { let options = FormatOptions( typeAttributes: .prevLine, - equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib") + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") ) testFormatting( @@ -248,7 +248,7 @@ final class RedundantEquatableTests: XCTestCase { let options = FormatOptions( typeAttributes: .prevLine, - equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib") + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") ) testFormatting(for: input, rule: .redundantEquatable, options: options) @@ -286,7 +286,10 @@ final class RedundantEquatableTests: XCTestCase { } """ - let options = FormatOptions(typeAttributes: .prevLine, equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib")) + let options = FormatOptions( + typeAttributes: .prevLine, + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") + ) testFormatting(for: input, [output], rules: [.redundantEquatable, .blankLinesAtEndOfScope, .wrapAttributes], options: options) } @@ -321,7 +324,7 @@ final class RedundantEquatableTests: XCTestCase { let options = FormatOptions( typeAttributes: .prevLine, - equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib") + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") ) testFormatting( @@ -367,7 +370,7 @@ final class RedundantEquatableTests: XCTestCase { let options = FormatOptions( typeAttributes: .prevLine, - equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib") + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") ) testFormatting( @@ -510,7 +513,7 @@ final class RedundantEquatableTests: XCTestCase { let options = FormatOptions( typeAttributes: .prevLine, - equatableMacroInfo: EquatableMacroInfo(rawValue: "@Equatable,MyEquatableMacroLib") + equatableMacro: .macro("@Equatable", module: "MyEquatableMacroLib") ) testFormatting(for: input, [output], rules: [.redundantEquatable, .emptyBraces, .wrapAttributes, .emptyExtensions, .consecutiveBlankLines], options: options)