Skip to content

Commit

Permalink
better diagnosis
Browse files Browse the repository at this point in the history
  • Loading branch information
MahdiBM committed Jul 16, 2024
1 parent 0a1b67d commit a733432
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 30 deletions.
17 changes: 1 addition & 16 deletions Sources/EnumeratorMacro/Enumerator.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
@attached(member, names: arbitrary)
public macro Enumerator(_ templates: StaticString...) = #externalMacro(
public macro Enumerator(_ templates: String...) = #externalMacro(
module: "EnumeratorMacroImpl",
type: "EnumeratorMacroType"
)

@Enumerator("""
var caseName: String {
switch self {
{{#cases}}
case .{{name}}: "{{name}}"
{{/cases}}
}
}
""")
enum TestEnum {
case a(val1: String, val2: Int)
case b
case testCase(testValue: String)
}
25 changes: 19 additions & 6 deletions Sources/EnumeratorMacroImpl/EnumeratorMacroType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,27 @@ extension EnumeratorMacroType: MemberMacro {
if exprList.isEmpty {
throw MacroError.expectedAtLeastOneArgument
}
let templates = try exprList.compactMap {
element -> (template: String, syntax: StringLiteralExprSyntax) in
let templates = exprList.compactMap {
element -> (template: String, syntax: StringLiteralExprSyntax)? in
guard let stringLiteral = element.expression.as(StringLiteralExprSyntax.self) else {
throw MacroError.allArgumentsMustBeStringLiterals(violation: element.description)
context.diagnose(
Diagnostic(
node: element.expression,
message: MacroError.allArgumentsMustBeNonInterpolatedStringLiterals
)
)
return nil
}
for segment in stringLiteral.segments where !segment.is(StringSegmentSyntax.self) {
context.diagnose(
Diagnostic(
node: segment,
message: MacroError.allArgumentsMustBeNonInterpolatedStringLiterals
)
)
return nil
}
let template = stringLiteral
.segments
.description
let template = stringLiteral.segments.description
return (template, stringLiteral)
}
let rendered = templates.compactMap {
Expand Down
10 changes: 5 additions & 5 deletions Sources/EnumeratorMacroImpl/MacroError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ enum MacroError: Error, CustomStringConvertible {
case macroDeclarationHasNoArguments
case unacceptableArguments
case expectedAtLeastOneArgument
case allArgumentsMustBeStringLiterals(violation: String)
case allArgumentsMustBeNonInterpolatedStringLiterals
case renderedSyntaxContainsErrors(String)
case couldNotFindLocationOfNode(syntax: String)
case mustacheTemplateError(message: String)
Expand All @@ -23,8 +23,8 @@ enum MacroError: Error, CustomStringConvertible {
"unacceptableArguments"
case .expectedAtLeastOneArgument:
"expectedAtLeastOneArgument"
case .allArgumentsMustBeStringLiterals:
"allArgumentsMustBeStringLiterals"
case .allArgumentsMustBeNonInterpolatedStringLiterals:
"allArgumentsMustBeNonInterpolatedStringLiterals"
case .renderedSyntaxContainsErrors:
"renderedSyntaxContainsErrors"
case .couldNotFindLocationOfNode:
Expand All @@ -48,8 +48,8 @@ enum MacroError: Error, CustomStringConvertible {
"The arguments passed to the macro were unacceptable"
case .expectedAtLeastOneArgument:
"At least one argument of type StaticString is required"
case let .allArgumentsMustBeStringLiterals(violation):
"All arguments must be string literals, but found: \(violation)"
case .allArgumentsMustBeNonInterpolatedStringLiterals:
"All arguments must be non-interpolated string literals."
case let .renderedSyntaxContainsErrors(syntax):
"Rendered syntax contains errors:\n\(syntax)"
case let .couldNotFindLocationOfNode(syntax):
Expand Down
45 changes: 42 additions & 3 deletions Tests/EnumeratorMacroTests/EnumeratorMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -404,13 +404,52 @@ final class EnumeratorMacroTests: XCTestCase {
diagnostics: [.init(
id: .init(
domain: "EnumeratorMacro.MacroError",
id: "allArgumentsMustBeStringLiterals"
id: "allArgumentsMustBeNonInterpolatedStringLiterals"
),
message: """
All arguments must be string literals, but found: myVariable
All arguments must be non-interpolated string literals.
""",
line: 1,
column: 1,
column: 13,
severity: .error
)],
macros: EnumeratorMacroEntryPoint.macros
)
}

func testDiagnosesStringInterpolationInMustacheTemplate() throws {
assertMacroExpansion(
#"""
@Enumerator("""
enum Subtype: String {
{{#cases}}
case \(name)
{{/cases}}
}
""")
enum TestEnum {
case a(val1: String, Int)
case b
case testCase(testValue: String)
}
"""#,
expandedSource: #"""
enum TestEnum {
case a(val1: String, Int)
case b
case testCase(testValue: String)
}
"""#,
diagnostics: [.init(
id: .init(
domain: "EnumeratorMacro.MacroError",
id: "allArgumentsMustBeNonInterpolatedStringLiterals"
),
message: """
All arguments must be non-interpolated string literals.
""",
line: 4,
column: 10,
severity: .error
)],
macros: EnumeratorMacroEntryPoint.macros
Expand Down

0 comments on commit a733432

Please sign in to comment.