Slide 1

Slide 1 text

How to Use 4XJGU4ZOUBY
 for Better Productivity Kuniwak - DeNA Co., Ltd. March 22, 2019 - try! Swift Tokyo 2019

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

About me

Slide 4

Slide 4 text

,VOJXBL • Software Engineer in Test • github.com/Kuniwak • qiita.com/Kuniwak • Favorites: testing/parsing • IDE: AppCode 4

Slide 5

Slide 5 text

What I want to share
 in these 5 minutes

Slide 6

Slide 6 text

6TJOH4XJGU4ZOUBY we can easily do Linting and Code Generation 6

Slide 7

Slide 7 text

What's SwiftSyntax?

Slide 8

Slide 8 text

4XJGU4ZOUBYis a library developed by Apple github.com/apple/swift-syntax 8

Slide 9

Slide 9 text

2 things Swift- Syntax can do

Slide 10

Slide 10 text

Readable/writable Data Convert Readable/Writable data into code Source Code Readable/writable Data 10 Source Code Convert code into Readable/Writable data

Slide 11

Slide 11 text

Readable/writable Data Convert Readable/Writable data into code Source Code Readable/writable Data 11 Source Code Convert code into Readable/Writable data

Slide 12

Slide 12 text

Convert code into Readable/Writable data if let self = self {} 12 This is an example code to explain

Slide 13

Slide 13 text

if let self = self {} If statement 13 This is an "if" statement Convert code into Readable/Writable data

Slide 14

Slide 14 text

if let self = self {} if let self = self {} If statement Body Condition 14 This if statements has
 2 components Convert code into Readable/Writable data

Slide 15

Slide 15 text

if let self = self {} if let self = self {} If statement Body Option binding condition 15 In this case, the Condition is
 the "Option binding condition" Convert code into Readable/Writable data

Slide 16

Slide 16 text

if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} 16 This option binding condition has
 2 components: "Identifier pattern" and
 "Identifier expression" Convert code into Readable/Writable data

Slide 17

Slide 17 text

if let self = self {} if let self = self {} if let self = self {} 17 If we look at the components,
 we can see we're traversing
 a tree through our code Convert code into Readable/Writable data

Slide 18

Slide 18 text

if let self = self {} if let self = self {} if let self = self {} 18 ෳจ If statement Body Identifier pattern Identifier expression Option binding condition Convert code into Readable/Writable data

Slide 19

Slide 19 text

19 if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} If statement Body Identifier pattern Identifier expression Option binding condition Convert code into Readable/Writable data This is cleed a "Syntax Tree"

Slide 20

Slide 20 text

Reading a Syntax Tree
 using SwiftSyntax

Slide 21

Slide 21 text

Reading a Syntax Tree using 4XJGU4ZOUBY if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} Let's read its name! 21

Slide 22

Slide 22 text

import SwiftSyntax let url = URL(fileURLWithPath: "example.swift") let source = try String(contentsOf: url) let syntax = try SyntaxParser.parse(source: source) Importing SwiftSyntax, creating the syntax tree that we need for reading the code and 22

Slide 23

Slide 23 text

if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} ifStmt Getting the if statement is
 bitty complicated. Thus it'll
 be explained later 23 Reading a Syntax Tree using 4XJGU4ZOUBY

Slide 24

Slide 24 text

if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} ifStmt .conditions[0] .condition 
 24 Reading a Syntax Tree using 4XJGU4ZOUBY

Slide 25

Slide 25 text

if let self = self {} if let self = self {} If statement Body Option binding condition Identifier
 expression Identifier
 pattern if let self = self {} "self" ifStmt .conditions[0] .condition .pattern
 .identifier .text 25 Reading a Syntax Tree using 4XJGU4ZOUBY

Slide 26

Slide 26 text

let name = ifStmt .conditions[0] .condition .pattern
 .identifier .text if name == "this" { print("Oops!") } For example, this is a Lint to
 check whether variable names
 follow your guideline Now, we can implement our Lint easily 26

Slide 27

Slide 27 text

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 } You can see how to get the if statement using dump() the syntax tree How to get the If statement 27 Skipped in 
 presentation 


Slide 28

Slide 28 text

Readable/writable Data Convert Readable/Writable data into code Convert code into Readable/Writable data Source Code Readable/writable Data 28 Source Code DONE

Slide 29

Slide 29 text

Readable/writable Data Convert Readable/Writable data into code Convert code into Readable/Writable data Source Code Readable/writable Data 29 Source Code DONE

Slide 30

Slide 30 text

if let this = self {} Generating fixed code using 4XJGU4ZOUBY if let self = self {} 30

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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)) Use SwiftSyntax 32

Slide 33

Slide 33 text

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)) Get a syntax tree using SwiftSyntax 33

Slide 34

Slide 34 text

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)) Rewrite the syntax tree and print it 34

Slide 35

Slide 35 text

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)) A class for rewriting syntax trees 35

Slide 36

Slide 36 text

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)) Use the override method to rewrite the node In this case, we want to rewrite the 9in "if let 9 = ..."
 (we can see the type of 9 by using dump) 36

Slide 37

Slide 37 text

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)) Rewrite the syntax tree if it says "if let this= ..." 37

Slide 38

Slide 38 text

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)) Overwrite with "self" 38

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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 We got the fixed code!

Slide 41

Slide 41 text

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 But a space after "self" is missing...

Slide 42

Slide 42 text

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)) We can restore the space by using Trivias Skipped in 
 presentation 
 42

Slide 43

Slide 43 text

if let this = self {} if let self = self {} 43 DONE Generating fixed code using 4XJGU4ZOUBY

Slide 44

Slide 44 text

Readable/writable Data Convert Readable/Writable data into code Convert code into Readable/Writable data Source Code Readable/writable Data 44 Source Code DONE DONE

Slide 45

Slide 45 text

We implemented a Lint and Code Generator by using 4XJGU4ZOUBY! Conclusion 45