From ee0488baef8f497a7b7f2d17f58649c3e1cf33ea Mon Sep 17 00:00:00 2001 From: Lukas Pistrol Date: Thu, 15 Jun 2023 08:52:58 +0200 Subject: [PATCH] add type validation and protocol conformance --- .../SFSymbolsDiagnostic.swift | 6 +++ .../SFSymbolsMacroImpl/SFSymbolsMacro.swift | 21 ++++++++- .../SFSymbolsMacroTests.swift | 44 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Sources/SFSymbolsMacroImpl/SFSymbolsDiagnostic.swift b/Sources/SFSymbolsMacroImpl/SFSymbolsDiagnostic.swift index b38b9ce..901672d 100644 --- a/Sources/SFSymbolsMacroImpl/SFSymbolsDiagnostic.swift +++ b/Sources/SFSymbolsMacroImpl/SFSymbolsDiagnostic.swift @@ -8,6 +8,8 @@ import SwiftDiagnostics enum SFSymbolDiagnostic: DiagnosticMessage { + case notAnEnum + case missingStringProtocolConformance(symbol: String) case noValidSFSymbol(symbol: String) var severity: DiagnosticSeverity { return .error } @@ -16,6 +18,10 @@ enum SFSymbolDiagnostic: DiagnosticMessage { switch self { case .noValidSFSymbol(let symbol): "\"\(symbol)\" is not a valid SF Symbol." + case .notAnEnum: + "Macro \"@SFSymbol\" can only be applied to enums." + case .missingStringProtocolConformance(let symbol): + "Enum \"\(symbol)\" needs conformance to String protocol." } } diff --git a/Sources/SFSymbolsMacroImpl/SFSymbolsMacro.swift b/Sources/SFSymbolsMacroImpl/SFSymbolsMacro.swift index 071d4c7..0cb5e7b 100644 --- a/Sources/SFSymbolsMacroImpl/SFSymbolsMacro.swift +++ b/Sources/SFSymbolsMacroImpl/SFSymbolsMacro.swift @@ -21,7 +21,7 @@ public struct SFSymbolMacro: MemberMacro { providingMembersOf declaration: Declaration, in context: Context ) throws -> [DeclSyntax] { - try validateSymbolName(declaration) + try validate(declaration) return [ """ var image: Image { @@ -54,7 +54,8 @@ public struct SFSymbolMacro: MemberMacro { // MARK: Helper Methods - private static func validateSymbolName(_ declaration: DeclGroupSyntax) throws { + private static func validate(_ declaration: DeclGroupSyntax) throws { + try validateStringProtocolInheritance(declaration) let members = declaration.memberBlock.members let cases = members.compactMap { $0.decl.as(EnumCaseDeclSyntax.self) } let ids = getIdentifiers(from: cases) @@ -63,6 +64,22 @@ public struct SFSymbolMacro: MemberMacro { } } + private static func validateStringProtocolInheritance(_ declaration: DeclGroupSyntax) throws { + guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { + throw DiagnosticsError(diagnostics: [ + .init(node: Syntax(declaration), message: SFSymbolDiagnostic.notAnEnum) + ]) + } + let identifier = enumDecl.identifier + guard enumDecl.inheritanceClause?.inheritedTypeCollection.contains(where: { + $0.typeName.as(SimpleTypeIdentifierSyntax.self)?.name.text == "String" + }) ?? false else { + throw DiagnosticsError(diagnostics: [ + .init(node: Syntax(identifier), message: SFSymbolDiagnostic.missingStringProtocolConformance(symbol: identifier.text)) + ]) + } + } + /// Gets either the rawValue (if available) or the identifier of the enum case. private static func getIdentifiers(from cases: [EnumCaseDeclSyntax]) -> [SymbolPair] { return cases.flatMap { enumCase in diff --git a/Tests/SFSymbolsMacroTests/SFSymbolsMacroTests.swift b/Tests/SFSymbolsMacroTests/SFSymbolsMacroTests.swift index d791ebc..5c36914 100644 --- a/Tests/SFSymbolsMacroTests/SFSymbolsMacroTests.swift +++ b/Tests/SFSymbolsMacroTests/SFSymbolsMacroTests.swift @@ -91,4 +91,48 @@ final class SFSymbolsMacroTests: XCTestCase { macros: testMacros ) } + + func testInvalidEnum() { + assertMacroExpansion( + """ + @SFSymbol + enum Symbols { + case globe + } + """, + expandedSource: + """ + + enum Symbols { + case globe + } + """, + diagnostics: [ + .init(message: "Enum \"Symbols\" needs conformance to String protocol.", line: 2, column: 6) + ], + macros: testMacros + ) + } + + func testInvalidType() { + assertMacroExpansion( + """ + @SFSymbol + struct Symbols { + let xyz = "xyz" + } + """, + expandedSource: + """ + + struct Symbols { + let xyz = "xyz" + } + """, + diagnostics: [ + .init(message: "Macro \"@SFSymbol\" can only be applied to enums.", line: 1, column: 1) + ], + macros: testMacros + ) + } }