SwiftSyntax で便利を実現する基礎 / How to Use SwiftSyntax for Better Productivity (Japanese version)

151a0b14f5914e786e2e104cfb3a9b2f?s=47 Kuniwak
March 22, 2019

SwiftSyntax で便利を実現する基礎 / How to Use SwiftSyntax for Better Productivity (Japanese version)

SwiftSyntax を使うと、Swift のコード生成や書き換え、静的検査などを実現できます。しかし、SwiftSyntax の使い方はほとんど知られていません。この方法をダイジェストで紹介します。

try! Swift Tokyo 2019
https://www.tryswift.co/events/2019/tokyo/jp/

151a0b14f5914e786e2e104cfb3a9b2f?s=128

Kuniwak

March 22, 2019
Tweet

Transcript

  1. 4XJGU4ZOUBYͰ
 ศརΛ࣮ݱ͢Δجૅ Kuniwak - DeNA Co.,Ltd. 2019.03.22 try! Swift Tokyo

    2019
  2. 2 Some code will be presented in the slides,
 you

    can see the slides and the code here: ൃදதʹίʔυ͕ग़͖ͯ·͕͢ɺಡΈͮΒ͍৔߹͸
 ҎԼ͔ΒεϥΠυΛ͝ཡ͍ͩ͘͞ɿ speakerdeck.com/orgachem
  3. "CPVUNF

  4. ,VOJXBL w ॴଐˠ • github.com/Kuniwak • qiita.com/Kuniwak w ߏจղੳ΍୯ମςετ͕޷෺ w

    "QQ$PEF࢖͍ 4
  5. ෼ؒͰ఻͍͑ͨ͜ͱ

  6. 4XJGU4ZOUBYΛ࢖͏ͱɺ4XJGUͷ
 ੩తݕࠪ΍ίʔυੜ੒Λ؆୯ʹͰ͖Δʂ 6

  7. 4XJGU4ZOUBYͱ͸

  8. 4XJGU4ZOUBY͸4XJGUͷެࣜϥΠϒϥϦ github.com/apple/swift-syntax 8

  9. 4XJGU4ZOUBYͰ
 Ͱ͖Δ͜ͱ

  10. ಡΈॻ͖͠΍͍͢σʔλ ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ ಡΈॻ͖͠΍͍͢σʔλΛɺίʔυʹ͢Δ   4XJGUͷίʔυ ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ 10

  11. ಡΈॻ͖͠΍͍͢σʔλ ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ ಡΈॻ͖͠΍͍͢σʔλΛɺίʔυʹ͢Δ   4XJGUͷίʔυ ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ 11

  12. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} 12 આ໌ʹ࢖͏αϯϓϧίʔυ

  13. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} JGจ 13

    ͜Ε͸ʮJGจʯͱݺ͹ΕΔ
  14. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} JGจ ෳจ ৚݅ 14 JGจ͸ʮ৚݅ʯͱʮෳจʯͰ
 ߏ੒͞Ε͍ͯΔ
  15. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} JGจ ෳจ 0QUJPOଋറ৚݅ 15 ࠓճͷ৚݅෦෼ʹ͸ɺ
 ʮ0QUJPOଋറ৚݅ʯͱ
 ݺ͹Ε͍ͯΔ΋ͷ͕ೖΔ
  16. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} JGจ ෳจ 0QUJPOଋറ৚݅ ม਺ࣜ ม਺ύλʔϯ if let self = self {} 16 0QUJPOଋറ৚݅͸ɺ୅ೖઌͷ
 ʮม਺ύλʔϯʯͱ୅ೖ͢Δ஋ͷ
 ʮม਺ࣜʯͰߏ੒͞Ε͍ͯΔ
  17. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} if let self = self {} 17 ͜ͷΑ͏ʹɺίʔυΛ෼ղͯ͠
 ߏ੒ཁૉΛḷ͍ͬͯ͘ͱɺ
 ίʔυΛʮ໦ߏ଄ʯͱͯ͠
 ѻ͍ͬͯΔ͜ͱʹؾͮ͘
  18. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} if let self = self {} 18 ෳจ JGจ ෳจ ม਺ύλʔϯ ม਺ࣜ 0QUJPOଋറ৚݅
  19. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ  if let self = self {} if let

    self = self {} JGจ ෳจ 0QUJPOଋറ ม਺ࣜ ม਺ύλʔϯ if let self = self {} JGจ ෳจ ม਺ύλʔϯ ม਺ࣜ ͜ΕΛʮߏจ໦ʯͱΑͿ 19 0QUJPOଋറ৚݅
  20. 4XJGU4ZOUBYͰ
 ʮߏจ໦ʯΛಡΉ

  21. 4XJGU4ZUOBYͰߏจ໦ΛಡΉ  if let self = self {} if let

    self = self {} JGจ ෳจ 0QUJPOଋറ ม਺ࣜ ม਺ύλʔϯ if let self = self {} ม਺໊ΛಡΜͰΈΑ͏ 21
  22. import SwiftSyntax let url = URL(fileURLWithPath: "example.swift") let source =

    try String(contentsOf: url) let syntax = try SyntaxParser.parse(source: source) 4XJGU4ZOUBYͰ ಡΈॻ͖͠΍͍͢σʔλʢߏจ໦ʣʹ͢Δ 4XJGUͷίʔυΛ 22
  23. if let self = self {} if let self =

    self {} JGจ ෳจ 0QUJPOଋറ ม਺ࣜ ม਺ύλʔϯ if let self = self {} ifStmt 4XJGU4ZUOBYͰߏจ໦ΛಡΉ  JGจ·ͰͷऔΓํ͸
 গ͠໘౗ͳͷͰޙड़ 23
  24. if let self = self {} if let self =

    self {} JGจ ෳจ 0QUJPOଋറ ม਺ࣜ ม਺ύλʔϯ if let self = self {} ifStmt .conditions[0] .condition 
 4XJGU4ZUOBYͰߏจ໦ΛಡΉ  24
  25. if let self = self {} if let self =

    self {} JGจ ෳจ 0QUJPOଋറ ม਺ࣜ ม਺ύλʔϯ if let self = self {} "self" ifStmt .conditions[0] .condition .pattern
 .identifier .text 4XJGU4ZUOBYͰߏจ໦ΛಡΉ  25
  26. let name = ifStmt .conditions[0] .condition .pattern
 .identifier .text if

    name == "this" { print("Oops!") } ྫ͑͹ɺif let self = ͷม਺໊͕
 ౷Ұ͞Ε͍ͯͳ͍৔߹ʹܯࠂͰ͖Δ ߏจ໦ΛಡΊΕ͹ɺΦϨΦϨ4XJGU-JOU͕ॻ͚Δ  26
  27. import SwiftSyntax let url = URL(fileURLWithPath: "example.swift") let source =

    try String(contentsOf: url) let syntax = try SyntaxParser.parse(source: source)
 let stmts = Array(syntax.statements)
 guard let ifStmt = stmts.first?.item as? IfStmtSyntax else { print("If statement not found at the position.") return } JGจ͸গ͠ख͕ؒͩ͜ͷΑ͏ʹऔΕΔɻ࣮ࡍͷߏจ໦ΛEVNQ͢ΔͱΘ͔Δ JGจ·ͰͷऔΓํ 27 ൃ ද Ͱ ͸ ল ུ
  28. ಡΈॻ͖͠΍͍͢ߏจ໦ ίʔυΛɺಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ   4XJGUͷίʔυ ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ DONE 28

  29. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ   4XJGUͷίʔυ 4XJGUͷίʔυ ಡΈॻ͖͠΍͍͢ߏจ໦ ಡΈॻ͖͠΍͍͢ߏจ໦ DONE 29

  30. if let this = self {} 4XJGU4ZUOBYͰमਖ਼ͨ͠ίʔυΛੜ੒͢Δ  if let

    self = self {} 30
  31. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) 31
  32. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) 4XJGU4ZOUBYΛ࢖͏ 32
  33. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) 4XJGU4ZOUBYͰಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ 33
  34. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) ߏจ໦Λॻ͖׵͑ͯɺ݁ՌΛදࣔ͢Δ 34
  35. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) ߏจ໦Λॻ͖׵͑ΔͨΊͷΫϥε 35
  36. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) ॻ͖׵͍͑ͨߏจ໦ͷWJTJUؔ਺ΛPWFSSJEF͢Δ ࠓճ͸JGMFU9ͷ9෦෼͕ର৅
 ʢܕͷ໊લ͸EVNQͰௐ΂ΔͱΘ͔Δʣ 36
  37. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) JGMFU9ͷ9෦෼͕UIJTͳΒॻ͖׵͑Δ 37
  38. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) 9෦෼ΛTFMGʹॻ͖׵͑Δ 38
  39. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) 39
  40. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) "if let self= self {}" 40 ίʔυΛੜ੒Ͱ͖ͨʂ
  41. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) "if let self= self {}" 41 Α͘ݟΔͱۭന͕
 ফ͑ͯ͠·͍ͬͯΔ
  42. import SwiftSyntax class RenameRewriter: SyntaxRewriter { override func visit(_ node:

    IdentifierPatternSyntax) -> PatternSyntax { if node.identifier.text == "this" { var modifiedNode = node let originalIdentifier = node.identifier modifiedNode.identifier = SyntaxFactory .makeIdentifier("self") .withTrailingTrivia(originalIdentifier.trailingTrivia) return modifiedNode } return node } } let syntax = try SyntaxParser.parse(source: String(contentsOf: url)) print(RenameRewriter().visit(syntax)) TFMGޙͷۭന͕ͳ͘ͳΔ໰୊͸5SJWJBͰௐ੔Ͱ͖Δ ൃ ද Ͱ ͸ ল ུ 42
  43. if let this = self {} 4XJGU4ZUOBYͰमਖ਼ͨ͠ίʔυΛੜ੒͢Δ  if let

    self = self {} 43 DONE
  44. ίʔυΛɺಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ   4XJGUͷίʔυ 4XJGUͷίʔυ ಡΈॻ͖͠΍͍͢ߏจ໦ ಡΈॻ͖͠΍͍͢ߏจ໦ DONE 44

    DONE
  45. 4XJGU4ZOUBYΛ࢖͏ͱɺ
 4XJGUͷߏจ໦ͷݕࠪ΍
 मਖ਼ͨ͠ίʔυΛੜ੒Ͱ͖ͨʂ ·ͱΊ 45