File: SequentialScopeSyntax.swift

package info (click to toggle)
swiftlang 6.1.3-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,791,644 kB
  • sloc: cpp: 9,901,738; ansic: 2,201,433; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (130 lines) | stat: -rw-r--r-- 5,035 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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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
//
//===----------------------------------------------------------------------===//

import SwiftSyntax

/// Scope that, in addition to names introduced by itself,
/// also handles names introduced by
/// `IntroducingToSequentialParentScopeSyntax` children scopes.
protocol SequentialScopeSyntax: ScopeSyntax {}

extension SequentialScopeSyntax {
  /// Returns names introduced by `codeBlockItems`
  /// and included `IntroducingToSequentialParentScopeSyntax` children
  /// scopes that match the lookup.
  ///
  /// ### Example
  /// ```swift
  /// func foo() {
  ///   let a = 1
  ///   guard let a = x else { return }
  ///   let a = a // <-- 1
  ///   guard let a = y else { return }
  ///   a // <-- 2
  /// }
  /// ```
  /// For the first `a` reference, sequential lookup returns
  /// two results: from `guard` scope and from code block scope
  /// in this exact order. For the second `a` reference,
  /// sequential lookup yields results from four scopes starting
  /// from the bottom: `guard`, code block, `guard` and
  /// code block scope in this exact order.
  func sequentialLookup(
    in codeBlockItems: some Collection<CodeBlockItemSyntax>,
    _ identifier: Identifier?,
    at lookUpPosition: AbsolutePosition,
    with config: LookupConfig,
    ignoreNamedDecl: Bool = false,
    propagateToParent: Bool = true
  ) -> [LookupResult] {
    // Sequential scope needs to ensure all type declarations are
    // available in the whole scope (first loop) and
    // then that results from IntroducingToSequentialParentScopeSyntax
    // are properly interleaved with the results produced by this scope.
    var results: [LookupResult] = []
    // We need to use currentChunk because we
    // can't add the names directly to results
    // as we need to partition them based on results
    // obtained from IntroducingToSequentialParentScopeSyntax
    var currentChunk: [LookupName] = []
    // During first iteration, the algorithm collects all named
    // decls from the code block and (nested) active clauses
    // of if config declarations. After the first pass, performs
    // name matching on these and appends as a separate result to the results.
    var collectedNamedDecls: [NamedDeclSyntax] = []

    for codeBlockItem in codeBlockItems.reversed() {
      if let namedDecl = codeBlockItem.item.asProtocol(NamedDeclSyntax.self) {
        guard !ignoreNamedDecl else { continue }

        collectedNamedDecls.append(namedDecl)
        continue
      } else if let ifConfigDecl = codeBlockItem.item.as(IfConfigDeclSyntax.self),
        !ignoreNamedDecl
      {
        collectedNamedDecls += ifConfigDecl.getNamedDecls(for: config)
      }

      if let introducingToParentScope = Syntax(codeBlockItem.item).asProtocol(SyntaxProtocol.self)
        as? IntroducingToSequentialParentScopeSyntax
      {
        // Get results from encountered scope.
        let introducedResults = introducingToParentScope.lookupFromSequentialParent(
          identifier,
          at: lookUpPosition,
          with: config
        )

        // Skip, if no results were found.
        guard !introducedResults.isEmpty else { continue }

        // If there are some names collected, create a new result for this scope.
        if !currentChunk.isEmpty {
          results.append(LookupResult.getResult(for: self, withNames: currentChunk))
          currentChunk = []
        }

        results += introducedResults
      } else {
        // Extract new names from encountered node.
        currentChunk += LookupName.getNames(
          from: codeBlockItem.item,
          accessibleAfter: codeBlockItem.item.endPosition
        ).filter { introducedName in
          checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition)
        }
      }
    }

    // If there are some names collected, create a new result for this scope.
    if !currentChunk.isEmpty {
      results.append(LookupResult.getResult(for: self, withNames: currentChunk))
      currentChunk = []
    }

    // Filter named decls to be appended to the results.
    for namedDecl in collectedNamedDecls.reversed() {
      currentChunk += LookupName.getNames(
        from: namedDecl,
        accessibleAfter: namedDecl.endPosition
      ).filter { introducedName in
        checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition)
      }
    }

    results += LookupResult.getResultArray(for: self, withNames: currentChunk)

    return results
      + (config.finishInSequentialScope || !propagateToParent
        ? [] : lookupInParent(identifier, at: lookUpPosition, with: config))
  }
}