File: Syntax%2BLexicalContext.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (101 lines) | stat: -rw-r--r-- 3,926 bytes parent folder | download
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
  }
}