-
Notifications
You must be signed in to change notification settings - Fork 441
/
Copy pathSyntax+LexicalContext.swift
101 lines (89 loc) · 3.83 KB
/
Syntax+LexicalContext.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if swift(>=6)
public import SwiftSyntax
import SwiftSyntaxBuilder
#else
import SwiftSyntax
import SwiftSyntaxBuilder
#endif
extension SyntaxProtocol {
/// If this syntax node acts as a lexical context from the perspective
/// of a macro, return a new syntax node based on this node that strips all
/// information that isn't supposed to be exposed as a lexical context, such
/// as function bodies or the members of types/extensions.
///
/// Returns `nil` for any syntax node that isn't a lexical context.
public func asMacroLexicalContext() -> Syntax? {
switch Syntax(self).asProtocol(SyntaxProtocol.self) {
// Functions have their body removed.
case var function as WithOptionalCodeBlockSyntax & SyntaxProtocol:
function = function.detached
function.body = nil
return Syntax(function) as Syntax
// Closures have their body removed.
case var closure as ClosureExprSyntax:
closure = closure.detached
closure.statements = CodeBlockItemListSyntax()
return Syntax(closure)
// Nominal types and extensions have their member list cleared out.
case var typeOrExtension as HasTrailingMemberDeclBlock & SyntaxProtocol:
typeOrExtension = typeOrExtension.detached
typeOrExtension.memberBlock = MemberBlockSyntax(members: MemberBlockItemListSyntax())
return Syntax(typeOrExtension) as Syntax
// Subscripts have their accessors removed.
case var subscriptDecl as SubscriptDeclSyntax:
subscriptDecl = subscriptDecl.detached
subscriptDecl.accessorBlock = nil
return Syntax(subscriptDecl)
// Enum cases are fine as-is.
case is EnumCaseElementSyntax:
return Syntax(self.detached)
// Pattern bindings have their accessors and initializer removed.
case var patternBinding as PatternBindingSyntax:
patternBinding = patternBinding.detached
patternBinding.accessorBlock = nil
patternBinding.initializer = nil
return Syntax(patternBinding)
// Freestanding macros are fine as-is because if any arguments change
// the whole macro would have to be re-evaluated.
case let freestandingMacro as FreestandingMacroExpansionSyntax:
return Syntax(freestandingMacro.detached) as Syntax
default:
return nil
}
}
/// Return an array of enclosing lexical contexts for the purpose of macros,
/// from the innermost enclosing lexical context (first in the array) to the
/// outermost. If this syntax node itself is a lexical context, it will be
/// the innermost lexical context.
///
/// - Parameter enclosingSyntax: provides a parent node when the operation
/// has reached the outermost syntax node (i.e., it has no parent), allowing
/// the caller to provide a new syntax node that can continue the walk
/// to collect additional lexical contexts, e.g., from outer macro
/// expansions.
/// - Returns: the array of enclosing lexical contexts.
public func allMacroLexicalContexts(
enclosingSyntax: (Syntax) -> Syntax? = { _ in nil }
) -> [Syntax] {
var parentContexts: [Syntax] = []
var currentNode = Syntax(self)
while let parentNode = currentNode.parent ?? enclosingSyntax(currentNode) {
if let parentContext = parentNode.asMacroLexicalContext() {
parentContexts.append(parentContext)
}
currentNode = parentNode
}
return parentContexts
}
}