|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +import SwiftSyntax |
| 14 | +import SwiftSyntaxBuilder |
| 15 | +import SyntaxSupport |
| 16 | +import Utils |
| 17 | + |
| 18 | +let syntaxVisitorFile = SourceFile { |
| 19 | + EnumDecl(""" |
| 20 | + /// The enum describes how the SyntaxVistor should continue after visiting |
| 21 | + /// the current node. |
| 22 | + public enum SyntaxVisitorContinueKind |
| 23 | + """) { |
| 24 | + EnumCaseDecl(""" |
| 25 | + /// The visitor should visit the descendents of the current node. |
| 26 | + case visitChildren |
| 27 | + """) |
| 28 | + |
| 29 | + EnumCaseDecl(""" |
| 30 | + /// The visitor should avoid visiting the descendents of the current node. |
| 31 | + case skipChildren |
| 32 | + """) |
| 33 | + } |
| 34 | + |
| 35 | + ClassDecl("open class SyntaxVisitor") { |
| 36 | + VariableDecl("public let viewMode: SyntaxTreeViewMode") |
| 37 | + |
| 38 | + InitializerDecl(""" |
| 39 | + @available(*, deprecated, message: "Use init(viewMode:) instead") |
| 40 | + public convenience init() { |
| 41 | + self.init(viewMode: .sourceAccurate) |
| 42 | + } |
| 43 | + """) |
| 44 | + |
| 45 | + InitializerDecl(""" |
| 46 | + public init(viewMode: SyntaxTreeViewMode) { |
| 47 | + self.viewMode = viewMode |
| 48 | + } |
| 49 | + """) |
| 50 | + |
| 51 | + FunctionDecl(""" |
| 52 | + /// Walk all nodes of the given syntax tree, calling the corresponding `visit` |
| 53 | + /// function for every node that is being visited. |
| 54 | + public func walk<SyntaxType: SyntaxProtocol>(_ node: SyntaxType) { |
| 55 | + visit(node.data) |
| 56 | + } |
| 57 | + """) |
| 58 | + |
| 59 | + for node in SYNTAX_NODES where node.isVisitable { |
| 60 | + FunctionDecl(""" |
| 61 | + /// Visiting `\(raw: node.name)` specifically. |
| 62 | + /// - Parameter node: the node we are visiting. |
| 63 | + /// - Returns: how should we continue visiting. |
| 64 | + open func visit(_ node: \(raw: node.name)) -> SyntaxVisitorContinueKind { |
| 65 | + return .visitChildren |
| 66 | + } |
| 67 | + """) |
| 68 | + |
| 69 | + FunctionDecl(""" |
| 70 | + /// The function called after visiting `\(raw: node.name)` and its descendents. |
| 71 | + /// - node: the node we just finished visiting. |
| 72 | + open func visitPost(_ node: \(raw: node.name)) {} |
| 73 | + """) |
| 74 | + } |
| 75 | + |
| 76 | + FunctionDecl(""" |
| 77 | + /// Visiting `TokenSyntax` specifically. |
| 78 | + /// - Parameter node: the node we are visiting. |
| 79 | + /// - Returns: how should we continue visiting. |
| 80 | + open func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind { |
| 81 | + return .visitChildren |
| 82 | + } |
| 83 | + """) |
| 84 | + |
| 85 | + FunctionDecl(""" |
| 86 | + /// The function called after visiting the node and its descendents. |
| 87 | + /// - node: the node we just finished visiting. |
| 88 | + open func visitPost(_ node: TokenSyntax) {} |
| 89 | + """) |
| 90 | + |
| 91 | + for node in NON_BASE_SYNTAX_NODES { |
| 92 | + FunctionDecl(""" |
| 93 | + /// Implementation detail of doVisit(_:_:). Do not call directly. |
| 94 | + private func visitImpl\(raw: node.name)(_ data: SyntaxData) { |
| 95 | + let node = \(raw: node.name)(data) |
| 96 | + let needsChildren = (visit(node) == .visitChildren) |
| 97 | + // Avoid calling into visitChildren if possible. |
| 98 | + if needsChildren && !node.raw.layoutView!.children.isEmpty { |
| 99 | + visitChildren(node) |
| 100 | + } |
| 101 | + visitPost(node) |
| 102 | + } |
| 103 | + """) |
| 104 | + } |
| 105 | + |
| 106 | + FunctionDecl("private func visit(_ data: SyntaxData)") { |
| 107 | + SwitchStmt(expression: Expr("data.raw.kind")) { |
| 108 | + SwitchCase("case .token:") { |
| 109 | + VariableDecl("let node = TokenSyntax(data)") |
| 110 | + |
| 111 | + Expr("_ = visit(node)") |
| 112 | + FunctionCallExpr(""" |
| 113 | + // No children to visit. |
| 114 | + visitPost(node) |
| 115 | + // The implementation of every generated case goes into its own function. This |
| 116 | + // circumvents an issue where the compiler allocates stack space for every |
| 117 | + // case statement next to each other in debug builds, causing it to allocate |
| 118 | + // ~50KB per call to this function. rdar://55929175 |
| 119 | + """) |
| 120 | + } |
| 121 | + for node in NON_BASE_SYNTAX_NODES { |
| 122 | + SwitchCase("case .\(raw: node.swiftSyntaxKind):") { |
| 123 | + FunctionCallExpr("visitImpl\(raw: node.name)(data)") |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + FunctionDecl(""" |
| 130 | + private func visitChildren<SyntaxType: SyntaxProtocol>(_ node: SyntaxType) { |
| 131 | + let syntaxNode = Syntax(node) |
| 132 | + for childRaw in NonNilRawSyntaxChildren(syntaxNode, viewMode: viewMode) { |
| 133 | + let childData = SyntaxData(childRaw, parent: syntaxNode) |
| 134 | + visit(childData) |
| 135 | + } |
| 136 | + } |
| 137 | + """) |
| 138 | + } |
| 139 | +} |
0 commit comments