Share
Embed
Mastering SwiftSyntax
- Introduction
- SwiftSyntax Overview
- Anatomy of Syntax Tree
- SwiftSyntax API
- Exercises
- Swift Macros
- SwiftLint
- Source Editor Extensions
Kishikawa Katsumi
September 01, 2023
Transcript
204 slides
-
r SFBMN4XJGU-JOU IUUQTHJUIVCDPNSFBMN4XJGU-JOU r BQQMFTXJGUGPSNBU IUUQTHJUIVCDPNBQQMFTXJGUGPSNBU r LS[ZT[UPG[BCMPDLJ4PVSDFSZ IUUQTHJUIVCDPNLS[ZT[UPG[BCMPDLJ4PVSDFSZ r
VCFSNPDLPMP IUUQTHJUIVCDPNVCFSNPDLPMP 8IPVTF4XJGU4ZOUBY 4XJGU4ZOUBYͷར༻ྫ -
r 4XJGU͔Βಋೖ༧ఆ r ίϯύΠϧ࣌ʹίʔυͷੜΛͨ͠Γ ಠࣗͷݕࠪΛՃͰ͖Δ r ϚΫϩͷ࣮ʹ4XJGU4ZOUBYΛ༻ ͢Δ r ϚΫϩͷྫ#Previewɺ
@Observableɺ@ModelͳͲ 4XJGU.BDSPT 4XJGUϚΫϩ -
r *OUSPEVDUJPO r 4XJGU4ZOUBY0WFSWJFX r "OBUPNZPG4ZOUBY5SFF r 4XJGU4ZOUBY"1* r &YFSDJTFT
‣ 4XJGU.BDSPT ‣ 4XJGU-JOU ‣ 4PVSDF&EJUPS&YUFOTJPOT 0VUMJOF ࣍ -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let 4ZOUBY5SFF -
let sourceFile = Parser.parse(source: "let number = 100") print(sourceFile.debugDescription) import
SwiftParser 4XJGU1BSTFS 4PVSDF$PEFUP4ZOUBY5SFF -
SourceFileSyntax ├─statements: CodeBlockItemListSyntax │ ╰─[0]: CodeBlockItemSyntax │ ╰─item: VariableDeclSyntax │
├─attributes: AttributeListSyntax │ ├─modifiers: DeclModifierListSyntax │ ├─bindingSpecifier: keyword(SwiftSyntax.Keyword.let) │ ╰─bindings: PatternBindingListSyntax │ ╰─[0]: PatternBindingSyntax │ ├─pattern: IdentifierPatternSyntax │ │ ╰─identifier: identifier("number") │ ╰─initializer: InitializerClauseSyntax │ ├─equal: equal │ ╰─value: IntegerLiteralExprSyntax │ ╰─literal: integerLiteral("100") ╰─endOfFileToken: endOfFile let sourceFile = Parser.parse(source: "let number = 100") print(sourceFile.debugDescription) // 509.0.0 or later -
SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile
number = 100 let SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile
number = 100 let SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile
amount = 100 let SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
πϦʔߏΛԼํʹͨͲΔ SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │
├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken let sourceFile = Parser.parse(source: "let number = 100") -
SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers
│ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken let statements = sourceFile.statements SourceFileSyntax -
let statements = Array(sourceFile.statements) SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item
│ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] CodeBlockItemSyntax SourceFileSyntax
├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item
= codeBlock.item SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers
│ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item = codeBlock.item print(item.syntaxNodeType) print(item.syntaxNodeType) -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item
= codeBlock.item print(item.syntaxNodeType) SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken VariableDeclSyntax -
SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers
│ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item = codeBlock.item if let decl = item.as(VariableDeclSyntax.self) { } item.as(VariableDeclSyntax.self) -
SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers
│ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item = codeBlock.item if let decl = item.as(VariableDeclSyntax.self) { let bindings = Array(decl.bindings) } -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item
= codeBlock.item if let decl = item.as(VariableDeclSyntax.self) { let bindings = Array(decl.bindings) let binding = bindings[0] } SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item
= codeBlock.item if let decl = item.as(VariableDeclSyntax.self) { let bindings = Array(decl.bindings) let binding = bindings[0] if let pattern = binding.pattern.as( IdentifierPatternSyntax.self ) { } } SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
let statements = Array(sourceFile.statements) let codeBlock = statements[0] let item
= codeBlock.item if let decl = item.as(VariableDeclSyntax.self) { let bindings = Array(decl.bindings) let binding = bindings[0] if let pattern = binding.pattern.as( IdentifierPatternSyntax.self ) { let identifier = pattern.identifier } } SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken if let pattern = binding.pattern.as( IdentifierPatternSyntax.self ) { let identifier = pattern.identifier } -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
let number = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax
IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let -
if let number = number { } ؔͷҾ ϩʔΧϧม 0QUJPOBM#JOEJOH
func () { let number = 0 } CodeBlockItem PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax number VariableDeclSyntax OptionalBindingConditionSyntax let IdentifierPatternSyntax number ConditionElementSyntax func round(number: Int) { ... } FunctionParameterSyntax number FunctionParameterListSyntax πϦʔͷܗΛߏతʹϚονϯά͢Δ -
πϦʔߏΛ্ํʹͨͲΔ if let pattern = identifier.parent?.as( IdentifierPatternSyntax.self ) { if
let binding = pattern.parent?.as( PatternBindingSyntax.self ) { } } SourceFileSyntax ├─statements │ ╰─[0] │ ╰─item │ ├─attributes │ ├─modifiers │ ├─bindingSpecifier │ ╰─bindings │ ╰─[0] │ ├─pattern │ │ ╰─identifier: "number" │ ╰─initializer │ ├─equal: equal │ ╰─value │ ╰─literal: "100" ╰─endOfFileToken -
let result = decl.with( \.bindings, [ binding.with( \.pattern, PatternSyntax( pattern.with(
\.identifier, identifier.with( \.tokenKind, .identifier("amount") ) ) ) ) ] ) let number = 100 let amount = 100 -
let result = decl.with( \.bindings, [ binding.with( \.pattern, PatternSyntax( pattern.with(
\.identifier, identifier.with( \.tokenKind, .identifier("amount") ) ) ) ) ] ) let number = 100 let amount = 100 ݩͷཁૉ ݩͷཁૉ ݩͷཁૉ ͜ͷཁૉ͚ͩม͑Δ -
let /* let number defines a constant. */ number =
100 WT3FHVMBS&YQSFTTJPO /\s*let\s+(number)\s*/ -
let /* let number defines a constant. */ number =
100 WT3FHVMBS&YQSFTTJPO /\s*let\s+(number)\s*/ -
VariableDeclSyntax ├─attributes: AttributeListSyntax ├─modifiers: DeclModifierListSyntax ├─bindingSpecifier: keyword(SwiftSyntax.Keyword.let) ╰─bindings: PatternBindingListSyntax ╰─[0]:
PatternBindingSyntax ├─pattern: IdentifierPatternSyntax │ ╰─identifier: identifier("number") ╰─initializer: InitializerClauseSyntax ├─equal: equal ╰─value: IntegerLiteralExprSyntax ╰─literal: integerLiteral("100") let /* let number defines a constant. */ number = 100 print(sourceFile.debugDescription) -
VariableDeclSyntax ├─attributes: AttributeListSyntax ├─modifiers: DeclModifierListSyntax ├─bindingSpecifier: keyword(SwiftSyntax.Keyword.let) trailingTrivia=[spaces(1), blockComment("..."), spaces(1)]
╰─bindings: PatternBindingListSyntax ╰─[0]: PatternBindingSyntax ├─pattern: IdentifierPatternSyntax │ ╰─identifier: identifier("number") trailingTrivia=spaces(1) ╰─initializer: InitializerClauseSyntax ├─equal: equal trailingTrivia=spaces(1) ╰─value: IntegerLiteralExprSyntax ╰─literal: integerLiteral("100") print(sourceFile.debugDescription(includeTrivia: true)) let /* let number defines a constant. */ number = 100 -
VariableDeclSyntax ├─attributes: AttributeListSyntax ├─modifiers: DeclModifierListSyntax ├─bindingSpecifier: keyword(SwiftSyntax.Keyword.let) trailingTrivia=[spaces(1), blockComment("..."), spaces(1)]
╰─bindings: PatternBindingListSyntax ╰─[0]: PatternBindingSyntax ├─pattern: IdentifierPatternSyntax │ ╰─identifier: identifier("number") trailingTrivia=spaces(1) ╰─initializer: InitializerClauseSyntax ├─equal: equal trailingTrivia=spaces(1) ╰─value: IntegerLiteralExprSyntax ╰─literal: integerLiteral("100") print(sourceFile.debugDescription(includeTrivia: true)) let /* let number defines a constant. */ number = 100 -
VariableDeclSyntax ├─attributes: AttributeListSyntax ├─modifiers: DeclModifierListSyntax ├─bindingSpecifier: keyword(SwiftSyntax.Keyword.let) trailingTrivia=[spaces(1), blockComment("..."), spaces(1)]
╰─bindings: PatternBindingListSyntax ╰─[0]: PatternBindingSyntax ├─pattern: IdentifierPatternSyntax │ ╰─identifier: identifier("number") trailingTrivia=spaces(1) ╰─initializer: InitializerClauseSyntax ├─equal: equal trailingTrivia=spaces(1) ╰─value: IntegerLiteralExprSyntax ╰─literal: integerLiteral("100") print(sourceFile.debugDescription(includeTrivia: true)) let /* let number defines a constant. */ number = 100 -
@nonobjc public let number: Int = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax
VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let AttributeListSyntax @ IdentifierTypeSyntax nonobjc DeclModifierListSyntax DeclModifierSyntax public -
@nonobjc public let number: Int = 100 SourceFileSyntax CodeBlockItemListSyntax CodeBlockItemSyntax
VariableDeclSyntax PatternBindingListSyntax PatternBindingSyntax IdentifierPatternSyntax InitializerClauseSyntax IntegerLiteralExprSyntax endOfFile number = 100 let AttributeListSyntax @ IdentifierTypeSyntax nonobjc DeclModifierListSyntax DeclModifierSyntax public -
8BZTUP1SPEVDF4ZOUBY5SFF r 4XJGU1BSTFS r ϊʔυͷΠχγϟϥΠβ r 4ZOUBY#VJMEFS ˞ཁ4XJGU4ZOUBY#VJMEFS ‣ 3FTVMU#VJMEFS
‣ 4USJOH*OUFSQPMBUJPO Parser.parse(source: "let number = 100") let tree = VariableDeclSyntax( .let, name: PatternSyntax(stringLiteral: "number"), initializer: InitializerClauseSyntax( value: IntegerLiteralExprSyntax( integerLiteral: 100 ) ) ) tree.formatted() let tree: DeclSyntax = "let number = 100" -
4XJGU4ZOUBY0WFSWJFX r 1BSTFSιʔείʔυ͔Β4ZOUBY5SFFΛੜ͢Δ r 4ZOUBY5SFFιʔείʔυΛҰఆͷߏͱͯ͠ѻ͑Δ r ۭനվߦɺίϝϯτ5SJWJBͱͯ͠4ZOUBY5SFFΛߏ͢Δ r ಛఆͷมΘΒͳ͍ܗΛݟ͚ͭͯߏతʹϚονϯά͢Δ r
4XJGU4ZOUBYΛͬͯ4ZOUBY5SFFΛ෦తʹॻ͖͑ΒΕΔ -
SyntaxProtocol TokenSyntax Pr S DeclSynta S VariableDeclS S SourceFileSyntax S
5PLFO τʔΫϯ 4ZOUBY ߏจ -
if let decl = item.as(VariableDeclSyntax.self) { ... } if let
decl = item as? VariableDeclSyntax { } ϊʔυͷܕΛม͢Δ ܕมͷؔΛ͏ -
ϊʔυͷܕΛม͢Δ if let decl = item.as(VariableDeclSyntax.self) { // == `as?`
operator } if item.is(VariableDeclSyntax.self) { == `is` operator } let decl = item.cast(VariableDeclSyntax.self) // Force cast (== as!) let decl = VariableDeclSyntax(item) // == `as?` operator ܕมͷؔΛ͏ -
ϊʔυͷܕΛม͢Δ if let decl = item.as(VariableDeclSyntax.self) { // == `as?`
operator } if item.is(VariableDeclSyntax.self) { == `is` operator } let decl = item.cast(VariableDeclSyntax.self) // Force cast (== as!) let decl = VariableDeclSyntax(item) // == `as?` operator ܕมͷؔΛ͏ -
ϊʔυͷܕΛม͢Δ if let decl = item.as(VariableDeclSyntax.self) { // == `as?`
operator } if item.is(VariableDeclSyntax.self) { == `is` operator } let decl = item.cast(VariableDeclSyntax.self) // Force cast (== as!) let decl = VariableDeclSyntax(item) // == `as?` operator ܕมͷؔΛ͏ -
ϊʔυͷܕΛม͢Δ if let decl = item.as(VariableDeclSyntax.self) { // == `as?`
operator } if item.is(VariableDeclSyntax.self) { == `is` operator } let decl = item.cast(VariableDeclSyntax.self) // Force cast (== as!) let decl = VariableDeclSyntax(item) // == `as?` operator ܕมͷؔΛ͏ -
r 4ZOUBY5SFFͷ֤ϊʔυͳΜΒ͔ͷ4ZOUBY 4ZOUBY1SPUPDPM r ۩ମతͳܕΛௐΔ͜ͱͰߏจͷछྨ͕Θ͔Δ FH7BSJBCMF%FDM4ZOUBYɺ'VODUJPO$BMM&YQS4ZOUBYɺ'PS4UNU4ZOUBY r 4ZOUBYͷ࠷খ୯ҐτʔΫϯ 5PLFO4ZOUBY
r πϦʔߏΛԼํʹͨͲΔͱ͖ܕͷϓϩύςΟ͕͑Δ FHsourceFile.statementsɺpattern.identifier ˞ෳͷܕͷީิ͕͋Δ߹μϯΩϟετ͕ඞཁ r ্ํʹཁૉΛͨͲΔͱ͖దٓμϯΩϟετ͢Δ "OBUPNZPG4ZOUBY5SFF -
r એݴ ‣ มɺؔɺΫϥεͷએݴͳͲ ‣ $MBTT%FDM4ZOUBYɺ'VODUJPO%FDM4ZOUBYɺ7BSJBCMF%FDM4ZOUBYɺ r ࣜ Λฦ͢
‣ Λฦ͢ߏจɻؔݺͼग़͠ɺϓϩύςΟΞΫηεɺΫϩʔδϟͳͲ ‣ $MPTVSF&YQS4ZOUBYɺ'VODUJPO$BMM&YQS4ZOUBYɺ 4USJOH-JUFSBM&YQS4ZOUBYɺ r ͦͷଞ ‣ ܕΞϊςʔγϣϯͳͲ ‣ *EFOUJ fi FS5ZQF4ZOUBYɺ.FNCFS5ZQF4ZOUBYɺ0QUJPOBM5ZQF4ZOUBYɺ ͍Ζ͍Ζͳߏจϊʔυͷܕ -
␣␣␣␣let␣ number␣ =␣ 100↲ 5PLFO4ZOUBY var leadingTrivia: Trivia trailingTrivia trailingTrivia
trailingTrivia trailingTrivia -FBEJOH5SJWJB5SBJMJOH5SJWJB -
␣␣␣␣let␣ number␣ =␣ 100↲ 5PLFO4ZOUBY keyword(Keyword.let) endOfFile integerLiteral("100") identifier("number") let
firstToken = sourceFile.firstToken(viewMode: .sourceAccurate) let lastToken = sourceFile.lastToken(viewMode: .sourceAccurate) let previousToken = lastToken?.previousToken(viewMode: .sourceAccurate) let nextToken = firstToken?.nextToken(viewMode: .sourceAccurate) /BWJHBUF5PLFO4ZOUBY -
r 4ZOUBY7JTJUPS ‣ 4ZOUBY5SFFΛਂ͞༏ઌ୳ࡧ %'4 ͰͨͲΔ αϒΫϥεͰॲཧΛ͢ΔରͷϊʔυͷWJTJUؔΛΦʔόʔϥΠυ͢Δ r 4ZOUBY3FXSJUFS ‣
4ZOUBY7JTJUPSͷػೳʹՃ͑ͯɺWJTJUؔͰͨͲͬͨϊʔυΛผͷϊʔυʹॻ ͖͑Δ͜ͱ͕Ͱ͖Δ ‣ ϚΫϩϦϑΝΫλϦϯάɺϑΥʔϚολʔͳͲ4ZOUBY5SFFΛ༻͍ͯιʔε ίʔυΛม͢Δ༻్ʹ෯͘ΘΕΔ 4XJGU4ZOUBY"1* 4ZOUBY7JTJUPS4ZOUBY3FXSJUFS -
4ZOUBY7JTJUPS4ZOUBY3FXSJUFS visit() visit() visit() visit() visit() visit() visit() visit() visit()
visit() visit() visit() visit() -
4ZOUBY7JTJUPS open class SyntaxVisitor { public init(viewMode: SyntaxTreeViewMode) public func
walk(_ node: some SyntaxProtocol) open func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind open func visitPost(_ node: AccessorBlockSyntax) open func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind open func visitPost(_ node: AccessorDeclSyntax) ... } -
4ZOUBY7JTJUPS open class SyntaxVisitor { public init(viewMode: SyntaxTreeViewMode) public func
walk(_ node: some SyntaxProtocol) open func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind open func visitPost(_ node: AccessorBlockSyntax) open func visit(_ node: AccessorDeclSyntax) -> SyntaxVisitorContinueKind open func visitPost(_ node: AccessorDeclSyntax) ... } ୳ࡧ͢Δରͷܕͷϊʔυͷ WJTJUؔΛΦʔόʔϥΠυ͢Δ -
&YBNQMF'JOE"MMGBUBM&SSPS'VODUJPO$BMMT 4ZOUBY7JTJUPS class FatalErrorChecker: SyntaxVisitor { override func visit(_ node:
FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { ... } } return .visitChildren } } -
&YBNQMF'JOE"MMGBUBM&SSPS'VODUJPO$BMMT 4ZOUBY7JTJUPS class FatalErrorChecker: SyntaxVisitor { override func visit(_ node:
FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { ... } } return .visitChildren } } -
&YBNQMF'JOE"MMGBUBM&SSPS'VODUJPO$BMMT 4ZOUBY7JTJUPS class FatalErrorChecker: SyntaxVisitor { override func visit(_ node:
FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { ... } } return .visitChildren } } -
ιʔείʔυͷςΩετΛൺֱ if calledExpression.baseName.tokenKind == .identifier("fatalError") { ... } if calledExpression.baseName.trimmed.description
== "fatalError" { ... } if calledExpression.baseName.text == "fatalError" { ... } if calledExpression.baseName.description == "fatalError" { ... } -
ιʔείʔυͷςΩετΛൺֱ if calledExpression.baseName.tokenKind == .identifier("fatalError") { ... } if calledExpression.baseName.trimmed.description
== "fatalError" { ... } if calledExpression.baseName.text == "fatalError" { ... } if calledExpression.baseName.description == "fatalError" { ... } public extension SyntaxProtocol { ... var trimmed: Self { return self .with(\.leadingTrivia, []) .with(\.trailingTrivia, []) } ... } -
ιʔείʔυͷςΩετΛൺֱ 4XJGU4ZOUBY#VJMEFS if calledExpression.baseName == "fatalError" { ... } import
SwiftSyntaxBuilder ... 4USJOHϦςϥϧ͔Β4ZOUBY͕࡞ΒΕΔͷͰ ίϯύΠϧΤϥʔʹͳΒͳ͍ -
4ZOUBY"OZ7JTJUPS &YBNQMF$POWFSU4ZOUBY5SFFUP)5.- class HTMLConverter: SyntaxAnyVisitor { var html = "<!DOCTYPE
html>" override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { html += #"<span class="\#(node.syntaxNodeType)">"# return .visitChildren } override func visitAnyPost(_ node: Syntax) { html += "</span>" } } -
4ZOUBY"OZ7JTJUPS &YBNQMF$POWFSU4ZOUBY5SFFUP)5.- class HTMLConverter: SyntaxAnyVisitor { var html = "<!DOCTYPE
html>" override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { html += #"<span class="\#(node.syntaxNodeType)">"# return .visitChildren } override func visitAnyPost(_ node: Syntax) { html += "</span>" } } -
class HTMLConverter: SyntaxAnyVisitor { var html = "<!DOCTYPE html>" override
func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { html += #"<span class="\#(node.syntaxNodeType)">"# return .visitChildren } override func visitAnyPost(_ node: Syntax) { html += "</span>" } override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind { html += node.description return .visitChildren } override func visitPost(_ node: TokenSyntax) {} } -
class HTMLConverter: SyntaxAnyVisitor { var html = "<!DOCTYPE html>" override
func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { html += #"<span class="\#(node.syntaxNodeType)">"# return .visitChildren } override func visitAnyPost(_ node: Syntax) { html += "</span>" } override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind { html += node.description return .visitChildren } override func visitPost(_ node: TokenSyntax) {} } visitAnyPost()͕ݺΕͳ͍Α͏ʹ -
class HTMLConverter: SyntaxAnyVisitor { var html = "<!DOCTYPE html>" override
func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind { html += #"<span class="\#(node.syntaxNodeType)">"# return .visitChildren } override func visitAnyPost(_ node: Syntax) { html += "</span>" } override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind { html += node.description return .visitChildren } override func visitPost(_ node: TokenSyntax) {} } var kind = "\(node.tokenKind)" if let index = kind.firstIndex(of: "(") { kind = String(kind.prefix(upTo: index)) } if kind.hasSuffix("Keyword") { kind = "keyword" } html += node.leadingTrivia.pieces.map { $0.htmlEscaped() }.joined() html += """ <span class="\(kind.htmlEscaped())">\(node.text.htmlEscaped())</span> """ html += node.trailingTrivia.pieces.map { $0.htmlEscaped() }.joined() return .visitChildren -
... <span class="SourceFileSyntax"> <span class="CodeBlockItemListSyntax"> <span class="CodeBlockItemSyntax"> <span class="VariableDeclSyntax"> <span
class="AttributeListSyntax"></span> <span class="DeclModifierListSyntax"></span> <span class="keyword">let</span> <span class="PatternBindingListSyntax"> <span class="PatternBindingSyntax"> <span class="IdentifierPatternSyntax"> <span class="identifier">number</span> </span> <span class="InitializerClauseSyntax"> <span class="equal">=</span> <span class="IntegerLiteralExprSyntax"> <span class="integerLiteral">100</span></span> </span> </span> </span> </span> </span> </span> <span class="endOfFile"></span> </span> ... -
4ZOUBY3FXSJUFS open class SyntaxRewriter { public func rewrite(_ node: some
SyntaxProtocol) -> Syntax open func visitPre(_ node: Syntax) open func visitAny(_ node: Syntax) -> Syntax? open func visitPost(_ node: Syntax) open func visit(_ node: AccessorBlockSyntax) -> AccessorBlockSyntax open func visit(_ node: AccessorDeclListSyntax) -> AccessorDeclListSyntax ... -
4ZOUBY3FXSJUFS open class SyntaxRewriter { public func rewrite(_ node: some
SyntaxProtocol) -> Syntax open func visitPre(_ node: Syntax) open func visitAny(_ node: Syntax) -> Syntax? open func visitPost(_ node: Syntax) open func visit(_ node: AccessorBlockSyntax) -> AccessorBlockSyntax open func visit(_ node: AccessorDeclListSyntax) -> AccessorDeclListSyntax ... ฦͨ͠ϊʔυͷ༰Ͱ 4ZOUBY5SFF͕ॻ͖ΘΔ -
4ZOUBY3FXSJUFS open class SyntaxRewriter { public func rewrite(_ node: some
SyntaxProtocol) -> Syntax open func visitPre(_ node: Syntax) open func visitAny(_ node: Syntax) -> Syntax? open func visitPost(_ node: Syntax) open func visit(_ node: AccessorBlockSyntax) -> AccessorBlockSyntax open func visit(_ node: AccessorDeclListSyntax) -> AccessorDeclListSyntax ... WJTJU"OZ @ -
4ZOUBY3FXSJUFS open class SyntaxRewriter { public func rewrite(_ node: some
SyntaxProtocol) -> Syntax open func visitPre(_ node: Syntax) open func visitAny(_ node: Syntax) -> Syntax? open func visitPost(_ node: Syntax) open func visit(_ node: AccessorBlockSyntax) -> AccessorBlockSyntax open func visit(_ node: AccessorDeclListSyntax) -> AccessorDeclListSyntax ... WJTJU1SF @ -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } ݩͷιʔείʔυͷ5SJWJBΛ ͦͷ··෮ݩ͢Δ -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } ʮࣜʯͷߏจͷϕʔεϊʔυ Ͱ͋Δ&YQS4ZOUBYܕΛฦ͢ -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } -
class FatalToPreconditionRewriter: SyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) ->
ExprSyntax { if let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) { if calledExpression.baseName.tokenKind == .identifier("fatalError") { return super.visit( ExprSyntax( node.with( \.calledExpression, ExprSyntax( calledExpression .with(\.baseName, .identifier("preconditionFailure")) .with(\.leadingTrivia, calledExpression.leadingTrivia) ) ) ) ) } } return super.visit(node) } } TVQFSΛݺͿͱࢠཁૉͷ୳ࡧΛଓ͚Δ -
4ZOUBY3FXSJUFS #BTF/PEFT r %FDM4ZOUBYએݴ ‣ มɺؔɺΫϥεͷએݴͳͲ r &YQS4ZOUBYࣜ Λฦ͢
‣ Λฦ͢ߏจɻؔݺͼग़͠ɺϓϩύςΟΞΫηεɺΫϩʔδϟͳͲ r 1BUUFSO4ZOUBY ‣ 4XJUDIจͷ$BTFઅมએݴͷมͷఆٛͳͲ r 5ZQF4ZOUBY ‣ ܕΞϊςʔγϣϯͳͲ -
4ZOUBY3FXSJUFS #BTF/PEFT // Non-failable initializer let expr = ExprSyntax(MemberAccessExprSyntax(name: .identifier("red")))
// Failable initializer let decl = DeclSyntax(MemberAccessExprSyntax(name: .identifier("red"))) .red nil -
4PVSDF-PDBUJPO 4PVSDF-PDBUJPO$POWFSUFS let sourceFile = Parser.parse(source: "let number = 100")
let locationConverter = SourceLocationConverter(fileName: "", tree: sourceFile) ... let identifier = pattern.identifier let start = identifier.startLocation(converter: locationConverter) let end = identifier.endLocation(converter: locationConverter) print("\(start.line):\(start.column)...\(end.line):\(end.column)") 1:5...1:11 -
4PVSDF-PDBUJPO 4PVSDF-PDBUJPO$POWFSUFS let sourceFile = Parser.parse(source: "let number = 100")
let locationConverter = SourceLocationConverter(fileName: "", tree: sourceFile) ... let identifier = pattern.identifier let start = identifier.startLocation(converter: locationConverter) let end = identifier.endLocation(converter: locationConverter) print("\(start.line):\(start.column)...\(end.line):\(end.column)") 1:5...1:11 let start = identifier.startLocation(converter: locationConverter) let end = identifier.endLocation(converter: locationConverter) print("\(start.line):\(start.column)...\(end.line):\(end.column)") let number = 100 1:5 1:11 -
4PVSDF-PDBUJPO 4PVSDF-PDBUJPO$POWFSUFS func startLocation( converter: SourceLocationConverter, afterLeadingTrivia: Bool = true
) -> SourceLocation func endLocation( converter: SourceLocationConverter, afterTrailingTrivia: Bool = false ) -> SourceLocation number -
1BSTFS3FTJMJFODZ 1BSTFS/FWFS'BJMT SyntaxVisitor(viewMode: .sourceAccurate) enum SyntaxTreeViewMode { case sourceAccurate case
fixedUp case all } ιʔείʔυʹදࣔ͞ΕΔτʔΫϯͷΈ 1BSTFS͕ิͨ͠τʔΫϯ८ճ͢Δ 1BSTFS͕ิͨ͠τʔΫϯ͓Αͼ༧ظ͠ͳ͍ϊʔυ८ճ͢Δ -
4XJGU.BDSPT *OJU.BDSP @Init class Person { let name: String let
age: Int let birthday: Date? } class Person { let name: String let age: Int let birthday: Date? } -
4XJGU.BDSPT *OJU.BDSP @Init class Person { let name: String let
age: Int let birthday: Date? } class Person { let name: String let age: Int let birthday: Date? } } init( name: String, age: Int, birthday: Date? ) { self.name = name self.age = age self.birthday = birthday } -
4XJGU.BDSPT *OJU.BDSP struct InitMacro: MemberMacro { static func expansion( of
node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { ... } } -
4XJGU.BDSPT *OJU.BDSP struct InitMacro: MemberMacro { static func expansion( of
node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { ... } } @Init -
4XJGU.BDSPT *OJU.BDSP struct InitMacro: MemberMacro { static func expansion( of
node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { ... } } @Init class Person { let name: String let age: Int let birthday: Date? } -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\($0.)\($0.)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\($0.) = \($0.)" }.joined(separator: "\n")) } """ ] -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\($0.)\($0.)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\($0.) = \($0.)" }.joined(separator: "\n")) } """ ] class Person { let name: String let age: Int let birthday: Date? } -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\($0.)\($0.)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\($0.) = \($0.)" }.joined(separator: "\n")) } """ ] (name, String) (age, Int) (birthday, Date?) -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\($0.)\($0.)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\($0.) = \($0.)" }.joined(separator: "\n")) } """ ] (pattern, typeAnnotation) -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } init( pattern: typeAnnotation, ) { self.pattern = pattern } -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] import SwiftSyntaxBuilder ... -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] return [ DeclSyntax( InitializerDeclSyntax( signature: FunctionSignatureSyntax( input: ParameterClauseSyntax( parameterList: FunctionParameterListSyntax( variableDecls.map { (pattern, typeAnnotation) in FunctionParameterSyntax(firstName: pattern.identifier, type: typeAnnotation.type) } ) ) ), body: CodeBlockSyntax( statements: CodeBlockItemListSyntax( variableDecls.map { (pattern, typeAnnotation) in CodeBlockItemSyntax( item: CodeBlockItemSyntax.Item( InfixOperatorExprSyntax( leftOperand: MemberAccessExprSyntax( base: IdentifierExprSyntax(identifier: .keyword(.self)), name: pattern.identifier ), operatorOperand: AssignmentExprSyntax(), rightOperand: IdentifierExprSyntax(identifier: pattern.identifier) ) ) ) } ) ) ) ) ] -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] init(name:Stringage:Intbirthday:Date?){self.name=nameself.age=ageself.birthday=birthday} -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] init(name: String age: Int birthday: Date?) { self.name = name self.age = age self.birthday = birthday } .formatted(using: BasicFormat()) -
let variableDecls: [(pattern: IdentifierPatternSyntax, typeAnnotation: TypeAnnotationSyntax)] variableDecls = declaration.memberBlock.members.compactMap {
guard let decl = $0.decl.as(VariableDeclSyntax.self) else { return nil } guard let binding = decl.bindings.first else { return nil } guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { return nil } guard let typeAnnotation = binding.typeAnnotation else { return nil } return (pattern, typeAnnotation) } return [ """ init( \(raw: variableDecls.map { "\(pattern)\(typeAnnotation)" }.joined(separator: ",\n")) ) { \(raw: variableDecls.map { "self.\(pattern) = \(pattern)" }.joined(separator: "\n")) } """ ] init(name: String age: Int birthday: Date?) { self.name = name self.age = age self.birthday = birthday } .formatted(using: BasicFormat()) struct InitMacro: MemberMacro { static var formatMode: FormatMode = .disabled ... -
'PMEJOH4FRVFODF&YQSFTTJPOT 4XJGU0QFSBUPST SequenceExprSyntax ExprListSyntax DeclReferenceExprSyntax BinaryOperatorExpr a + DeclReferenceExprSyntax BinaryOperatorExpr
* b DeclReferenceExprSyntax c InfixOperatorExprSyntax DeclReferenceExprSyntax BinaryOperatorExpr a + DeclReferenceExprSyntax BinaryOperatorExpr * b DeclReferenceExprSyntax c InfixOperatorExprSyntax if a + b * c == d { ... } 1BSTF 'PME -
'PMEJOH4FRVFODF&YQSFTTJPOT 4XJGU0QFSBUPST r ԋࢉࢠͷ༏ઌॱҐʹैͬͯ4ZOUBY5SFFΛʮંΓͨͨΉʯ r ԋࢉࢠͷ༏ઌॱҐͷ௨Γʹ८ճͰ͖ΔπϦʔߏ͕ੜ͞ΕΔ r ԋࢉࢠͷ༏ઌॱҐԋࢉࢠςʔϒϧʹ͋Β͔͡Ίొ͢Δ r ະͷԋࢉࢠ
ಠࣗఆٛͷԋࢉࢠ ͕ԋࢉࢠ͕͋Δͱࣦഊ͢Δ r ϚΫϩͷ߹ɺࣗಈͰંΓͨͨΜͩ4ZOUBY5SFF͕͞ΕΔ ‣ ࣗͰ1BSTFͨ͠4ZOUBY5SFFͱҟͳ͍ͬͯͯ͋Θͯͳ͍ r ϚΫϩͷ߹ɺಠࣗఆٛͷԋࢉࢠ͕͋ͬͯڧҾʹંΓͨͨΉ ‣ ༏ઌॱҐ͕ؒҧͬͯંΓͨͨ·ΕΔ ߹͕͋Δ r ͕͋Δ߹4ZOUBY5SFF͔ΒιʔείʔυΛੜͯࣗ͠Ͱ࠶1BSTF͢Δ -
"OBUPNZPG4XJGU4ZOUBY 4XJGU4ZOUBY 4XJGU4ZOUBY.BDSPT 4XJGU0QFSBUPST 4XJGU4ZOUBY#VJMEFS 4XJGU1BSTFS 4XJGU$PNQJMFS1MVHJO 4XJGU#BTJD'PSNBU 4ZOUBY5SFFͷσʔλߏ ܕ
ͱ7JTJUPSɾ 3FXSJUFSͳͲجຊతͳ"1*Λఏڙ ߏจղੳͱ4ZOUBY5SFFͷੜ จࣈྻϦςϥϧ &YQSFTTJCMF#Z4USJOH-JUFSBM 3FTVMU#VJMEFS͔Β4ZOUBY5SFFΛੜ͢Δ "1*Λఏڙ ϚΫϩͷ࣮ʹඞཁͳ"1*Λఏڙ ඞཁͳۭനվߦΛࣗಈతʹૠೖ͢Δ ԋࢉࢠͷ༏ઌॱҐʹैͬͯࣜΛʮંΓͨͨΉʯ -
*OJU.BDSP "EWBODFE&YFSDJTFT r $MBTTɺ4USVDUɺ"DUPSҎ֎ͷܕʹద༻͠ͳ͍ r ܕʹͭ͘ΞΫηεम০ࢠ QVCMJDɺQSJWBUFͳͲ ͱΠχγϟϥΠβͷΞΫηεϨ ϕϧΛҰகͤ͞Δ r
$PNQVUFE1SPQFSUZΛআ֎͢Δ r ؔܕͷϓϩύςΟʹରԠ͢Δ r αϯϓϧίʔυ IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJJPTEDTBNQMFT -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF PQFOJOH@CSBDF r 0QFOJOHCSBDFTTIPVMECFQSFDFEFECZBTJOHMFTQBDFBOEPOUIFTBNF MJOFBTUIFEFDMBSBUJPO func run() { } if
a == b { } func run(){ } func run() { } if a == b { } guard let a = b else { } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF %FUFDUWJPMBUJPO🔎 override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind {
if node.tokenKind == .leftBrace { if let previousToken = node.previousToken(viewMode: .sourceAccurate), previousToken.trailingTrivia == .space { let position = node.positionAfterSkippingLeadingTrivia violations.append(position) } } return .visitChildren } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF %FUFDUWJPMBUJPO🔎 override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind {
if node.tokenKind == .leftBrace { if let previousToken = node.previousToken(viewMode: .sourceAccurate), previousToken.trailingTrivia == .space { let position = node.positionAfterSkippingLeadingTrivia violations.append(position) } } return .visitChildren } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF %FUFDUWJPMBUJPO🔎 override func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind {
if node.tokenKind == .leftBrace { if let previousToken = node.previousToken(viewMode: .sourceAccurate), previousToken.trailingTrivia == .space { let position = node.positionAfterSkippingLeadingTrivia violations.append(position) } } return .visitChildren } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF "VUPDPSSFDUJPO🪄 func run() { } func run(){ } func
run() { } if a == b { } func run() { } if a == b { } -
5IF5SJWJB"UUSJCVUJPO3VMF "UPLFOPXOTBMMPGJUTUSBJMJOHUSJWJBVQUP CVUOPUJODMVEJOH UIFOFYU OFXMJOFDIBSBDUFS -PPLJOHCBDLXBSEJOUIFUFYU BUPLFOPXOTBMMPGUIFMFBEJOHUSJWJBVQ UPBOEJODMVEJOHUIF
fi STUDPOUJHVPVTTFRVFODFPGOFXMJOFTDIBSBDUFST IUUQTHJUIVCDPNBQQMFTXJGUTZOUBYJTTVFT -
func run()␣␣↲ {↲ ␣␣*** } func run()↲ {↲ ␣␣*** }
func run()␣␣{↲ ␣␣*** } 4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF 5IF5SJWJB"UUSJCVUJPO3VMF -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } private extension TokenSyntax { func hasSingleSpaceLeading() -> Bool { if let previousToken = previousToken(viewMode: .sourceAccurate), previousToken.trailingTrivia == .space { return true } else { return false } } } -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
override func visit(_ node: FunctionDeclSyntax) -> DeclSyntax { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } func run(name: T) { } ?????????????? -
override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) if let genericWhereClause = node.genericWhereClause { return super.visit( node .with(\.genericWhereClause, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { guard let
body = node.body else { return super.visit(node) } let openingBrace = body.leftBrace if !openingBrace.hasSingleSpaceLeading() { let position = openingBrace.positionAfterSkippingLeadingTrivia correctionPositions.append(position) if let genericWhereClause = node.genericWhereClause { return super.visit( node .with(\.genericWhereClause, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit( node .with(\.signature, node.signature.with(\.trailingTrivia, .space)) .with(\.leftBrace, node.leftBrace.with(\.leadingTrivia, [])) ) } return super.visit(node) } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF "VUPDPSSFDUJPO🪄 func run<T>(name: T) where T: StringProtocol { }
func run() { } func run(){ } func run<T>(name: T) where T: StringProtocol { } if a == b { } if a == b { } *Gจରɻଞʹʁ -
-BOHVBHF3FGFSFODF "CPVUUIF-BOHVBHF3FGFSFODF r "OBSSPX ˠ JTVTFEUPNBSLHSBNNBSQSPEVDUJPOTBOEDBOCFSFBEBTlDBODPOTJTUPGz r 4ZOUBDUJDDBUFHPSJFTBSFJOEJDBUFECZJUBMJDUFYUBOEBQQFBSPOCPUITJEFTPGBHSBNNBS QSPEVDUJPOSVMF r
-JUFSBMXPSETBOEQVODUVBUJPOBSFJOEJDBUFECZCPMEGBDFDPOTUBOUXJEUIUFYUBOEBQQFBSPOMZPO UIFSJHIUIBOETJEFPGBHSBNNBSQSPEVDUJPOSVMF r "MUFSOBUJWFHSBNNBSQSPEVDUJPOTBSFTFQBSBUFECZWFSUJDBMCBST c 8IFOBMUFSOBUJWF QSPEVDUJPOTBSFUPPMPOHUPSFBEFBTJMZ UIFZ`SFCSPLFOJOUPNVMUJQMFHSBNNBSQSPEVDUJPOSVMFT POOFXMJOFT r *OBGFXDBTFT SFHVMBSGPOUUFYUJTVTFEUPEFTDSJCFUIFSJHIUIBOETJEFPGBHSBNNBSQSPEVDUJPO SVMF r 0QUJPOBMTZOUBDUJDDBUFHPSJFTBOEMJUFSBMTBSFNBSLFECZBUSBJMJOHRVFTUJPONBSL -
-BOHVBHF3FGFSFODF "CPVUUIF-BOHVBHF3FGFSFODF r ҹ ˠ ߏจΛࣔ͠ɺʮࠨลӈลͷʓʓͰߏ͞ΕΔʯͱಡΈ·͢ r ߏจΧςΰϦΠλϦοΫͷςΩετͰࣔ͞Εɺߏจϧʔϧͷ྆ଆʹݱΕ·͢ r ୯ޠϦςϥϧͱه߸ɺଠࣈͰ෯ͷॻମͰࣔ͞ΕɺߏจϧʔϧͷӈଆʹͷΈݱΕ
·͢ r ෳͷॻ͖ํ͕͋ΔߏจύΠϓ c Ͱ۠ΒΕ·͢ɻԣʹ͘ಡΈʹ͘͘ͳΔ߹ ৽͍͠ߦͰෳͷߏจϧʔϧʹׂ͞Ε·͢ r ௨ৗͷॻମΛ༻ͯ͠จ๏نଇͷӈଆΛهड़͢Δ͜ͱ͕͋Γ·͢ r ඞਢͰͳ͍ߏจΧςΰϦͱϦςϥϧɺඌʹΫΤενϣϯϚʔΫA A͕͖·͢ -
-BOHVBHF3FGFSFODF "CPVUUIF-BOHVBHF3FGFSFODF Grammar of a getter-setter block getter-setter-block → {
getter-clause setter-clause? } | { setter-clause getter-clause } var name: String { get { ... } set { ... } } var name: String { get { ... } } -
Grammar of a function declaration function-declaration → function-head function-name generic-parameter-clause?
function-signature generic-where-clause? function-body? function-head → attributes? declaration-modi fi ers? func function-name → identi fi er | operator function-signature → parameter-clause async? throws? function-result? function-signature → parameter-clause async? rethrows function-result? function-result → -> attributes? type function-body → code-block parameter-clause → ( ) | ( parameter-list ) parameter-list → parameter | parameter , parameter-list parameter → external-parameter-name? local-parameter-name type-annotation default-argument-clause? parameter → external-parameter-name? local-parameter-name type-annotation parameter → external-parameter-name? local-parameter-name type-annotation ... external-parameter-name → identi fi er local-parameter-name → identi fi er default-argument-clause → = expression -
Grammar of a function declaration function-declaration → function-head function-name generic-parameter-clause?
function-signature generic-where-clause? function-body? function-head → attributes? declaration-modi fi ers? func function-name → identi fi er | operator function-signature → parameter-clause async? throws? function-result? function-signature → parameter-clause async? rethrows function-result? function-result → -> attributes? type function-body → code-block parameter-clause → ( ) | ( parameter-list ) parameter-list → parameter | parameter , parameter-list parameter → external-parameter-name? local-parameter-name type-annotation default-argument-clause? parameter → external-parameter-name? local-parameter-name type-annotation parameter → external-parameter-name? local-parameter-name type-annotation ... external-parameter-name → identi fi er local-parameter-name → identi fi er default-argument-clause → = expression -
Grammar of a function declaration function-declaration → function-head function-name generic-parameter-clause?
function-signature generic-where-clause? function-body? function-head → attributes? declaration-modi fi ers? func function-name → identi fi er | operator function-signature → parameter-clause async? throws? function-result? function-signature → parameter-clause async? rethrows function-result? function-result → -> attributes? type function-body → code-block parameter-clause → ( ) | ( parameter-list ) parameter-list → parameter | parameter , parameter-list parameter → external-parameter-name? local-parameter-name type-annotation default-argument-clause? parameter → external-parameter-name? local-parameter-name type-annotation parameter → external-parameter-name? local-parameter-name type-annotation ... external-parameter-name → identi fi er local-parameter-name → identi fi er default-argument-clause → = expression -
-BOHVBHF3FGFSFODF 4VNNBSZPGUIF(SBNNBS r มએݴ r $PNQVUFE1SPQFSUZ r HFUUFSTFUUFS r XJMM4FUEJE4FU
r GPSJOXIJMFSFQFBUXIJMFJGHVBSETXJUDI r EFGFS r EPEPDBUDI -
-BOHVBHF3FGFSFODF 4VNNBSZPGUIF(SBNNBS Grammar of a structure declaration struct-declaration → attributes?
access-level-modi fi er? struct struct-name generic-parameter-clause? type-inheritance-clause? generic-where-clause? struct-body struct-name → identi fi er struct-body → { struct-members? } struct-members → struct-member struct-members? struct-member → declaration | compiler-control-statement -
-BOHVBHF3FGFSFODF 4VNNBSZPGUIF(SBNNBS r มએݴ r $PNQVUFE1SPQFSUZ r HFUUFSTFUUFS r XJMM4FUEJE4FU
r GPSJOXIJMFSFQFBUXIJMFJGHVBSETXJUDI r EFGFS r EPEPDBUDI r FOVNTUSVDUDMBTTBDUPSQSPUPDPMFYUFOTJPOએݴ r QSFDFEFODFHSPVQએݴ r $MPTVSF -
-BOHVBHF3FGFSFODF 4VNNBSZPGUIF(SBNNBS r มએݴ r $PNQVUFE1SPQFSUZ r HFUUFSTFUUFS r XJMM4FUEJE4FU
r GPSJOXIJMFSFQFBUXIJMFJGHVBSEEFGFSEPEPDBUDI r FOVNTUSVDUDMBTTBDUPSQSPUPDPMFYUFOTJPOએݴ r QSFDFEFODFHSPVQએݴ r TXJUDI$MPTVSF %FDM(SPVQ4ZOUBY 8JUI$PEF#MPDL4ZOUBY 8JUI4UBUFNFOUT4ZOUBY -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF "VUPDPSSFDUJPO🪄 extension DeclGroupSyntax { func violationPosition() -> AbsolutePosition? {
let openingBrace = memberBlock.leftBrace if !openingBrace.hasSingleSpaceLeading() { return openingBrace.positionAfterSkippingLeadingTrivia } return nil } } -
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { if let
position = node.violationPosition() { violations.append(position) } return .visitChildren } override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { if let position = node.violationPosition() { violations.append(position) } return .visitChildren } ... -
private extension DeclGroupSyntax { ... func correct<T: SyntaxProtocol>(keyPath: WritableKeyPath<Self, T>)
-> Self { return self .with(keyPath, self[keyPath: keyPath].with(\.trailingTrivia, .space)) .with(\.memberBlock, memberBlock.with(\.leadingTrivia, [])) } func correct<T: SyntaxProtocol>(keyPath: WritableKeyPath<Self, T?>) -> Self? { guard let value = self[keyPath: keyPath] else { return nil } return self .with(keyPath, value.with(\.trailingTrivia, .space)) .with(\.memberBlock, memberBlock.with(\.leadingTrivia, [])) } } -
4XJGU-JOU0QFOJOH#SBDF4QBDJOH3VMF -FTTPOT-FBSOFE r 4ZOUBY7JTJUPSΛͬͯҧΛνΣοΫ r 4ZOUBY3FXSJUFSΛͬͯҧΛνΣοΫˍࣗಈमਖ਼ r 1SPUPDPM&YUFOTJPOΛͬͯॲཧΛ·ͱΊΔ r 4XJGU-BOHVBHF3FGFSFODF
r 1VMM3FRVFTU IUUQTHJUIVCDPNSFBMN4XJGU-JOUQVMM -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU DPMMFDUJPO@BMJHONFOU r "MMFMFNFOUTJOBDPMMFDUJPOMJUFSBMTIPVMECFWFSUJDBMMZBMJHOFE let abc = [ "alpha": "a",
"beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU DPMMFDUJPO@BMJHONFOU r "MMFMFNFOUTJOBDPMMFDUJPOMJUFSBMTIPVMECFWFSUJDBMMZBMJHOFE r "MMFMFNFOUTJOBDPMMFDUJPOMJUFSBMTIPVMECFWFSUJDBMMZBMJHOFE let abc = [
"alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d" ] -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU 'BMTF1PTJUJWF NSAttributedString( string: "…", attributes: [.font: UIFont.systemFont(ofSize: 12, weight:
.regular), .foregroundColor: UIColor(white: 0, alpha: 0.2)] ) IUUQTHJUIVCDPNSFBMN4XJGU-JOUJTTVFT -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU 'BMTF1PTJUJWF NSAttributedString( string: "…", attributes: [.font: UIFont.systemFont(ofSize: 12, weight:
.regular), .foregroundColor: UIColor(white: 0, alpha: 0.2)] ) IUUQTHJUIVCDPNSFBMN4XJGU-JOUJTTVFT 31 29 -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU DPMMFDUJPO@BMJHONFOU override func visitPost(_ node: DictionaryElementListSyntax) { let locations
= node.map { element in let position = alignColons ? element.colon.positionAfterSkippingLeadingTrivia : element.keyExpression.positionAfterSkippingLeadingTrivia return locationConverter.location(for: position) } violations.append(contentsOf: validate(keyLocations: locations)) } "4$** 65'ͰCZUF ͳΒΓཱͭ -
override func visitPost(_ node: DictionaryElementListSyntax) { let locations = node.map
{ element in let position = alignColons ? element.colon.positionAfterSkippingLeadingTrivia : element.keyExpression.positionAfterSkippingLeadingTrivia let location = locationConverter.location(for: position) let graphemeColumn: Int let graphemeClusters = String( locationConverter.sourceLines[location.line - 1].utf8.prefix(location.column - 1) ) if let graphemeClusters { graphemeColumn = graphemeClusters.count + 1 } else { graphemeColumn = location.column } return SourceLocation( line: location.line, column: graphemeColumn, offset: location.offset, file: location.file ) } violations.append(contentsOf: validate(keyLocations: locations)) } -
override func visitPost(_ node: DictionaryElementListSyntax) { let locations = node.map
{ element in let position = alignColons ? element.colon.positionAfterSkippingLeadingTrivia : element.keyExpression.positionAfterSkippingLeadingTrivia let location = locationConverter.location(for: position) let graphemeColumn: Int let graphemeClusters = String( locationConverter.sourceLines[location.line - 1].utf8.prefix(location.column - 1) ) if let graphemeClusters { graphemeColumn = graphemeClusters.count + 1 } else { graphemeColumn = location.column } return SourceLocation( line: location.line, column: graphemeColumn, offset: location.offset, file: location.file ) } violations.append(contentsOf: validate(keyLocations: locations)) } locationConverter.sourceLines[location.line - 1].utf8.prefix(location.column - 1) ݩͷιʔείʔυ͕ߦ͝ͱʹ֨ೲ͞Εͨྻ -
override func visitPost(_ node: DictionaryElementListSyntax) { let locations = node.map
{ element in let position = alignColons ? element.colon.positionAfterSkippingLeadingTrivia : element.keyExpression.positionAfterSkippingLeadingTrivia let location = locationConverter.location(for: position) let graphemeColumn: Int let graphemeClusters = String( locationConverter.sourceLines[location.line - 1].utf8.prefix(location.column - 1) ) if let graphemeClusters { graphemeColumn = graphemeClusters.count + 1 } else { graphemeColumn = location.column } return SourceLocation( line: location.line, column: graphemeColumn, offset: location.offset, file: location.file ) } violations.append(contentsOf: validate(keyLocations: locations)) } 29 31 -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU 'BMTF1PTJUJWF NSAttributedString( string: "…", attributes: [.font: UIFont.systemFont(ofSize: 12, weight:
.regular), .foregroundColor: UIColor(white: 0, alpha: 0.2)] ) IUUQTHJUIVCDPNSFBMN4XJGU-JOUJTTVFT -
let graphemeStartColumn: Int if let prefix = String(locationConverter.sourceLines[start.line - 1].utf8.prefix(start.column
- 1)) { graphemeStartColumn = prefix.utf16.count + 1 } else { graphemeStartColumn = start.column } let graphemeEndColumn: Int if let prefix = String(locationConverter.sourceLines[end.line - 1].utf8.prefix(end.column - 1)) { graphemeEndColumn = prefix.utf16.count + 1 } else { graphemeEndColumn = end.column } 4XJGU"45&YQMPSFS +BWB4DSJQU4USJOH-FOHUI*TJO65'$PEF6OJUT -
4XJGU-JOU$PMMFDUJPO&MFNFOU"MJHONFOU -FTTPOT-FBSOFE r 4XJGU4ZOUBYͷߏจϊʔυͷҐஔใ65'ͷόΠτ r ࣮ࡍͷιʔείʔυͷҐஔͱඞͣ͠Ұக͠ͳ͍ "4$**Ҏ֎ͷจࣈͳͲ r ࠷ऴతͳग़ྗઌʹ߹Θ͕ͤͯඞཁ
‣ ݟͨͷจࣈˠॻهૉΫϥελ ‣ +BWB4DSJQUˠ65' r 1VMM3FRVFTU IUUQTHJUIVCDPNSFBMN4XJGU-JOUJTTVFT -
&YFSDJTFT4PVSDF&EJUPS&YUFOTJPOT $POWFSU9$5"TTFSU 'VODUJPOTUP1PXFS"TTFSU.BDSPT XCTAssert(actual == expected) XCTAssertEqual(actual, expected) XCTAssertNotEqual(actual, expected)
XCTAssertGreaterThan(a, b) XCTAssertGreaterThanOrEqual(a, b) XCTAssertIdentical(actual, expected) XCTAssertNotIdentical(actual, expected) #assert(actual == expected) #assert(actual == expected) #assert(actual != expected) #assert(a > b) #assert(a >= b) #assert(actual === expected) #assert(actual !== expected) -
&YFSDJTFT4PVSDF&EJUPS&YUFOTJPOT $POWFSU9$5"TTFSU 'VODUJPOTUP1PXFS"TTFSU.BDSPT r 9$5"TTFSU ؔΛBTTFSU ϚΫϩʹม͢Δ r ςετͷ࣮ߦ݁Ռ͕มΘΒͳ͍Α͏ʹ͢Δ r
NFTTBHFɺ fi MFɺMJOFͷσϑΥϧτҾʹҙ͢Δ r ුಈখΛൺֱ͢Δ9$5"TTFSU&RVBM @@BDDVSBDZ@ fi MFMJOF ؔର֎ r ෆશͳιʔείʔυ͕ͬͯ͘Δ߹͕͋Δ r ղྫ IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJTXJGUQPXFSBTTFSUQVMM -
8SBQ6Q r 4XJGU1BSTFSͰιʔείʔυΛߏจղੳ͠ɺ4ZOUBY5SFFΛ୳ࡧ͢Δ r 4ZOUBY5SFFΛมͯ͠ιʔείʔυΛॻ͖͑Δ r ϚΫϩɺ-JOUɺ'PSNBUUFSɺ9DPEF&YUFOTJPOɺͳͲ͕࡞ΕΔ r 4ZOUBY3FXSJUFS4PVSDF-PDBUJPO$POWFSUFSͳͲಠಛͷ"1*ʹ׳ΕΕҙ֎ ͱγϯϓϧʹॻ͚Δ
r ίϯύΠϥͰͳ͍ͷͰܕใͳͲߏจ͔ΒಡΈऔΕͳ͍ͷѻ͑ͳ͍ r 4XJGU4ZOUBYΛ༗ޮʹ͏ʹ4XJGUʹؔ͢Δ෯͍͕ࣝॏཁ -
3FGFSFODFT r 4BNQMF$PEF IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJJPTEDTBNQMFT r 4XJGU4ZOUBY IUUQTHJUIVCDPNBQQMFTXJGUTZOUBY r 4XJGU"45&YQMPSFS IUUQTTXJGUBTUFYQMPSFSDPN
r 4XJGU-BOHVBHF3FGFSFODF IUUQTEPDTTXJGUPSHTXJGUCPPLEPDVNFOUBUJPOUIFTXJGUQSPHSBNNJOHMBOHVBHFBCPVUUIFMBOHVBHFSFGFSFODF r 4XJGU.BDSPT IUUQTHJUIVCDPNLS[ZT[UPG[BCMPDLJ4XJGU.BDSPT