diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt
index 5c2b0520b92..7c00da25f0e 100644
--- a/Sources/CMakeLists.txt
+++ b/Sources/CMakeLists.txt
@@ -11,6 +11,7 @@ add_subdirectory(_SwiftSyntaxCShims)
add_subdirectory(SwiftBasicFormat)
add_subdirectory(SwiftSyntax)
add_subdirectory(SwiftDiagnostics)
+add_subdirectory(SwiftLexicalLookup)
add_subdirectory(SwiftLibraryPluginProvider)
add_subdirectory(SwiftParser)
add_subdirectory(SwiftParserDiagnostics)
diff --git a/Sources/SwiftLexicalLookup/CMakeLists.txt b/Sources/SwiftLexicalLookup/CMakeLists.txt
new file mode 100644
index 00000000000..b6cd9204fc3
--- /dev/null
+++ b/Sources/SwiftLexicalLookup/CMakeLists.txt
@@ -0,0 +1,29 @@
+# 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 http://swift.org/LICENSE.txt for license information
+# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
+
+add_swift_syntax_library(SwiftLexicalLookup
+ IdentifiableSyntax.swift
+ LookupName.swift
+ LookupResult.swift
+ SimpleLookupQueries.swift
+
+ Configurations/FileScopeHandlingConfig.swift
+ Configurations/LookupConfig.swift
+
+ Scopes/GenericParameterScopeSyntax.swift
+ Scopes/IntroducingToSequentialParentScopeSyntax.swift
+ Scopes/ScopeImplementations.swift
+ Scopes/ScopeSyntax.swift
+ Scopes/SequentialScopeSyntax.swift
+ Scopes/TypeScopeSyntax.swift
+ Scopes/WithGenericParametersScopeSyntax.swift
+)
+
+target_link_swift_syntax_libraries(SwiftLexicalLookup PUBLIC
+ SwiftSyntax)
+
diff --git a/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift b/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift
index 893ccc3e914..45aa35e166f 100644
--- a/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift
+++ b/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift
@@ -25,6 +25,12 @@ import SwiftSyntax
}
}
+@_spi(Experimental) extension FunctionParameterSyntax: IdentifiableSyntax {
+ @_spi(Experimental) public var identifier: TokenSyntax {
+ secondName ?? firstName
+ }
+}
+
@_spi(Experimental) extension ClosureShorthandParameterSyntax: IdentifiableSyntax {
@_spi(Experimental) public var identifier: TokenSyntax {
name
@@ -42,3 +48,15 @@ import SwiftSyntax
name
}
}
+
+@_spi(Experimental) extension GenericParameterSyntax: IdentifiableSyntax {
+ @_spi(Experimental) public var identifier: TokenSyntax {
+ name
+ }
+}
+
+@_spi(Experimental) extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax {
+ @_spi(Experimental) public var identifier: TokenSyntax {
+ name
+ }
+}
diff --git a/Sources/SwiftLexicalLookup/Scopes/GenericParameterScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/GenericParameterScopeSyntax.swift
new file mode 100644
index 00000000000..eac1a67c824
--- /dev/null
+++ b/Sources/SwiftLexicalLookup/Scopes/GenericParameterScopeSyntax.swift
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 introduces generic parameter names and directs
+/// futher lookup to its `WithGenericParametersScopeSyntax`
+/// parent scope's parent scope (i.e. on return, bypasses names
+/// introduced by its parent).
+@_spi(Experimental) public protocol GenericParameterScopeSyntax: ScopeSyntax {}
+
+@_spi(Experimental) extension GenericParameterScopeSyntax {
+ /// Returns names matching lookup and bypasses
+ /// `WithGenericParametersScopeSyntax` parent scope in futher lookup.
+ ///
+ /// ### Example
+ /// ```swift
+ /// let a = 23
+ /// func foo(a: A) {
+ /// a // <-- start lookup here
+ /// }
+ /// ```
+ /// When starting lookup at the `a` reference,
+ /// lookup first visits the code block scope associated
+ /// with the function's body. Then, it's forwarded to the
+ /// function declaration scope and then to generic parameter
+ /// scope (`WithGenericParametersScopeSyntax`).
+ /// Then, to ensure there is no infinite cycle,
+ /// this method passes lookup to function scope's parent scope
+ /// (in this case: file scope).
+ @_spi(Experimental) public func lookup(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ return defaultLookupImplementation(
+ identifier,
+ at: lookUpPosition,
+ with: config,
+ propagateToParent: false
+ )
+ + lookupBypassingParentResults(
+ identifier,
+ at: lookUpPosition,
+ with: config
+ )
+ }
+
+ /// Bypasses names introduced by `WithGenericParametersScopeSyntax` parent scope.
+ ///
+ /// ### Example
+ /// ```swift
+ /// let a = 23
+ /// func foo(a: A) {
+ /// a // <-- start lookup here
+ /// }
+ /// ```
+ /// When starting lookup at the `a` reference,
+ /// lookup first visits the code block scope associated
+ /// with the function's body. Then, it's forwarded to the
+ /// function declaration scope and then to generic parameter
+ /// scope (`WithGenericParametersScopeSyntax`).
+ /// Then, to ensure there is no infinite cycle,
+ /// we use this method instead of the standard `lookupInParent`
+ /// to pass lookup to the function scope's parent scope (in this case: file scope)
+ /// and effectively bypass names already looked up before.
+ private func lookupBypassingParentResults(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ guard let parentScope else { return [] }
+
+ if let parentScope = Syntax(parentScope).asProtocol(SyntaxProtocol.self)
+ as? WithGenericParametersScopeSyntax
+ {
+ return parentScope.lookupInParent(identifier, at: lookUpPosition, with: config)
+ } else {
+ return lookupInParent(identifier, at: lookUpPosition, with: config)
+ }
+ }
+}
diff --git a/Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/IntroducingToSequentialParentScopeSyntax.swift
similarity index 100%
rename from Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift
rename to Sources/SwiftLexicalLookup/Scopes/IntroducingToSequentialParentScopeSyntax.swift
diff --git a/Sources/SwiftLexicalLookup/ScopeImplementations.swift b/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift
similarity index 73%
rename from Sources/SwiftLexicalLookup/ScopeImplementations.swift
rename to Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift
index 187e42e4793..5c59f8ba66c 100644
--- a/Sources/SwiftLexicalLookup/ScopeImplementations.swift
+++ b/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift
@@ -176,7 +176,7 @@ import SwiftSyntax
/// All names introduced by the closure signature.
/// Could be closure captures or (shorthand) parameters.
///
- /// Example:
+ /// ### Example
/// ```swift
/// let x = { [weak self, a] b, _ in
/// // <--
@@ -222,7 +222,7 @@ import SwiftSyntax
/// Finds parent scope, omitting ancestor `if` statements if part of their `else if` clause.
///
- /// Example:
+ /// ### Example
/// ```swift
/// func foo() {
/// if let a = x {
@@ -262,7 +262,7 @@ import SwiftSyntax
/// Lookup triggered from inside of `else`
/// clause is immediately forwarded to parent scope.
///
- /// Example:
+ /// ### Example
/// ```swift
/// if let a = x {
/// // <-- a is visible here
@@ -290,6 +290,24 @@ import SwiftSyntax
LookupName.getNames(from: member.decl)
}
}
+
+ /// Creates a result from associated type declarations
+ /// made by it's members.
+ func lookupAssociatedTypeDeclarations(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ let filteredNames = members.flatMap { member in
+ guard member.decl.kind == .associatedTypeDecl else { return [LookupName]() }
+
+ return LookupName.getNames(from: member.decl)
+ }.filter { name in
+ checkIdentifier(identifier, refersTo: name, at: lookUpPosition)
+ }
+
+ return filteredNames.isEmpty ? [] : [.fromScope(self, withNames: filteredNames)]
+ }
}
@_spi(Experimental) extension GuardStmtSyntax: IntroducingToSequentialParentScopeSyntax {
@@ -308,7 +326,7 @@ import SwiftSyntax
/// Lookup triggered from within of the `else` body
/// returns no names.
///
- /// Example:
+ /// ### Example
/// ```swift
/// guard let a = x else {
/// return // a is not visible here
@@ -330,10 +348,10 @@ import SwiftSyntax
}
}
-@_spi(Experimental) extension ActorDeclSyntax: TypeScopeSyntax {}
-@_spi(Experimental) extension ClassDeclSyntax: TypeScopeSyntax {}
-@_spi(Experimental) extension StructDeclSyntax: TypeScopeSyntax {}
-@_spi(Experimental) extension EnumDeclSyntax: TypeScopeSyntax {}
+@_spi(Experimental) extension ActorDeclSyntax: TypeScopeSyntax, WithGenericParametersScopeSyntax {}
+@_spi(Experimental) extension ClassDeclSyntax: TypeScopeSyntax, WithGenericParametersScopeSyntax {}
+@_spi(Experimental) extension StructDeclSyntax: TypeScopeSyntax, WithGenericParametersScopeSyntax {}
+@_spi(Experimental) extension EnumDeclSyntax: TypeScopeSyntax, WithGenericParametersScopeSyntax {}
@_spi(Experimental) extension ExtensionDeclSyntax: TypeScopeSyntax {}
@_spi(Experimental) extension AccessorDeclSyntax: ScopeSyntax {
@@ -356,7 +374,95 @@ import SwiftSyntax
@_spi(Experimental) extension CatchClauseSyntax: ScopeSyntax {
/// Implicit `error` when there are no catch items.
- public var introducedNames: [LookupName] {
+ @_spi(Experimental) public var introducedNames: [LookupName] {
return catchItems.isEmpty ? [.implicit(.error(self))] : []
}
}
+
+@_spi(Experimental) extension SwitchCaseSyntax: ScopeSyntax {
+ /// Names introduced within `case` items.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ label.as(SwitchCaseLabelSyntax.self)?.caseItems.flatMap { child in
+ LookupName.getNames(from: child.pattern)
+ } ?? []
+ }
+}
+
+@_spi(Experimental) extension ProtocolDeclSyntax: ScopeSyntax {
+ /// Protocol declarations don't introduce names by themselves.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ []
+ }
+
+ /// For the lookup initiated from inside primary
+ /// associated type clause, this function also finds
+ /// all associated type declarations made inside the
+ /// protocol member block.
+ ///
+ /// ### Example
+ /// ```swift
+ /// class A {}
+ ///
+ /// protocol Foo*/> {
+ /// associatedtype A
+ /// class A {}
+ /// }
+ /// ```
+ /// For the lookup started at the primary associated type `A`,
+ /// the function returns exactly two results. First associated with the member
+ /// block that consists of the `associatedtype A` declaration and
+ /// the latter one from the file scope and `class A` exactly in this order.
+ public func lookup(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ var results: [LookupResult] = []
+
+ if let primaryAssociatedTypeClause,
+ primaryAssociatedTypeClause.range.contains(lookUpPosition)
+ {
+ results = memberBlock.lookupAssociatedTypeDeclarations(
+ identifier,
+ at: lookUpPosition,
+ with: config
+ )
+ }
+
+ return results + defaultLookupImplementation(identifier, at: lookUpPosition, with: config)
+ }
+}
+
+@_spi(Experimental) extension GenericParameterClauseSyntax: GenericParameterScopeSyntax {
+ /// Generic parameter names introduced by this clause.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ parameters.children(viewMode: .fixedUp).flatMap { child in
+ LookupName.getNames(from: child, accessibleAfter: child.endPosition)
+ }
+ }
+}
+
+@_spi(Experimental) extension FunctionDeclSyntax: WithGenericParametersScopeSyntax {
+ /// Function parameters introduced by this function's signature.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ signature.parameterClause.parameters.flatMap { parameter in
+ LookupName.getNames(from: parameter)
+ }
+ }
+}
+
+@_spi(Experimental) extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax {
+ /// Parameters introduced by this subscript.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ parameterClause.parameters.flatMap { parameter in
+ LookupName.getNames(from: parameter)
+ }
+ }
+}
+
+@_spi(Experimental) extension TypeAliasDeclSyntax: WithGenericParametersScopeSyntax {
+ /// Type alias doesn't introduce any names to it's children.
+ @_spi(Experimental) public var introducedNames: [LookupName] {
+ []
+ }
+}
diff --git a/Sources/SwiftLexicalLookup/ScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift
similarity index 93%
rename from Sources/SwiftLexicalLookup/ScopeSyntax.swift
rename to Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift
index a407f8fcb0e..c1b6c43d59e 100644
--- a/Sources/SwiftLexicalLookup/ScopeSyntax.swift
+++ b/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift
@@ -87,7 +87,8 @@ extension SyntaxProtocol {
func defaultLookupImplementation(
_ identifier: Identifier?,
at lookUpPosition: AbsolutePosition,
- with config: LookupConfig
+ with config: LookupConfig,
+ propagateToParent: Bool = true
) -> [LookupResult] {
let filteredNames =
introducedNames
@@ -95,12 +96,9 @@ extension SyntaxProtocol {
checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition)
}
- if filteredNames.isEmpty {
- return lookupInParent(identifier, at: lookUpPosition, with: config)
- } else {
- return [.fromScope(self, withNames: filteredNames)]
- + lookupInParent(identifier, at: lookUpPosition, with: config)
- }
+ let fromThisScope = filteredNames.isEmpty ? [] : [LookupResult.fromScope(self, withNames: filteredNames)]
+
+ return fromThisScope + (propagateToParent ? lookupInParent(identifier, at: lookUpPosition, with: config) : [])
}
/// Looks up in parent scope.
diff --git a/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift
similarity index 99%
rename from Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift
rename to Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift
index d8a87c3dc3d..da96821373e 100644
--- a/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift
+++ b/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift
@@ -22,7 +22,7 @@ extension SequentialScopeSyntax {
/// and included `IntroducingToSequentialParentScopeSyntax` children
/// scopes that match the lookup.
///
- /// Example:
+ /// ### Example
/// ```swift
/// func foo() {
/// let a = 1
diff --git a/Sources/SwiftLexicalLookup/TypeScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/TypeScopeSyntax.swift
similarity index 100%
rename from Sources/SwiftLexicalLookup/TypeScopeSyntax.swift
rename to Sources/SwiftLexicalLookup/Scopes/TypeScopeSyntax.swift
diff --git a/Sources/SwiftLexicalLookup/Scopes/WithGenericParametersScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/WithGenericParametersScopeSyntax.swift
new file mode 100644
index 00000000000..5ab700bfe4b
--- /dev/null
+++ b/Sources/SwiftLexicalLookup/Scopes/WithGenericParametersScopeSyntax.swift
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+@_spi(Experimental) public protocol WithGenericParametersScopeSyntax: ScopeSyntax {
+ var genericParameterClause: GenericParameterClauseSyntax? { get }
+}
+
+@_spi(Experimental) extension WithGenericParametersScopeSyntax {
+ /// Returns names matching lookup and passes lookup to
+ /// the generic parameter clause scopes.
+ ///
+ /// ### Example
+ /// ```swift
+ /// let a = 23
+ /// func foo(a: A) {
+ /// a // <-- start lookup here
+ /// }
+ /// ```
+ /// When starting lookup at the `a` reference,
+ /// lookup first visits the code block scope associated
+ /// with the function's body. Then, it's forwarded to the
+ /// function declaration scope and then to generic parameter
+ /// scope (`WithGenericParametersScopeSyntax`)
+ /// instead of it's actual parent scope (in this case: file scope).
+ @_spi(Experimental) public func lookup(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ return defaultLookupImplementation(
+ identifier,
+ at: position,
+ with: config,
+ propagateToParent: false
+ )
+ + lookupThroughGenericParameterScope(
+ identifier,
+ at: lookUpPosition,
+ with: config
+ )
+ }
+
+ /// Passes lookup to this scope's generic parameter or
+ /// primary associated type clause scope (`WithGenericParametersScopeSyntax`).
+ ///
+ /// ### Example
+ /// ```swift
+ /// let a = 23
+ /// func foo(a: A) {
+ /// a // <-- start lookup here
+ /// }
+ /// ```
+ /// When starting lookup at the `a` reference,
+ /// lookup first visits the code block scope associated
+ /// with the function's body. Then, it's forwarded to the
+ /// function declaration scope and then to generic parameter
+ /// scope (`WithGenericParametersScopeSyntax`)
+ /// with this method (instead of using standard `lookupInParent`).
+ private func lookupThroughGenericParameterScope(
+ _ identifier: Identifier?,
+ at lookUpPosition: AbsolutePosition,
+ with config: LookupConfig
+ ) -> [LookupResult] {
+ if let genericParameterClause {
+ return genericParameterClause.lookup(identifier, at: lookUpPosition, with: config)
+ } else {
+ return lookupInParent(identifier, at: lookUpPosition, with: config)
+ }
+ }
+}
diff --git a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift
index e3f15a917a9..ff96b8230d9 100644
--- a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift
+++ b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift
@@ -835,4 +835,187 @@ final class testNameLookup: XCTestCase {
]
)
}
+
+ func testSwitchExpression() {
+ assertLexicalNameLookup(
+ source: """
+ switch {
+ case .x(let 1️⃣a, let 2️⃣b), .y(.c(let 3️⃣c), .z):
+ print(4️⃣a, 5️⃣b, 6️⃣c)
+ case .z(let 7️⃣a), .smth(let 8️⃣a)
+ print(9️⃣a)
+ default:
+ print(0️⃣a)
+ }
+ """,
+ references: [
+ "4️⃣": [.fromScope(SwitchCaseSyntax.self, expectedNames: ["1️⃣"])],
+ "5️⃣": [.fromScope(SwitchCaseSyntax.self, expectedNames: ["2️⃣"])],
+ "6️⃣": [.fromScope(SwitchCaseSyntax.self, expectedNames: ["3️⃣"])],
+ "9️⃣": [.fromScope(SwitchCaseSyntax.self, expectedNames: ["7️⃣", "8️⃣"])],
+ "0️⃣": [],
+ ],
+ expectedResultTypes: .all(IdentifierPatternSyntax.self)
+ )
+ }
+
+ func testSimpleGenericParameterScope() {
+ assertLexicalNameLookup(
+ source: """
+ class A<1️⃣T1, 2️⃣T2> {
+ let 7️⃣x: 3️⃣T1 = v
+ let y: 4️⃣T2 = v
+
+ class B<5️⃣T1> {
+ let z: 6️⃣T1 = v
+
+ func test() {
+ print(8️⃣x)
+ }
+ }
+ }
+ """,
+ references: [
+ "3️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"])],
+ "4️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"])],
+ "6️⃣": [
+ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["5️⃣"]),
+ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]),
+ ],
+ "8️⃣": [.fromScope(MemberBlockSyntax.self, expectedNames: ["7️⃣"])],
+ ],
+ expectedResultTypes: .all(GenericParameterSyntax.self, except: ["7️⃣": IdentifierPatternSyntax.self])
+ )
+ }
+
+ func testGenericParameterOrdering() {
+ assertLexicalNameLookup(
+ source: """
+ class Foo<1️⃣A: 2️⃣A, B: 3️⃣A, 4️⃣C: 5️⃣D, D: 6️⃣C> {}
+ """,
+ references: [
+ "2️⃣": [],
+ "3️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"])],
+ "5️⃣": [],
+ "6️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"])],
+ ],
+ expectedResultTypes: .all(GenericParameterSyntax.self)
+ )
+ }
+
+ func testPrimaryAssociatedTypes() {
+ assertLexicalNameLookup(
+ source: """
+ 0️⃣class A {}
+
+ protocol Foo<1️⃣A, 2️⃣B> {
+ 3️⃣associatedtype A
+ 4️⃣associatedtype B
+
+ class A {}
+ class B {}
+ }
+ """,
+ references: [
+ "1️⃣": [
+ .fromScope(MemberBlockSyntax.self, expectedNames: ["3️⃣"]),
+ .fromFileScope(expectedNames: ["0️⃣"]),
+ ],
+ "2️⃣": [
+ .fromScope(MemberBlockSyntax.self, expectedNames: ["4️⃣"])
+ ],
+ ],
+ expectedResultTypes: .all(
+ AssociatedTypeDeclSyntax.self,
+ except: [
+ "0️⃣": ClassDeclSyntax.self
+ ]
+ )
+ )
+ }
+
+ func testFunctionDeclarationScope() {
+ assertLexicalNameLookup(
+ source: """
+ class X<1️⃣A> {
+ let 2️⃣a: A
+
+ func foo<3️⃣A, 4️⃣B>(5️⃣a: 6️⃣A, 7️⃣b: 8️⃣B) -> 9️⃣B {
+ return 0️⃣a + 🔟b
+ }
+ }
+ """,
+ references: [
+ "6️⃣": [
+ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["3️⃣"]),
+ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]),
+ ],
+ "8️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"])],
+ "9️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"])],
+ "0️⃣": [
+ .fromScope(FunctionDeclSyntax.self, expectedNames: ["5️⃣"]),
+ .fromScope(MemberBlockSyntax.self, expectedNames: ["2️⃣"]),
+ ],
+ "🔟": [.fromScope(FunctionDeclSyntax.self, expectedNames: ["7️⃣"])],
+ ],
+ expectedResultTypes: .all(
+ GenericParameterSyntax.self,
+ except: [
+ "2️⃣": IdentifierPatternSyntax.self,
+ "5️⃣": FunctionParameterSyntax.self,
+ "7️⃣": FunctionParameterSyntax.self,
+ ]
+ )
+ )
+ }
+
+ func testSubscript() {
+ assertLexicalNameLookup(
+ source: """
+ class X {
+ let 0️⃣c = 123
+
+ subscript<1️⃣A, 2️⃣B>(3️⃣a: 4️⃣A, 5️⃣b: 6️⃣B) -> 7️⃣B {
+ return 8️⃣a + 9️⃣b + 🔟c
+ }
+ }
+ """,
+ references: [
+ "4️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"])],
+ "6️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"])],
+ "7️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"])],
+ "8️⃣": [.fromScope(SubscriptDeclSyntax.self, expectedNames: ["3️⃣"])],
+ "9️⃣": [.fromScope(SubscriptDeclSyntax.self, expectedNames: ["5️⃣"])],
+ "🔟": [.fromScope(MemberBlockSyntax.self, expectedNames: ["0️⃣"])],
+ ],
+ expectedResultTypes: .all(
+ GenericParameterSyntax.self,
+ except: [
+ "0️⃣": IdentifierPatternSyntax.self,
+ "3️⃣": FunctionParameterSyntax.self,
+ "5️⃣": FunctionParameterSyntax.self,
+ ]
+ )
+ )
+ }
+
+ func testTypealias() {
+ assertLexicalNameLookup(
+ source: """
+ typealias SomeType<1️⃣A> = X<2️⃣A, 3️⃣NoMatch>
+
+ 7️⃣typealias SomeOtherType<4️⃣A> = X<5️⃣A, 6️⃣SomeOtherType>
+ """,
+ references: [
+ "2️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"])],
+ "3️⃣": [],
+ "5️⃣": [.fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"])],
+ "6️⃣": [.fromFileScope(expectedNames: ["7️⃣"])],
+ ],
+ expectedResultTypes: .all(
+ GenericParameterSyntax.self,
+ except: ["7️⃣": TypeAliasDeclSyntax.self]
+ )
+ )
+ }
}