Skip to content

Commit

Permalink
Fix ParsableMacro optional argument decoding (#62)
Browse files Browse the repository at this point in the history
Fixes a bug in the ParsableMacro decoding logic that prevented parsing
past an optional argument.
  • Loading branch information
rauhul authored Jan 5, 2024
1 parent 1aa2b3b commit 14fd664
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,17 @@ extension ParsableMacro {
// matched, then parse into the same child, e.g. a variadic argument.
try updateChildAndIncrementExpressionIndex()
} else if expression.label?.text != nil, previousLabelMatched {
// If the expression has no label and the previous expression's label
// If the expression has a label and the previous expression's label
// matched, then we need to move to the next child and attempt to match
// it's label.
previousLabelMatched = false
children.formIndex(after: &childIndex)
} else if child.isParsed {
// If the expression has a label and does not match the current child's
// label and child has already been parsed, then we need to move to the
// next child and attempt to match it's label.
previousLabelMatched = false
children.formIndex(after: &childIndex)
} else {
throw context.error(
at: expression,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ final class ParsableMacroTests: XCTestCase {
""",
diagnostics: [
.init(
message: ErrorDiagnostic<A>.unexpectedArgumentLabel(expected: "foo", actual: "bar").message,
message: ErrorDiagnostic<A>.unexpectedExtraArgument(label: "bar").message,
line: 1,
column: 4,
highlight: "bar: 1")
Expand All @@ -251,15 +251,94 @@ final class ParsableMacroTests: XCTestCase {
highlight: "baz: 1")
],
macros: ["A": A.self])
}

func test_twoArgumentOptional_parse() {
struct A: MMIOArgumentParsingMacro {
@Argument(label: "foo")
var foo: Int?

@Argument(label: "bar")
var bar: Int?

mutating func update(
label: String,
from expression: ExprSyntax,
in context: MacroContext<some ParsableMacro, some MacroExpansionContext>
) throws {
switch label {
case "foo":
try self._foo.update(from: expression, in: context)
case "bar":
try self._bar.update(from: expression, in: context)
default:
fatalError()
}
}
}

// Good...
assertMacroExpansion(
"""
@A struct S {}
""",
expandedSource: """
struct S {}
""",
macros: ["A": A.self])

assertMacroExpansion(
"""
@A() struct S {}
""",
expandedSource: """
struct S {}
""",
macros: ["A": A.self])

// FIXME: Add test two optional arguments, only specify 2nd one
// struct A: ParsableMacro {
// @Argument(label: "foo")
// var bar: Int?
// @Argument(label: "baz")
// var baz: Int?
// }
// test that @A(baz: 1) works
assertMacroExpansion(
"""
@A(foo: 1) struct S {}
""",
expandedSource: """
struct S {}
""",
macros: ["A": A.self])

assertMacroExpansion(
"""
@A(bar: 1) struct S {}
""",
expandedSource: """
struct S {}
""",
macros: ["A": A.self])

assertMacroExpansion(
"""
@A(foo: 1, bar: 1) struct S {}
""",
expandedSource: """
struct S {}
""",
macros: ["A": A.self])

// Bad...
assertMacroExpansion(
"""
@A(foo: 1, baz: 1) struct S {}
""",
expandedSource: """
struct S {}
""",
diagnostics: [
.init(
message: ErrorDiagnostic<A>.unexpectedExtraArgument(label: "baz").message,
line: 1,
column: 12,
highlight: "baz: 1")
],
macros: ["A": A.self])
}

func test_oneArgumentArray_parse() {
Expand Down Expand Up @@ -440,7 +519,7 @@ final class ParsableMacroTests: XCTestCase {
""",
diagnostics: [
.init(
message: ErrorDiagnostic<A>.unexpectedArgumentLabel(expected: "bar", actual: "baz").message,
message: ErrorDiagnostic<A>.unexpectedExtraArgument(label: "baz").message,
line: 1,
column: 12,
highlight: "baz: 1")
Expand Down

0 comments on commit 14fd664

Please sign in to comment.