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] + ) + ) + } }