Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Expression Macros: a practical introduction

Swift Expression Macros: a practical introduction

Swift Expression Macros:
a practical introduction
-実践 Swiftマクロ入門-

Macro proposal timeline
Swift macro overview
How to implement macros
Swift macro essentials
Pros and cons
Various kinds of macros
Declaration macros
Practical Example: Power Assert
Summary

Kishikawa Katsumi

January 21, 2023
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. Agenda • Macro proposal timeline • Swift macro overview •

    How to implement macros • Swift macro essentials • Pros and cons • Various kinds of macros • Declaration macros • Practical Example: Power Assert • Summary ໨࣍
  2. • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900
 https://gist.github.com/DougGregor/4f3ba5f4eadac474ae62eae836328b71

    • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ Macro proposal timeline A Possible Vision for Macros in Swift
  3. • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 •

    Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ ➡[Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline
  4. • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 •

    Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 ➡[Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline
  5. • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900 •

    Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 ➡SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 ϚΫϩʢExpression Macros/ࣜϚΫϩʣͷػೳ͕ঝೝ͞ΕΔ·Ͱ Macro proposal timeline
  6. એݴϚΫϩʢDeclaration Macrosʣ • [Pitch] Declaration macros
 https://forums.swift.org/t/pitch-declaration-macros/62373/17 • Declaration Macros


    https://github.com/DougGregor/swift-evolution/blob/declaration-macros/proposals/nnnn-declaration-macros.md Macro proposal timeline
  7. func f(a: Int, _: Double, c: Int) { print(#function) //

    => f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ
  8. func f(a: Int, _: Double, c: Int) { print(#function) //

    => f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ func f(a: Int, _: Double, c: Int) { print("f(a:_:c:)") }
  9. Swift Macro Overview SwiftϚΫϩͷ֓ཁ let color = #colorLiteral(red: 0.5, green:

    0.5, blue: 0.25, alpha: 1.0) let color = .init(_colorLiteralRed: 0.5, green: 0.5, blue: 0.25, alpha: 1.0)
  10. func doSomething(_ a: Int, b: Int, c d: Int, e

    _: Int, _: Int, _ _: Int) { #printArguments() ... } Swift Macro Overview SwiftϚΫϩͷ֓ཁ https://forums.swift.org/t/se-0382-expression-macros/62090/9
  11. func doSomething(_ a: Int, b: Int, c d: Int, e

    _: Int, _: Int, _ _: Int) { #printArguments() // => doSomething(42, b: 256, c: 512, e: _, _, _) ... } Swift Macro Overview SwiftϚΫϩͷ֓ཁ https://forums.swift.org/t/se-0382-expression-macros/62090/9
  12. Swift Macro Overview SwiftϚΫϩͷ֓ཁ func doSomething(_ a: Int, b: Int,

    c d: Int, e _: Int, _: Int, _ _: Int) { print("doSomething(\(a), b: \(b), c: \(d), e: _, _, _)") ... } https://forums.swift.org/t/se-0382-expression-macros/62090/9 func doSomething(_ a: Int, b: Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() // => doSomething(42, b: 256, c: 512, e: _, _, _) ... }
  13. Swift Macro Overview SwiftϚΫϩͷ֓ཁ #function "f(a:_:c:)" #colorLiteral( red: 0.5, green:

    0.5, blue: 0.25, alpha: 1.0 ) .init( _colorLiteralRed: 0.5, green: 0.5, blue: 0.25, alpha: 1.0 ) #printArguments() print("doSomething(\(a), b: \(b), c: \(d), e: _, _, _)”)
  14. First impressions about macros Pros and cons • The code

    might be harder to read • Macros are hard to imagine what they do • Incorrect conversions cause unintended behavior • Macros are hard to debug
  15. First impressions about macros Pros and cons • The code

    might be harder to read • Macros are hard to imagine what they do • Incorrect conversions cause unintended behavior • Macros are hard to debug • Macro can eliminate boilerplate
  16. How to implement macros #colorLiteral() @expression public macro colorLiteral( red:

    Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")
  17. How to implement macros #colorLiteral() @expression public macro colorLiteral( red:

    Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")
  18. How to implement macros #colorLiteral() @expression public macro colorLiteral( red:

    Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")
  19. How to implement macros #colorLiteral() @expression public macro colorLiteral( red:

    Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro")
  20. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  21. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  22. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  23. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  24. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  25. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  26. How to implement macros #colorLiteral() import SwiftSyntax import _SwiftSyntaxMacros public

    struct ColorLiteralMacro: ExpressionMacro { public static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax } }
  27. Swift Syntax public static func expansion( of macro: MacroExpansionExprSyntax, in

    context: inout MacroExpansionContext ) -> ExprSyntax { let argList = replaceFirstLabel( of: macro.argumentList, with: "_colorLiteralRed" ) let initSyntax: ExprSyntax = ".init(\(argList))" if let leadingTrivia = macro.leadingTrivia { return initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax }
  28. How to implement macros #colorLiteral() /// Replace the label of

    the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }
  29. How to implement macros #colorLiteral() /// Replace the label of

    the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }
  30. How to implement macros #colorLiteral() /// Replace the label of

    the fi rst element in the tuple with the given /// new label. private func replaceFirstLabel( of tuple: TupleExprElementListSyntax, with newLabel: String ) -> TupleExprElementListSyntax { guard let firstElement = tuple.first else { return tuple } return tuple.replacing( childAt: 0, with: firstElement.withLabel(.identifier(newLabel)) ) }
  31. How to implement macros #printArguments() func doSomething(_ a: Int, b:

    Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() ... }
  32. How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro {

    public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList let parameters = parameterList.map { parameter -> String in let potentialLabel = parameter.firstName!.withoutTrivia().description let label = potentialLabel == "_" ? nil : potentialLabel let potentialName = parameter.secondName?.withoutTrivia().description ?? potentialLabel let name = potentialName == "_" ? nil : potentialName var string: String if let label { string = "\(label): " } else { string = "" } if let name { string += "\\(\(name))" } else { string += "_" } return string } let parametersString = parameters.joined(separator: ", ") return "print(\"\(raw: functionSyntax.identifier.description)(\(raw: parametersString))\")" } }
  33. How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro {

    public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }
  34. How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro {

    public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }
  35. How to implement macros #printArguments() public struct PrintArgumentsMacro: ExpressionMacro {

    public static func expansion( of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { var syntax = node.as(Syntax.self) while syntax != nil && syntax!.as(FunctionDeclSyntax.self) == nil { syntax = syntax!.parent } guard let functionSyntax = syntax!.as(FunctionDeclSyntax.self) else { return "" } let signature: FunctionSignatureSyntax = functionSyntax.signature let parameterList = signature.input.parameterList ... } }
  36. public struct PrintArgumentsMacro: ExpressionMacro { public static func expansion( of

    node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) -> ExprSyntax { ... let parameters = parameterList.map { parameter -> String in let potentialLabel = parameter.firstName!.withoutTrivia().description let label = potentialLabel == "_" ? nil : potentialLabel let potentialName = parameter.secondName?.withoutTrivia().description ?? potentialLabel let name = potentialName == "_" ? nil : potentialName var string: String if let label { string = "\(label): " } else { string = "" } if let name { string += "\\(\(name))" } else { string += "_" } return string } let parametersString = parameters.joined(separator: ", ") return "print(\"\(raw: functionSyntax.identifier.description)(\(raw: parametersString))\")" } }
  37. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  38. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  39. Swift macro essentials if let leadingTrivia = macro.leadingTrivia { return

    initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax
  40. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  41. Additional checks at build time let homepage = #url("https://tryswift.jp/") @expression

    public macro url(_ string: String) -> URL = ... @expression public macro sql(_ string: String) -> SQLQuery = ... let query = #sql("SELECT * FROM ...") More ideas
  42. #URL() Compile error if argument is an invalid URL public

    static func expansion( of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext ) throws -> ExprSyntax { ... if !validate(macro.argumentList.first) { context.diagnose( Diagnostic( node: Syntax(macro), message: SimpleDiagnosticMessage( message: "...", diagnosticID: MessageID(domain: "...", id: "..."), severity: .error ) ) ) } ... }
  43. Swift macro essentials • Type safe • Syntactic transformations •

    Run at build time • Additional checks at build time • Sandboxed
  44. Sandboxed Embed binaries in apps without bundles let data =

    #embed("images/icon.png") let data = Data([0x4d, 0x49, 0x54, ..., 0x65, 0x6e, 0x73])
  45. Sandboxed Can I access the fi le system from macros?

    https://forums.swift.org/t/pitch-2-expression-macros/61861/9
  46. Pros and cons 👍 Macros are type safe 👍 Macros

    can extend Swift 👍 Macros can cooperate with the build process 🤔 Shoot yourself in the foot with any language • Macros are hard to imagine what they do • Macros are hard to debug • Macros or Functions / Property Wrappers / Result Builders 🤔 Requires knowledge of Swift Syntax 🤔 Things not suited for macro
  47. Things not suited for macro • Use functions in preference

    to macros • Use Result Builders and Property Wrappers • Macros are code generators Pros and cons
  48. Various kinds of macros Declaration macros (Freestanding macros, Attached macros)

    @addCompletionHandler func fetchAvatar(_ username: String) async -> Image? { ... } @declaration(peers: [.overloaded]) macro addCompletionHandler: Void /// Expansion of the macro produces the following. func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void) { Task.detached { completionHandler(await fetchAvatar(username)) } }
  49. Various kinds of macros Declaration macros @optionSetMembers struct MyOptions: OptionSet

    { enum Option: Int { case a case b case c } } // Expands to... struct MyOptions: OptionSet { enum Option: Int { case a case b case c } // Synthesized code below... var rawValue: Int = 0 static var a = MyOptions(rawValue: 1 << Option.a.rawValue) static var b = MyOptions(rawValue: 1 << Option.b.rawValue) static var c = MyOptions(rawValue: 1 << Option.c.rawValue) }
  50. Various kinds of macros Declaration macros @traced(logLevel: 2) func myFunction(a:

    Int, b: Int) { ... } @declaration(body) macro traced(logLevel: Int = 0) if shouldLog(atLevel: 2) { log("Entering myFunction((a: \(a), b: \(b)))") }
  51. Various kinds of macros Declaration macros • Replace code generators

    with a macro @swiftCodeGen enum Asset { enum Images { static let banana = ImageAsset(value: "Exotic/Banana") static let mango = ImageAsset(value: "Exotic/Mango") } enum Colors { static let primary = ColorAsset(value: "Vengo/Primary") static let tint = ColorAsset(value: "Vengo/Tint") } enum Symbols { static let exclamationMark = SymbolAsset(name: "Exclamation Mark") static let plus = SymbolAsset(name: "Plus") } } @swiftCodeGen enum Asset { }
  52. What is Power Assert? let john = Person(name: "John", age:

    42) let mike = Person(name: "Mike", age: 13) #powerAssert(mike.isTeenager && john.age < mike.age)
  53. What is Power Assert? #powerAssert(mike.isTeenager && john.age < mike.age) |

    | | | | | | | | false | | 42 | | 13 | | | | Person(name: "Mike", age: 13) | | | false | | Person(name: "John", age: 42) | false Person(name: "Mike", age: 13) let john = Person(name: "John", age: 42) let mike = Person(name: "Mike", age: 13) #powerAssert(mike.isTeenager && john.age < mike.age)
  54. Why Power Assert? Many di ff erent assertion functions public

    func XCTAssert() public func XCTAssertEqual() public func XCTAssertEqualWithAccuracy() public func XCTAssertFalse() public func XCTAssertGreaterThan() public func XCTAssertGreaterThanOrEqual() public func XCTAssertIdentical() public func XCTAssertLessThan() public func XCTAssertLessThanOrEqual() public func XCTAssertNil() public func XCTAssertNoThrow() public func XCTAssertNotEqual() public func XCTAssertNotEqualWithAccuracy() public func XCTAssertNotIdentical() public func XCTAssertNotNil() public func XCTAssertThrowsError() public func XCTAssertTrue()
  55. What is Power Assert? • Only simple assert function such

    as powerAssert(a == b) is su ff i cient • Display easy-to-understand information on assert failure (when the test does not pass) • Bene fi t of not having to use many di ff erent assertion functions https://azu.github.io/slide/sakurajs/power-assert.html#/2
  56. Why Power Assert? That's all you need @expression public macro

    powerAssert(_ expression: @autoclosure () throws -> Bool)
  57. Implement Power Assert with macros #powerAssert(a * b == 91)

    TupleExprElementList Inspect the macro syntax structure
  58. Implement Power Assert with macros #powerAssert(a * b == 91)

    TupleExprElementList Identi fi erExpr Inspect the macro syntax structure
  59. Implement Power Assert with macros #powerAssert(a * b == 91)

    TupleExprElementList Identi fi erExpr BinaryOperatorExpr Inspect the macro syntax structure
  60. Implement Power Assert with macros #powerAssert(a * b == 91)

    TupleExprElementList Identi fi erExpr BinaryOperatorExpr IntegerLiteralExpr Inspect the macro syntax structure
  61. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()
  62. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()
  63. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()
  64. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()
  65. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render()
  66. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render() // => #powerAssert(a * b == 91) | | | | 10 9 | 91 false
  67. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(a * b == 91)"#,

    line: 2) .assert(a * b == 91) .capture(expression: a.self, column: 13) .capture(expression: b.self, column: 17) .capture(expression: a * b == 91, column: 19) .capture(expression: 91, column: 22) .render() // => #powerAssert(a * b == 91) | | | | 10 9 | 91 false #powerAssert(a * b == 91)
  68. Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) ==

    "test") FunctionCallExpr Identi fi erExpr MemberAccessExpr
  69. Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) ==

    "test") FunctionCallExpr Identi fi erExpr MemberAccessExpr
  70. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(String(data: data, encoding: .utf8) ==

    "test")"#, line: 2) .assert(String(data: data, encoding: .utf8) == "test") .capture(expression: String(data: data, encoding: .utf8), column: 13) .capture(expression: data.self, column: 26) .capture(expression: .utf8, column: 42) .capture(expression: String(data: data, encoding: .utf8) == "test", column: 49) .capture(expression: "test", column: 52) .render()
  71. Implement Power Assert with macros PowerAssert.Assertion(#"#powerAssert(String(data: data, encoding: .utf8) ==

    "test")"#, line: 2) .assert(String(data: data, encoding: .utf8) == "test") .capture(expression: String(data: data, encoding: .utf8), column: 13) .capture(expression: data.self, column: 26) .capture(expression: .utf8, column: 42) .capture(expression: String(data: data, encoding: .utf8) == "test", column: 49) .capture(expression: "test", column: 52) .render()
  72. Implement Power Assert with macros .capture(expression: data.self, column: 26) .capture(expression:

    .utf8, column: 42) .capture(expression: String(data: data, No type information in syntax tree
  73. Summary Macros are fun! • Macros are a very powerful

    feature that can extend Swift itself • Shoot yourself in the foot if used incorrectly • Swift Syntax becomes more important • Macros are hard to debug • Post your ideas!
  74. Summary Macros are fun! • Macros are a very powerful

    feature that can extend Swift itself • Shoot yourself in the foot if used incorrectly • Swift Syntax becomes more important • Macros are hard to debug • Post your ideas!
  75. References • A Possible Vision for Macros in Swift
 https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900

    • Swift project in 2023
 https://www.swift.org/blog/focus-areas-2023/ • [Pitch] Expression macros
 https://forums.swift.org/t/pitch-expression-macros/61499 • [Pitch #2] Expression macros
 https://forums.swift.org/t/pitch-2-expression-macros/61861 • SE-0382: Expression Macros
 https://forums.swift.org/t/se-0382-expression-macros/62090 • [Pitch] Declaration macros
 https://forums.swift.org/t/pitch-declaration-macros/62373/36 • 5෼͙Β͍ͰΘ͔Δpower assert
 https://azu.github.io/slide/sakurajs/power-assert.html