Skip to content

Add MetaEnumMacro diagnostics aligned with ep251 #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions Tests/MacroTestingTests/MacroExamples/MetaEnumMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,19 @@ public struct MetaEnumMacro {
) throws {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else {
throw DiagnosticsError(diagnostics: [
CaseMacroDiagnostic.notAnEnum(declaration).diagnose(at: Syntax(node))
CaseMacroDiagnostic.notAnEnum(declaration).diagnose(at: Syntax(declaration.introducer))
])
}

var seenCaseNames: Set<String> = []
for name in enumDecl.caseElements.map(\.name) {
defer { seenCaseNames.insert(name.text) }
guard !seenCaseNames.contains(name.text) else {
throw DiagnosticsError(diagnostics: [
CaseMacroDiagnostic.overloadedCase.diagnose(at: Syntax(name))
])
}
}

parentTypeName = enumDecl.name.with(\.trailingTrivia, [])

Expand Down Expand Up @@ -105,29 +115,33 @@ extension EnumDeclSyntax {
}

enum CaseMacroDiagnostic {
case overloadedCase
case notAnEnum(DeclGroupSyntax)
}

extension CaseMacroDiagnostic: DiagnosticMessage {
var message: String {
switch self {
case .overloadedCase:
"'@MetaEnum' cannot be applied to enums with overloaded case names."
case .notAnEnum(let decl):
return
"'@MetaEnum' can only be attached to an enum, not \(decl.descriptiveDeclKind(withArticle: true))"
"'@MetaEnum' can only be attached to an enum, not \(decl.descriptiveDeclKind(withArticle: true))"
}
}

var diagnosticID: MessageID {
switch self {
case .overloadedCase:
MessageID(domain: "MetaEnumDiagnostic", id: "overloadedCase")
case .notAnEnum:
return MessageID(domain: "MetaEnumDiagnostic", id: "notAnEnum")
MessageID(domain: "MetaEnumDiagnostic", id: "notAnEnum")
}
}

var severity: DiagnosticSeverity {
switch self {
case .notAnEnum:
return .error
case .overloadedCase: .error
case .notAnEnum: .error
}
}

Expand All @@ -138,21 +152,8 @@ extension CaseMacroDiagnostic: DiagnosticMessage {

extension DeclGroupSyntax {
func descriptiveDeclKind(withArticle article: Bool = false) -> String {
switch self {
case is ActorDeclSyntax:
return article ? "an actor" : "actor"
case is ClassDeclSyntax:
return article ? "a class" : "class"
case is ExtensionDeclSyntax:
return article ? "an extension" : "extension"
case is ProtocolDeclSyntax:
return article ? "a protocol" : "protocol"
case is StructDeclSyntax:
return article ? "a struct" : "struct"
case is EnumDeclSyntax:
return article ? "an enum" : "enum"
default:
fatalError("Unknown DeclGroupSyntax")
}
let introducerText = introducer.text
let prefix = article ? ["a", "e", "i", "o", "u"].contains(introducerText.first!) ? "an " : "a " : ""
return prefix + introducerText
}
}
42 changes: 40 additions & 2 deletions Tests/MacroTestingTests/MetaEnumMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,51 @@ final class MetaEnumMacroTests: BaseTestCase {
} diagnostics: {
"""
@MetaEnum struct Cell {
┬────────
╰─ 🛑 '@MetaEnum' can only be attached to an enum, not a struct
─────
╰─ 🛑 '@MetaEnum' can only be attached to an enum, not a struct
let integer: Int
let text: String
let boolean: Bool
}
"""
}
}

func testDuplicateCaseName() {
assertMacro {
"""
@MetaEnum enum Foo {
case bar(int: Int)
case bar(string: String)
}
"""
} diagnostics: {
"""
@MetaEnum enum Foo {
case bar(int: Int)
case bar(string: String)
┬──
╰─ 🛑 '@MetaEnum' cannot be applied to enums with overloaded case names.
}
"""
}
}

func testOverloadedCaseName_SingleLine() {
assertMacro {
"""
@MetaEnum enum Foo {
case bar(int: Int), bar(string: String)
}
"""
} diagnostics: {
"""
@MetaEnum enum Foo {
case bar(int: Int), bar(string: String)
┬──
╰─ 🛑 '@MetaEnum' cannot be applied to enums with overloaded case names.
}
"""
}
}
}