diff --git a/Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift b/Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift index 5d58aa7a87b..9163ffa7359 100644 --- a/Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift @@ -19,8 +19,8 @@ protocol IntroducingToSequentialParentScopeSyntax: ScopeSyntax { /// Returns results matching lookup that should be /// interleaved with results of the sequential parent. func lookupFromSequentialParent( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] } diff --git a/Sources/SwiftLexicalLookup/LookupName.swift b/Sources/SwiftLexicalLookup/LookupName.swift index aac72f6c24f..5bd15a93bf0 100644 --- a/Sources/SwiftLexicalLookup/LookupName.swift +++ b/Sources/SwiftLexicalLookup/LookupName.swift @@ -110,10 +110,6 @@ import SwiftSyntax case declaration(NamedDeclSyntax) /// Name introduced implicitly by certain syntax nodes. case implicit(ImplicitDecl) - /// Explicit `self` keyword. - case `self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?) - /// Explicit `Self` keyword. - case `Self`(IdentifiableSyntax, accessibleAfter: AbsolutePosition?) /// Syntax associated with this name. @_spi(Experimental) public var syntax: SyntaxProtocol { @@ -124,8 +120,6 @@ import SwiftSyntax return syntax case .implicit(let implicitName): return implicitName.syntax - case .self(let syntax, _), .Self(let syntax, _): - return syntax } } @@ -138,10 +132,6 @@ import SwiftSyntax return Identifier(syntax.name) case .implicit(let kind): return kind.identifier - case .self: - return Identifier("self") - case .Self: - return Identifier("Self") } } @@ -149,9 +139,7 @@ import SwiftSyntax /// If set to `nil`, the name is available at any point in scope. var accessibleAfter: AbsolutePosition? { switch self { - case .identifier(_, let absolutePosition), - .self(_, let absolutePosition), - .Self(_, let absolutePosition): + case .identifier(_, let absolutePosition): return absolutePosition default: return nil @@ -159,15 +147,9 @@ import SwiftSyntax } /// Checks if this name was introduced before the syntax used for lookup. - func isAccessible(at origin: AbsolutePosition) -> Bool { + func isAccessible(at lookUpPosition: AbsolutePosition) -> Bool { guard let accessibleAfter else { return true } - return accessibleAfter <= origin - } - - /// Checks if this name refers to the looked up phrase. - func refersTo(_ lookedUpIdentifier: Identifier) -> Bool { - guard let identifier else { return false } - return identifier == lookedUpIdentifier + return accessibleAfter <= lookUpPosition } /// Extracts names introduced by the given `syntax` structure. @@ -229,16 +211,10 @@ import SwiftSyntax accessibleAfter: AbsolutePosition? = nil ) -> [LookupName] { switch identifiable.identifier.tokenKind { - case .keyword(.self): - return [.self(identifiable, accessibleAfter: accessibleAfter)] - case .keyword(.Self): - return [.Self(identifiable, accessibleAfter: accessibleAfter)] + case .wildcard: + return [] default: - if identifiable.identifier.tokenKind != .wildcard { - return [.identifier(identifiable, accessibleAfter: accessibleAfter)] - } else { - return [] - } + return [.identifier(identifiable, accessibleAfter: accessibleAfter)] } } diff --git a/Sources/SwiftLexicalLookup/LookupResult.swift b/Sources/SwiftLexicalLookup/LookupResult.swift index 3fb4c30b046..3bcc1ac2a8b 100644 --- a/Sources/SwiftLexicalLookup/LookupResult.swift +++ b/Sources/SwiftLexicalLookup/LookupResult.swift @@ -36,4 +36,14 @@ import SwiftSyntax return names } } + + /// Returns result specific for the particular `scope` kind with provided `names`. + static func getResult(for scope: ScopeSyntax, withNames names: [LookupName]) -> LookupResult { + switch Syntax(scope).as(SyntaxEnum.self) { + case .sourceFile(let sourceFileSyntax): + return .fromFileScope(sourceFileSyntax, withNames: names) + default: + return .fromScope(scope, withNames: names) + } + } } diff --git a/Sources/SwiftLexicalLookup/ScopeImplementations.swift b/Sources/SwiftLexicalLookup/ScopeImplementations.swift index 432f99b900f..187e42e4793 100644 --- a/Sources/SwiftLexicalLookup/ScopeImplementations.swift +++ b/Sources/SwiftLexicalLookup/ScopeImplementations.swift @@ -97,15 +97,15 @@ import SwiftSyntax /// - for `memberBlock` - a, b, c, d, e, f /// - for `codeBlock` - a @_spi(Experimental) public func lookup( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { switch config.fileScopeHandling { case .memberBlock: let names = introducedNames(using: .memberBlock) .filter { lookupName in - checkName(identifier, refersTo: lookupName, at: origin) + checkIdentifier(identifier, refersTo: lookupName, at: lookUpPosition) } return names.isEmpty ? [] : [.fromFileScope(self, withNames: names)] @@ -119,24 +119,22 @@ import SwiftSyntax if encounteredNonDeclaration { sequentialItems.append(codeBlockItem) + } else if item.is(DeclSyntax.self) { + let foundNames = LookupName.getNames(from: item) + members.append( + contentsOf: foundNames.filter { checkIdentifier(identifier, refersTo: $0, at: lookUpPosition) } + ) } else { - if item.is(DeclSyntax.self) { - let foundNames = LookupName.getNames(from: item) - - members.append(contentsOf: foundNames.filter { checkName(identifier, refersTo: $0, at: origin) }) - } else { - encounteredNonDeclaration = true - sequentialItems.append(codeBlockItem) - } + encounteredNonDeclaration = true + sequentialItems.append(codeBlockItem) } } let sequentialNames = sequentialLookup( in: sequentialItems, - for: identifier, - at: origin, - with: config, - createResultsForThisScopeWith: { .fromFileScope(self, withNames: $0) } + identifier, + at: lookUpPosition, + with: config ) return (members.isEmpty ? [] : [.fromFileScope(self, withNames: members)]) + sequentialNames @@ -154,16 +152,15 @@ import SwiftSyntax } @_spi(Experimental) public func lookup( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { sequentialLookup( in: statements, - for: identifier, - at: origin, - with: config, - createResultsForThisScopeWith: { .fromScope(self, withNames: $0) } + identifier, + at: lookUpPosition, + with: config ) } } @@ -274,14 +271,14 @@ import SwiftSyntax /// } /// ``` @_spi(Experimental) public func lookup( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { - if let elseBody, elseBody.position <= origin, elseBody.endPosition >= origin { - return lookupInParent(for: identifier, at: origin, with: config) + if let elseBody, elseBody.range.contains(lookUpPosition) { + return lookupInParent(identifier, at: lookUpPosition, with: config) } else { - return defaultLookupImplementation(for: identifier, at: origin, with: config) + return defaultLookupImplementation(identifier, at: lookUpPosition, with: config) } } } @@ -319,15 +316,14 @@ import SwiftSyntax /// // a is visible here /// ``` func lookupFromSequentialParent( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { - guard body.position > origin || body.endPosition < origin - else { return [] } + guard !body.range.contains(lookUpPosition) else { return [] } let names = namesIntroducedToSequentialParent.filter { introducedName in - checkName(identifier, refersTo: introducedName, at: origin) + checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition) } return names.isEmpty ? [] : [.fromScope(self, withNames: names)] diff --git a/Sources/SwiftLexicalLookup/ScopeSyntax.swift b/Sources/SwiftLexicalLookup/ScopeSyntax.swift index af9a247a40c..a407f8fcb0e 100644 --- a/Sources/SwiftLexicalLookup/ScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/ScopeSyntax.swift @@ -44,10 +44,10 @@ extension SyntaxProtocol { /// in this exact order. The constant declaration within the function body is omitted /// due to the ordering rules that prioritize visibility within the function body. @_spi(Experimental) public func lookup( - for identifier: Identifier?, + _ identifier: Identifier?, with config: LookupConfig = LookupConfig() ) -> [LookupResult] { - scope?.lookup(for: identifier, at: self.position, with: config) ?? [] + scope?.lookup(identifier, at: self.position, with: config) ?? [] } } @@ -58,10 +58,9 @@ extension SyntaxProtocol { var introducedNames: [LookupName] { get } /// Finds all declarations `identifier` refers to. `syntax` specifies the node lookup was triggered with. /// If `identifier` set to `nil`, returns all available names at the given node. - /// `state` represents lookup state passed between lookup methods. func lookup( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] } @@ -74,47 +73,50 @@ extension SyntaxProtocol { /// Returns `LookupResult` of all names introduced in this scope that `identifier` /// refers to and is accessible at given syntax node then passes lookup to the parent. /// If `identifier` set to `nil`, returns all available names at the given node. - /// `state` represents lookup state passed between lookup methods. @_spi(Experimental) public func lookup( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { - defaultLookupImplementation(for: identifier, at: origin, with: config) + defaultLookupImplementation(identifier, at: lookUpPosition, with: config) } /// Returns `LookupResult` of all names introduced in this scope that `identifier` /// refers to and is accessible at given syntax node then passes lookup to the parent. /// If `identifier` set to `nil`, returns all available names at the given node. func defaultLookupImplementation( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { let filteredNames = introducedNames .filter { introducedName in - checkName(identifier, refersTo: introducedName, at: origin) + checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition) } if filteredNames.isEmpty { - return lookupInParent(for: identifier, at: origin, with: config) + return lookupInParent(identifier, at: lookUpPosition, with: config) } else { return [.fromScope(self, withNames: filteredNames)] - + lookupInParent(for: identifier, at: origin, with: config) + + lookupInParent(identifier, at: lookUpPosition, with: config) } } /// Looks up in parent scope. func lookupInParent( - for identifier: Identifier?, - at origin: AbsolutePosition, + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, with config: LookupConfig ) -> [LookupResult] { - parentScope?.lookup(for: identifier, at: origin, with: config) ?? [] + parentScope?.lookup(identifier, at: lookUpPosition, with: config) ?? [] } - func checkName(_ name: Identifier?, refersTo introducedName: LookupName, at origin: AbsolutePosition) -> Bool { - introducedName.isAccessible(at: origin) && (name == nil || introducedName.refersTo(name!)) + func checkIdentifier( + _ identifier: Identifier?, + refersTo introducedName: LookupName, + at lookUpPosition: AbsolutePosition + ) -> Bool { + introducedName.isAccessible(at: lookUpPosition) && (identifier == nil || introducedName.identifier == identifier!) } } diff --git a/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift b/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift index 21f6de167cd..d8a87c3dc3d 100644 --- a/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift @@ -40,12 +40,19 @@ extension SequentialScopeSyntax { /// code block scope in this exact order. func sequentialLookup( in codeBlockItems: some Collection, - for identifier: Identifier?, - at origin: AbsolutePosition, - with config: LookupConfig, - createResultsForThisScopeWith getResults: ([LookupName]) -> (LookupResult) + _ identifier: Identifier?, + at lookUpPosition: AbsolutePosition, + with config: LookupConfig ) -> [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] = [] var itemsWithoutNamedDecl: [CodeBlockItemSyntax] = [] @@ -55,7 +62,7 @@ extension SequentialScopeSyntax { from: codeBlockItem.item, accessibleAfter: codeBlockItem.endPosition ).filter { introducedName in - checkName(identifier, refersTo: introducedName, at: origin) + checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition) } } else { itemsWithoutNamedDecl.append(codeBlockItem) @@ -63,15 +70,15 @@ extension SequentialScopeSyntax { } for codeBlockItem in itemsWithoutNamedDecl { - guard codeBlockItem.position < origin else { break } + guard codeBlockItem.position <= lookUpPosition else { break } if let introducingToParentScope = Syntax(codeBlockItem.item).asProtocol(SyntaxProtocol.self) as? IntroducingToSequentialParentScopeSyntax { // Get results from encountered scope. let introducedResults = introducingToParentScope.lookupFromSequentialParent( - for: identifier, - at: origin, + identifier, + at: lookUpPosition, with: config ) @@ -80,7 +87,7 @@ extension SequentialScopeSyntax { // If there are some names collected, create a new result for this scope. if !currentChunk.isEmpty { - results.append(getResults(currentChunk)) + results.append(LookupResult.getResult(for: self, withNames: currentChunk)) currentChunk = [] } @@ -91,16 +98,16 @@ extension SequentialScopeSyntax { from: codeBlockItem.item, accessibleAfter: codeBlockItem.endPosition ).filter { introducedName in - checkName(identifier, refersTo: introducedName, at: origin) + checkIdentifier(identifier, refersTo: introducedName, at: lookUpPosition) } } } // If there are some names collected, create a new result for this scope. if !currentChunk.isEmpty { - results.append(getResults(currentChunk)) + results.append(LookupResult.getResult(for: self, withNames: currentChunk)) } - return results.reversed() + lookupInParent(for: identifier, at: origin, with: config) + return results.reversed() + lookupInParent(identifier, at: lookUpPosition, with: config) } } diff --git a/Sources/SwiftSyntax/Identifier.swift b/Sources/SwiftSyntax/Identifier.swift index 97a588395e3..320e125e2b3 100644 --- a/Sources/SwiftSyntax/Identifier.swift +++ b/Sources/SwiftSyntax/Identifier.swift @@ -19,15 +19,16 @@ public struct Identifier: Equatable, Hashable, Sendable { @_spi(RawSyntax) public let raw: RawIdentifier - let arena: SyntaxArenaRef? + private let arena: SyntaxArenaRef? public init?(_ token: TokenSyntax) { - guard case .identifier = token.tokenKind else { + switch token.tokenKind { + case .identifier, .keyword(.self), .keyword(.Self): + self.raw = RawIdentifier(token.tokenView) + self.arena = token.raw.arenaReference + default: return nil } - - self.raw = RawIdentifier(token.tokenView) - self.arena = token.raw.arenaReference } public init(_ staticString: StaticString) { @@ -40,13 +41,6 @@ public struct Identifier: Equatable, Hashable, Sendable { } } -extension Identifier { - @_spi(Testing) public init(anyToken token: TokenSyntax) { - self.raw = RawIdentifier(token.tokenView.rawText) - self.arena = token.raw.arenaReference - } -} - @_spi(RawSyntax) public struct RawIdentifier: Equatable, Hashable, Sendable { public let name: SyntaxText @@ -64,10 +58,6 @@ public struct RawIdentifier: Equatable, Hashable, Sendable { } fileprivate init(_ staticString: StaticString) { - self.init(SyntaxText(staticString)) - } - - fileprivate init(_ syntaxText: SyntaxText) { - name = syntaxText + name = SyntaxText(staticString) } } diff --git a/Tests/SwiftLexicalLookupTest/Assertions.swift b/Tests/SwiftLexicalLookupTest/Assertions.swift index eb671c5ccce..ee70e4835c6 100644 --- a/Tests/SwiftLexicalLookupTest/Assertions.swift +++ b/Tests/SwiftLexicalLookupTest/Assertions.swift @@ -93,9 +93,9 @@ func assertLexicalNameLookup( assertLexicalScopeQuery( source: source, methodUnderTest: { marker, tokenAtMarker in - let lookupIdentifier = Identifier(tokenAtMarker) ?? Identifier(anyToken: tokenAtMarker) + let lookupIdentifier = Identifier(tokenAtMarker) - let result = tokenAtMarker.lookup(for: useNilAsTheParameter ? nil : lookupIdentifier, with: config) + let result = tokenAtMarker.lookup(useNilAsTheParameter ? nil : lookupIdentifier, with: config) guard let expectedValues = references[marker] else { XCTFail("For marker \(marker), couldn't find result expectation") diff --git a/Tests/SwiftLexicalLookupTest/ExpectedName.swift b/Tests/SwiftLexicalLookupTest/ExpectedName.swift index 8db81175767..4047ef90f3b 100644 --- a/Tests/SwiftLexicalLookupTest/ExpectedName.swift +++ b/Tests/SwiftLexicalLookupTest/ExpectedName.swift @@ -62,15 +62,11 @@ enum NameExpectation: ExpectedName { case identifier(String) case declaration(String) case implicit(ImplicitNameExpectation) - case `self`(String) - case `Self`(String) var marker: String { switch self { case .identifier(let marker), - .declaration(let marker), - .self(let marker), - .Self(let marker): + .declaration(let marker): return marker case .implicit(let implicitName): return implicitName.marker @@ -81,8 +77,6 @@ enum NameExpectation: ExpectedName { switch (name, self) { case (.identifier, .identifier): break case (.declaration, .declaration): break - case (.self, .self): break - case (.Self, .Self): break case (.implicit(let implicitName), .implicit(let implicitNameExpectation)): implicitNameExpectation.assertExpectation(marker: marker, for: implicitName) default: diff --git a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift index 121610a1ed9..e3f15a917a9 100644 --- a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift +++ b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift @@ -112,7 +112,7 @@ final class testNameLookup: XCTestCase { let 1️⃣a = 1 let 2️⃣b = 2 let 3️⃣x: (Int, Int, Int) = { 4️⃣a, _, 5️⃣c in - print(6️⃣a, 7️⃣b, 8️⃣c, 0️⃣$0) + print(6️⃣a, 7️⃣b, 8️⃣c) } 9️⃣x() } @@ -125,7 +125,6 @@ final class testNameLookup: XCTestCase { "7️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: ["2️⃣"])], "8️⃣": [.fromScope(ClosureExprSyntax.self, expectedNames: ["5️⃣"])], "9️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: ["3️⃣"])], - "0️⃣": [], ], expectedResultTypes: .all( IdentifierPatternSyntax.self, @@ -152,7 +151,7 @@ final class testNameLookup: XCTestCase { """, references: [ "5️⃣": [ - .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.`self`("2️⃣")]), + .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.identifier("2️⃣")]), .fromScope(ClassDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("7️⃣"))]), ], "6️⃣": [ @@ -179,7 +178,7 @@ final class testNameLookup: XCTestCase { let 1️⃣a = 1 let 2️⃣b = 2 let 3️⃣x = { (4️⃣a b: Int, 5️⃣c: Int) in - print(6️⃣a, 7️⃣b, 8️⃣c, 0️⃣$0) + print(6️⃣a, 7️⃣b, 8️⃣c) } 9️⃣x() } @@ -192,7 +191,6 @@ final class testNameLookup: XCTestCase { ], "8️⃣": [.fromScope(ClosureExprSyntax.self, expectedNames: ["5️⃣"])], "9️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: ["3️⃣"])], - "0️⃣": [], ], expectedResultTypes: .all( IdentifierPatternSyntax.self, @@ -659,12 +657,19 @@ final class testNameLookup: XCTestCase { 8️⃣oldValue } } + + var x: Int = 2 { + didSet(myNewValue) { + print(9️⃣newValue) + } + } """, references: [ "2️⃣": [.fromScope(AccessorDeclSyntax.self, expectedNames: [NameExpectation.implicit(.newValue("1️⃣"))])], "4️⃣": [.fromScope(AccessorDeclSyntax.self, expectedNames: [NameExpectation.identifier("3️⃣")])], "6️⃣": [.fromScope(AccessorDeclSyntax.self, expectedNames: [NameExpectation.implicit(.newValue("5️⃣"))])], "8️⃣": [.fromScope(AccessorDeclSyntax.self, expectedNames: [NameExpectation.implicit(.oldValue("7️⃣"))])], + "9️⃣": [], ] ) }