Share
Embed
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
Transcript
100 slides
-
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 ࣍ -
• 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 -
• 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 -
• 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 -
• 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 -
એݴϚΫϩʢ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 -
Swift Macro Overview SwiftϚΫϩͷ֓ཁ func f(a: Int, _: Double, c:
Int) { print(#function) } -
func f(a: Int, _: Double, c: Int) { print(#function) //
=> f(a:_:c:) } Swift Macro Overview SwiftϚΫϩͷ֓ཁ -
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:)") } -
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) -
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 -
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 -
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: _, _, _) ... } -
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: _, _, _)”) -
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 -
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 -
How to implement macros #colorLiteral() let color = #colorLiteral(red: 0.5,
green: 0.5, blue: 0.25, alpha: 1.0) -
How to implement macros #colorLiteral() @expression public macro colorLiteral( red:
Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro") -
How to implement macros #colorLiteral() @expression public macro colorLiteral( red:
Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro") -
How to implement macros #colorLiteral() @expression public macro colorLiteral( red:
Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro") -
How to implement macros #colorLiteral() @expression public macro colorLiteral( red:
Float, green: Float, blue: Float, alpha: Float ) -> _ColorLiteralType = #externalMacro(module: "MacroExamplesPlugin", type: "ColorLiteralMacro") -
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 } } -
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 } } -
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 } } -
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 } } -
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 } } -
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 } } -
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 } } -
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 } -
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)) ) } -
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)) ) } -
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)) ) } -
How to implement macros #printArguments() func doSomething(_ a: Int, b:
Int, c d: Int, e _: Int, _: Int, _ _: Int) { #printArguments() ... } -
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))\")" } } -
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 ... } } -
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 ... } } -
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 ... } } -
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))\")" } } -
Swift macro essentials • Type safe • Syntactic transformations •
Run at build time • Additional checks at build time • Sandboxed -
Swift macro essentials • Type safe • Syntactic transformations •
Run at build time • Additional checks at build time • Sandboxed -
Swift macro essentials if let leadingTrivia = macro.leadingTrivia { return
initSyntax.withLeadingTrivia(leadingTrivia) } return initSyntax -
Swift macro essentials • Type safe • Syntactic transformations •
Run at build time • Additional checks at build time • Sandboxed -
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 -
#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 ) ) ) } ... } -
Swift macro essentials • Type safe • Syntactic transformations •
Run at build time • Additional checks at build time • Sandboxed -
Sandboxed Embed binaries in apps without bundles let data =
#embed("images/icon.png") let data = Data([0x4d, 0x49, 0x54, ..., 0x65, 0x6e, 0x73]) -
Sandboxed Can I access the fi le system from macros?
https://forums.swift.org/t/pitch-2-expression-macros/61861/9 -
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 -
Things not suited for macro • Use functions in preference
to macros • Use Result Builders and Property Wrappers • Macros are code generators Pros and cons -
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)) } } -
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) } -
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)))") } -
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 { } -
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) -
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) -
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() -
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 -
Why Power Assert? That's all you need @expression public macro
powerAssert(_ expression: @autoclosure () throws -> Bool) -
Implement Power Assert with macros #powerAssert(a * b == 91)
TupleExprElementList Inspect the macro syntax structure -
Implement Power Assert with macros #powerAssert(a * b == 91)
TupleExprElementList Identi fi erExpr Inspect the macro syntax structure -
Implement Power Assert with macros #powerAssert(a * b == 91)
TupleExprElementList Identi fi erExpr BinaryOperatorExpr Inspect the macro syntax structure -
Implement Power Assert with macros #powerAssert(a * b == 91)
TupleExprElementList Identi fi erExpr BinaryOperatorExpr IntegerLiteralExpr Inspect the macro syntax structure -
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() -
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() -
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() -
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() -
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() -
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 -
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) -
Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) ==
"test") FunctionCallExpr Identi fi erExpr MemberAccessExpr -
Implement Power Assert with macros #powerAssert(String(data: data, encoding: .utf8) ==
"test") FunctionCallExpr Identi fi erExpr MemberAccessExpr -
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() -
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() -
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 -
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! -
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! -
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