diff --git a/Release Notes/602.md b/Release Notes/602.md index 5ddcbd22511..ffc24f54a83 100644 --- a/Release Notes/602.md +++ b/Release Notes/602.md @@ -7,6 +7,11 @@ - Pull Request: https://github.com/swiftlang/swift-syntax/pull/2981 - Migration steps: None required. The new `category` property has optional type, and there is a default implementation that returns `nil`. Types that conform to `DiagnosticMessage` can choose to implement this property and provide a category when appropriate. +- `SwiftLexicalLookup` - A new Swift unqualified lookup library + - Description: The library provides a new Swift unqualified lookup implementation detached from the compiler and accessible to outside clients. The query is stateless and can be directly run on swift-syntax syntax tree, with any syntax node functioning as an entry point. It produces an enum-based data structure as a result that partitions collected names based on the lexical scope of introduction. + - Pull Request: https://github.com/swiftlang/swift-syntax/pull/2952 + - Notes: The library follows the behavior of the compiler implementation with some minor differences, such as a different way of handling dollar identifiers `$x` and generic parameters inside extensions. Furthermore, in the future, once the compiler adopts `SwiftLexicalLookup` and it becomes the canonical implementation, results produced by the query will be guaranteed to be correct. + ## API Behavior Changes ## Deprecations diff --git a/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift b/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift index 45aa35e166f..fa053a00699 100644 --- a/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift +++ b/Sources/SwiftLexicalLookup/IdentifiableSyntax.swift @@ -13,50 +13,50 @@ import SwiftSyntax /// Syntax node that can be refered to with an identifier. -@_spi(Experimental) public protocol IdentifiableSyntax: SyntaxProtocol { +protocol IdentifiableSyntax: SyntaxProtocol { var identifier: TokenSyntax { get } } -@_spi(Experimental) extension IdentifierPatternSyntax: IdentifiableSyntax {} +extension IdentifierPatternSyntax: IdentifiableSyntax {} -@_spi(Experimental) extension ClosureParameterSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension ClosureParameterSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { secondName ?? firstName } } -@_spi(Experimental) extension FunctionParameterSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension FunctionParameterSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { secondName ?? firstName } } -@_spi(Experimental) extension ClosureShorthandParameterSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension ClosureShorthandParameterSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { name } } -@_spi(Experimental) extension ClosureCaptureSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension ClosureCaptureSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { name } } -@_spi(Experimental) extension AccessorParametersSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension AccessorParametersSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { name } } -@_spi(Experimental) extension GenericParameterSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension GenericParameterSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { name } } -@_spi(Experimental) extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax { - @_spi(Experimental) public var identifier: TokenSyntax { +extension PrimaryAssociatedTypeSyntax: IdentifiableSyntax { + var identifier: TokenSyntax { name } } diff --git a/Sources/SwiftLexicalLookup/LookupConfig.swift b/Sources/SwiftLexicalLookup/LookupConfig.swift index 7b9ebaa376d..8d5be3d09c7 100644 --- a/Sources/SwiftLexicalLookup/LookupConfig.swift +++ b/Sources/SwiftLexicalLookup/LookupConfig.swift @@ -12,7 +12,7 @@ import SwiftIfConfig -@_spi(Experimental) public struct LookupConfig { +public struct LookupConfig { /// Specifies whether lookup should finish in the closest sequential scope. /// /// ### Example @@ -32,14 +32,14 @@ import SwiftIfConfig /// function parameter and the `a` declaration from `class X` member block. /// If `finishInSequentialScope` would be set to `false`, the only name /// returned by lookup would be the `a` declaration from inside function body. - @_spi(Experimental) public var finishInSequentialScope: Bool - @_spi(Experimental) public var configuredRegions: ConfiguredRegions? + public var finishInSequentialScope: Bool + public var configuredRegions: ConfiguredRegions? /// Creates a new lookup configuration. /// /// - `finishInSequentialScope` - specifies whether lookup should finish /// in the closest sequential scope. `false` by default. - @_spi(Experimental) public init( + public init( finishInSequentialScope: Bool = false, configuredRegions: ConfiguredRegions? = nil ) { diff --git a/Sources/SwiftLexicalLookup/LookupName.swift b/Sources/SwiftLexicalLookup/LookupName.swift index a5b10a0996a..b750cc4c5aa 100644 --- a/Sources/SwiftLexicalLookup/LookupName.swift +++ b/Sources/SwiftLexicalLookup/LookupName.swift @@ -13,15 +13,18 @@ import SwiftSyntax /// An entity that is implicitly declared based on the syntactic structure of the program. -@_spi(Experimental) public enum ImplicitDecl { +public enum ImplicitDecl { /// `self` keyword representing object instance. - /// Could be associated with type declaration, extension, - /// or closure captures. Introduced at function edge. - case `self`(DeclSyntaxProtocol) + /// Introduced at member boundary. + /// Associated syntax node could be: `FunctionDeclSyntax`, + /// `AccessorDeclSyntax`, `SubscriptDeclSyntax`, + /// `DeinitializerDeclSyntax`, or `InitializerDeclSyntax`. + case `self`(DeclSyntax) /// `Self` keyword representing object type. - /// Could be associated with type declaration or extension. - case `Self`(DeclSyntaxProtocol) - /// `error` value caught by a `catch` + /// Associated syntax node could be: `ExtensionDeclSyntax`, + /// or `ProtocolDeclSyntax`. + case `Self`(DeclSyntax) + /// `error` available by default inside `catch` /// block that does not specify a catch pattern. case error(CatchClauseSyntax) /// `newValue` available by default inside `set` and `willSet`. @@ -30,7 +33,7 @@ import SwiftSyntax case oldValue(AccessorDeclSyntax) /// Syntax associated with this name. - @_spi(Experimental) public var syntax: SyntaxProtocol { + public var syntax: SyntaxProtocol { switch self { case .self(let syntax): return syntax @@ -46,7 +49,7 @@ import SwiftSyntax } /// The name of the implicit declaration. - private var name: StaticString { + public var name: StaticString { switch self { case .self: return "self" @@ -85,12 +88,12 @@ import SwiftSyntax /// ``` /// `self` and `Self` identifers override implicit `self` and `Self` introduced by /// the `Foo` class declaration. - var identifier: Identifier { + public var identifier: Identifier { Identifier(canonicalName: name) } /// Position of this implicit name. - @_spi(Experimental) public var position: AbsolutePosition { + public var position: AbsolutePosition { switch self { case .self(let declSyntax): switch Syntax(declSyntax).as(SyntaxEnum.self) { @@ -128,17 +131,15 @@ import SwiftSyntax } } -@_spi(Experimental) public enum LookupName { +public enum LookupName { /// Identifier associated with the name. /// Could be an identifier of a variable, function or closure parameter and more. - case identifier(IdentifiableSyntax, accessibleAfter: AbsolutePosition?) + case identifier(Syntax, accessibleAfter: AbsolutePosition?) /// Declaration associated with the name. /// Could be class, struct, actor, protocol, function and more. - case declaration(NamedDeclSyntax) + case declaration(Syntax) /// Name introduced implicitly by certain syntax nodes. case implicit(ImplicitDecl) - /// Dollar identifier introduced by a closure without parameters. - case dollarIdentifier(ClosureExprSyntax, strRepresentation: String) /// Represents equivalent names grouped together. /// - Important: The array should be non-empty. /// @@ -154,7 +155,7 @@ import SwiftSyntax case equivalentNames([LookupName]) /// Syntax associated with this name. - @_spi(Experimental) public var syntax: SyntaxProtocol { + public var syntax: SyntaxProtocol { switch self { case .identifier(let syntax, _): return syntax @@ -162,24 +163,20 @@ import SwiftSyntax return syntax case .implicit(let implicitName): return implicitName.syntax - case .dollarIdentifier(let closureExpr, _): - return closureExpr case .equivalentNames(let names): return names.first!.syntax } } /// Identifier used for name comparison. - @_spi(Experimental) public var identifier: Identifier? { + public var identifier: Identifier { switch self { case .identifier(let syntax, _): - return Identifier(syntax.identifier) + return Identifier((syntax.asProtocol(SyntaxProtocol.self) as! IdentifiableSyntax).identifier)! case .declaration(let syntax): - return Identifier(syntax.name) + return Identifier((syntax.asProtocol(SyntaxProtocol.self) as! NamedDeclSyntax).name)! case .implicit(let kind): return kind.identifier - case .dollarIdentifier(_, strRepresentation: _): - return nil case .equivalentNames(let names): return names.first!.identifier } @@ -192,16 +189,15 @@ import SwiftSyntax /// Such cases are function parameters (as they can /// contain two identifiers) and function declarations (where name /// is precided by access modifiers and `func` keyword). - @_spi(Experimental) public var position: AbsolutePosition { + public var position: AbsolutePosition { switch self { case .identifier(let syntax, _): - return syntax.identifier.positionAfterSkippingLeadingTrivia + return (syntax.asProtocol(SyntaxProtocol.self) as! IdentifiableSyntax).identifier + .positionAfterSkippingLeadingTrivia case .declaration(let syntax): - return syntax.name.positionAfterSkippingLeadingTrivia + return (syntax.asProtocol(SyntaxProtocol.self) as! NamedDeclSyntax).name.positionAfterSkippingLeadingTrivia case .implicit(let implicitName): return implicitName.position - case .dollarIdentifier(let closureExpr, _): - return closureExpr.positionAfterSkippingLeadingTrivia case .equivalentNames(let names): return names.first!.position } @@ -226,13 +222,7 @@ import SwiftSyntax func refersTo(_ otherIdentifier: Identifier?) -> Bool { guard let otherIdentifier else { return true } - - switch self { - case .dollarIdentifier(_, let strRepresentation): - return strRepresentation == otherIdentifier.name - default: - return identifier == otherIdentifier - } + return identifier == otherIdentifier } /// Extracts names introduced by the given `syntax` structure. @@ -305,7 +295,7 @@ import SwiftSyntax return [] } - return [.identifier(identifiable, accessibleAfter: accessibleAfter)] + return [.identifier(Syntax(identifiable), accessibleAfter: accessibleAfter)] } /// Extracts name introduced by `NamedDeclSyntax` node. @@ -313,14 +303,14 @@ import SwiftSyntax namedDecl: NamedDeclSyntax, accessibleAfter: AbsolutePosition? = nil ) -> [LookupName] { - [.declaration(namedDecl)] + [.declaration(Syntax(namedDecl))] } /// Debug description of this lookup name. - @_spi(Experimental) public var debugDescription: String { + public var debugDescription: String { let sourceLocationConverter = SourceLocationConverter(fileName: "", tree: syntax.root) let location = sourceLocationConverter.location(for: position) - let strName = (identifier?.name ?? "NO-NAME") + " at: \(location.line):\(location.column)" + let strName = identifier.name + " at: \(location.line):\(location.column)" switch self { case .identifier: @@ -336,8 +326,6 @@ import SwiftSyntax return "declaration: \(strName)" case .implicit: return "implicit: \(strName)" - case .dollarIdentifier(_, strRepresentation: let str): - return "dollarIdentifier: \(str)" case .equivalentNames(let names): return "Composite name: [ \(names.map(\.debugDescription).joined(separator: ", ")) ]" } diff --git a/Sources/SwiftLexicalLookup/LookupResult.swift b/Sources/SwiftLexicalLookup/LookupResult.swift index 2bfe1fb3e7e..b86378a7e71 100644 --- a/Sources/SwiftLexicalLookup/LookupResult.swift +++ b/Sources/SwiftLexicalLookup/LookupResult.swift @@ -13,13 +13,11 @@ import SwiftSyntax /// Represents result from a specific scope. -@_spi(Experimental) public enum LookupResult { +public enum LookupResult { /// Scope and the names that matched lookup. - case fromScope(ScopeSyntax, withNames: [LookupName]) - /// File scope and names that matched lookup. - case fromFileScope(SourceFileSyntax, withNames: [LookupName]) + case fromScope(Syntax, withNames: [LookupName]) /// Indicates where to perform member lookup. - case lookInMembers(LookInMembersScopeSyntax) + case lookForMembers(in: Syntax) /// Indicates to lookup generic parameters of extended type. /// /// ### Example @@ -30,10 +28,10 @@ import SwiftSyntax /// } /// } /// ``` - /// For a lookup started at the marked position, `lookInGenericParametersOfExtendedType` + /// For a lookup started at the marked position, `lookForGenericParameters` /// will be included as one of the results prompting the client /// to lookup the generic parameters of of the extended `Foo` type. - case lookInGenericParametersOfExtendedType(ExtensionDeclSyntax) + case lookForGenericParameters(of: ExtensionDeclSyntax) /// Indicates this closure expression could introduce dollar identifiers. /// /// ### Example @@ -45,65 +43,51 @@ import SwiftSyntax /// } /// ``` /// When looking up for any identifier at the indicated position, - /// the result will include `mightIntroduceDollarIdentifiers` - /// result kind. If it's performed for a dollar identifier, `LookupName.dollarIdentifier` - /// with the appropriate identifier will be used in the - /// result associated with the closure expression inside `a`. - case mightIntroduceDollarIdentifiers(ClosureExprSyntax) + /// the result will include `lookForImplicitClosureParameters`. + case lookForImplicitClosureParameters(ClosureExprSyntax) /// Associated scope. - @_spi(Experimental) public var scope: ScopeSyntax { + public var scope: SyntaxProtocol { switch self { case .fromScope(let scopeSyntax, _): return scopeSyntax - case .fromFileScope(let fileScopeSyntax, _): - return fileScopeSyntax - case .lookInMembers(let lookInMemb): + case .lookForMembers(let lookInMemb): return lookInMemb - case .lookInGenericParametersOfExtendedType(let extensionDecl): + case .lookForGenericParameters(let extensionDecl): return extensionDecl - case .mightIntroduceDollarIdentifiers(let closureExpr): + case .lookForImplicitClosureParameters(let closureExpr): return closureExpr } } /// Names that matched lookup. - @_spi(Experimental) public var names: [LookupName] { + public var names: [LookupName] { switch self { - case .fromScope(_, let names), .fromFileScope(_, let names): + case .fromScope(_, let names): return names - case .lookInMembers(_), - .lookInGenericParametersOfExtendedType(_), - .mightIntroduceDollarIdentifiers(_): + case .lookForMembers(_), + .lookForGenericParameters(_), + .lookForImplicitClosureParameters(_): return [] } } - /// 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) - } - } - /// Returns result specific for the particular `scope` kind with provided `names` /// as an array with one element. If names are empty, returns an empty array. static func getResultArray(for scope: ScopeSyntax, withNames names: [LookupName]) -> [LookupResult] { guard !names.isEmpty else { return [] } - return [getResult(for: scope, withNames: names)] + return [.fromScope(Syntax(scope), withNames: names)] } /// Debug description of this lookup name. - @_spi(Experimental) public var debugDescription: String { + public var debugDescription: String { var description = - resultKindDebugName + ": " + scope.scopeDebugDescription + resultKindDebugName + ": " + + ((Syntax(scope).asProtocol(SyntaxProtocol.self) as? ScopeSyntax)?.scopeDebugDescription ?? "NOT-A-SCOPE") switch self { - case .lookInMembers: + case .lookForMembers: break default: if !names.isEmpty { @@ -127,21 +111,19 @@ import SwiftSyntax switch self { case .fromScope: return "fromScope" - case .fromFileScope: - return "fromFileScope" - case .lookInMembers: - return "lookInMembers" - case .lookInGenericParametersOfExtendedType(_): - return "lookInGenericParametersOfExtendedType" - case .mightIntroduceDollarIdentifiers(_): - return "mightIntroduceDollarIdentifiers" + case .lookForMembers: + return "lookForMembers" + case .lookForGenericParameters(_): + return "lookForGenericParameters" + case .lookForImplicitClosureParameters(_): + return "lookForImplicitClosureParameters" } } } -@_spi(Experimental) extension [LookupResult] { +extension [LookupResult] { /// Debug description this array of lookup results. - @_spi(Experimental) public var debugDescription: String { + public var debugDescription: String { return self.map(\.debugDescription).joined(separator: "\n") } } diff --git a/Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift index b7453f0f851..1b2d22c2b8e 100644 --- a/Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift @@ -22,7 +22,7 @@ extension FunctionScopeSyntax { @_spi(Experimental) public var defaultIntroducedNames: [LookupName] { signature.parameterClause.parameters.flatMap { parameter in LookupName.getNames(from: parameter) - } + (isMember ? [.implicit(.self(self))] : []) + } + (isMember ? [.implicit(.self(DeclSyntax(self)))] : []) } /// Lookup results from this function scope. diff --git a/Sources/SwiftLexicalLookup/Scopes/LookInMembersScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/LookInMembersScopeSyntax.swift index c8767d2099b..06b9353c9d3 100644 --- a/Sources/SwiftLexicalLookup/Scopes/LookInMembersScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/Scopes/LookInMembersScopeSyntax.swift @@ -12,7 +12,7 @@ import SwiftSyntax -@_spi(Experimental) public protocol LookInMembersScopeSyntax: ScopeSyntax { +public protocol LookInMembersScopeSyntax: SyntaxProtocol { /// Position used for member lookup. var lookupMembersPosition: AbsolutePosition { get } } diff --git a/Sources/SwiftLexicalLookup/Scopes/NominalTypeDeclSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/NominalTypeDeclSyntax.swift index 6b5b3d261b4..32a1d2cca52 100644 --- a/Sources/SwiftLexicalLookup/Scopes/NominalTypeDeclSyntax.swift +++ b/Sources/SwiftLexicalLookup/Scopes/NominalTypeDeclSyntax.swift @@ -43,7 +43,7 @@ extension NominalTypeDeclSyntax { } else if name.range.contains(lookUpPosition) || genericWhereClause?.range.contains(lookUpPosition) ?? false { return lookupInParent(identifier, at: lookUpPosition, with: config) } else { - return [.lookInMembers(self)] + lookupInParent(identifier, at: lookUpPosition, with: config) + return [.lookForMembers(in: Syntax(self))] + lookupInParent(identifier, at: lookUpPosition, with: config) } } } diff --git a/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift b/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift index 348b57b5def..94db0fa4605 100644 --- a/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift +++ b/Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift @@ -189,16 +189,9 @@ import SwiftSyntax checkIdentifier(identifier, refersTo: name, at: lookUpPosition) } - if let identifier, identifier.isDollarIdentifier { - signatureResults = LookupResult.getResultArray( - for: self, - withNames: filteredCaptureNames + [LookupName.dollarIdentifier(self, strRepresentation: identifier.name)] - ) - } else { - signatureResults = - LookupResult.getResultArray(for: self, withNames: filteredCaptureNames) - + [.mightIntroduceDollarIdentifiers(self)] - } + signatureResults = + LookupResult.getResultArray(for: self, withNames: filteredCaptureNames) + + [.lookForImplicitClosureParameters(self)] } else { signatureResults = LookupResult.getResultArray( for: self, @@ -390,7 +383,7 @@ import SwiftSyntax "EnumDeclScope" } } -@_spi(Experimental) extension ExtensionDeclSyntax: LookInMembersScopeSyntax { +@_spi(Experimental) extension ExtensionDeclSyntax: ScopeSyntax, LookInMembersScopeSyntax { @_spi(Experimental) public var lookupMembersPosition: AbsolutePosition { if let memberType = extendedType.as(MemberTypeSyntax.self) { return memberType.name.positionAfterSkippingLeadingTrivia @@ -415,27 +408,27 @@ import SwiftSyntax with config: LookupConfig ) -> [LookupResult] { if memberBlock.range.contains(lookUpPosition) { - let implicitSelf: [LookupName] = [.implicit(.Self(self))] + let implicitSelf: [LookupName] = [.implicit(.Self(DeclSyntax(self)))] .filter { name in checkIdentifier(identifier, refersTo: name, at: lookUpPosition) } return LookupResult.getResultArray(for: self, withNames: implicitSelf) - + [.lookInGenericParametersOfExtendedType(self)] + + [.lookForGenericParameters(of: self)] + defaultLookupImplementation(identifier, at: lookUpPosition, with: config, propagateToParent: false) - + [.lookInMembers(self)] + + [.lookForMembers(in: Syntax(self))] + lookupInParent(identifier, at: lookUpPosition, with: config) } else if !extendedType.range.contains(lookUpPosition), let genericWhereClause { if genericWhereClause.range.contains(lookUpPosition) { - return [.lookInGenericParametersOfExtendedType(self)] + [.lookInMembers(self)] + return [.lookForGenericParameters(of: self)] + [.lookForMembers(in: Syntax(self))] + defaultLookupImplementation(identifier, at: lookUpPosition, with: config) } - return [.lookInGenericParametersOfExtendedType(self)] + return [.lookForGenericParameters(of: self)] + defaultLookupImplementation(identifier, at: lookUpPosition, with: config) } - return [.lookInGenericParametersOfExtendedType(self)] + return [.lookForGenericParameters(of: self)] + lookupInParent(identifier, at: lookUpPosition, with: config) } } @@ -477,7 +470,7 @@ import SwiftSyntax return defaultLookupImplementation(identifier, at: lookUpPosition, with: config) } - let implicitSelf: [LookupName] = [.implicit(.self(self))] + let implicitSelf: [LookupName] = [.implicit(.self(DeclSyntax(self)))] .filter { name in checkIdentifier(identifier, refersTo: name, at: lookUpPosition) && !attributes.range.contains(lookUpPosition) } @@ -492,7 +485,7 @@ import SwiftSyntax identifier, at: lookUpPosition, with: config, - resultsToInterleave: implicitSelf.isEmpty ? [] : [.fromScope(self, withNames: implicitSelf)] + resultsToInterleave: implicitSelf.isEmpty ? [] : [.fromScope(Syntax(self), withNames: implicitSelf)] ) } } @@ -592,13 +585,11 @@ import SwiftSyntax var partitioned: [Identifier: [LookupName]] = [:] for extractedName in extractedNames { - guard let identifier = extractedName.identifier else { continue } - - if !partitioned.keys.contains(identifier) { - orderedKeys.append(identifier) + if !partitioned.keys.contains(extractedName.identifier) { + orderedKeys.append(extractedName.identifier) } - partitioned[identifier, default: []].append(extractedName) + partitioned[extractedName.identifier, default: []].append(extractedName) } return @@ -675,7 +666,7 @@ import SwiftSyntax @_spi(Experimental) extension ProtocolDeclSyntax: ScopeSyntax, LookInMembersScopeSyntax { /// Protocol declarations don't introduce names by themselves. @_spi(Experimental) public var defaultIntroducedNames: [LookupName] { - [.implicit(.Self(self))] + [.implicit(.Self(DeclSyntax(self)))] } @_spi(Experimental) public var lookupMembersPosition: AbsolutePosition { @@ -724,7 +715,7 @@ import SwiftSyntax let lookInMembers: [LookupResult] if !(inheritanceClause?.range.contains(lookUpPosition) ?? false) { - lookInMembers = [.lookInMembers(self)] + lookInMembers = [.lookForMembers(in: Syntax(self))] } else { lookInMembers = [] } @@ -785,7 +776,7 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe } if let accessorBlock, case .getter = accessorBlock.accessors { - return parameters + [.implicit(.self(self))] + return parameters + [.implicit(.self(DeclSyntax(self)))] } else { return parameters } @@ -948,7 +939,7 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe || shouldIntroduceSelfIfLazy(lookUpPosition: lookUpPosition) { return defaultLookupImplementation( - in: (isMember ? [.implicit(.self(self))] : LookupName.getNames(from: self)), + in: (isMember ? [.implicit(.self(DeclSyntax(self)))] : LookupName.getNames(from: self)), identifier, at: lookUpPosition, with: config @@ -986,7 +977,7 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe @_spi(Experimental) extension DeinitializerDeclSyntax: ScopeSyntax { @_spi(Experimental) public var defaultIntroducedNames: [LookupName] { - [.implicit(.self(self))] + [.implicit(.self(DeclSyntax(self)))] } @_spi(Experimental) public var scopeDebugName: String { diff --git a/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift index ab67dd07f15..bdac3f27ec4 100644 --- a/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift @@ -13,7 +13,7 @@ import SwiftSyntax extension SyntaxProtocol { - /// Returns all names that `for` refers to at this syntax node. + /// Returns all names that `identifier` refers to at this syntax node. /// Optional configuration can be passed as `config` to customize the lookup behavior. /// /// - Returns: An array of `LookupResult` for `identifier` at this syntax node, @@ -28,22 +28,19 @@ extension SyntaxProtocol { /// var a = 42 /// /// func a(a: Int) { + /// let a = a /// a // <--- lookup here /// /// let a = 0 /// } - /// - /// func a() { - /// // ... - /// } /// } /// ``` /// When calling this function on the declaration reference `a` within its name, - /// the function returns the parameter first, then the identifier of the variable - /// declaration, followed by the first function name, and then the second function name, - /// 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( + /// the function returns the variable declaration `let a = a` first, then the function parameter, + /// and lastly `LookupResult.lookInMembers` in this exact order. + /// The second declaration within the function body is omitted + /// due to the ordering rules within the function body. + public func lookup( _ identifier: Identifier?, with config: LookupConfig = LookupConfig() ) -> [LookupResult] { diff --git a/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift b/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift index cca7f8e5e95..e917b70cb97 100644 --- a/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift +++ b/Sources/SwiftLexicalLookup/Scopes/SequentialScopeSyntax.swift @@ -89,7 +89,7 @@ extension SequentialScopeSyntax { // If there are some names collected, create a new result for this scope. if !currentChunk.isEmpty { - results.append(LookupResult.getResult(for: self, withNames: currentChunk)) + results.append(.fromScope(Syntax(self), withNames: currentChunk)) currentChunk = [] } @@ -107,7 +107,7 @@ extension SequentialScopeSyntax { // If there are some names collected, create a new result for this scope. if !currentChunk.isEmpty { - results.append(LookupResult.getResult(for: self, withNames: currentChunk)) + results.append(.fromScope(Syntax(self), withNames: currentChunk)) currentChunk = [] } diff --git a/Sources/SwiftLexicalLookup/SwiftLexicalLookup.docc/SwiftLexicalLookup.md b/Sources/SwiftLexicalLookup/SwiftLexicalLookup.docc/SwiftLexicalLookup.md index bec53429d71..0f42ecf53c2 100644 --- a/Sources/SwiftLexicalLookup/SwiftLexicalLookup.docc/SwiftLexicalLookup.md +++ b/Sources/SwiftLexicalLookup/SwiftLexicalLookup.docc/SwiftLexicalLookup.md @@ -4,8 +4,6 @@ A library for performing lexical queries on Swift syntax trees. ## Overview -> Important: `SwiftLexicalLookup` is currently in it's experimental phase. Use `@_spi(Experimental) import SwiftLexicalLookup` to work with the library. Clients should not rely on the current implementation. - `SwiftLexicalLookup` provides APIs that traverse and extract information from the Swift syntax tree. Queries on the syntax tree can include responses to questions like: "Where is an error thrown here handled?" or "What is the source and destination of this `fallthrough` keyword?". ```swift diff --git a/Tests/SwiftLexicalLookupTest/ExpectedName.swift b/Tests/SwiftLexicalLookupTest/ExpectedName.swift index 4bc408a6dab..7c540b86a7b 100644 --- a/Tests/SwiftLexicalLookupTest/ExpectedName.swift +++ b/Tests/SwiftLexicalLookupTest/ExpectedName.swift @@ -62,14 +62,12 @@ enum NameExpectation: ExpectedName { case identifier(String) case declaration(String) case implicit(ImplicitNameExpectation) - case dollarIdentifier(String, String) case equivalentNames([NameExpectation]) var marker: [String] { switch self { case .identifier(let marker), - .declaration(let marker), - .dollarIdentifier(let marker, _): + .declaration(let marker): return [marker] case .implicit(let implicitName): return [implicitName.marker] @@ -88,11 +86,6 @@ enum NameExpectation: ExpectedName { case (.declaration, .declaration): break case (.implicit(let implicitName), .implicit(let implicitNameExpectation)): implicitNameExpectation.assertExpectation(marker: marker, for: implicitName) - case (.dollarIdentifier(_, let actualStr), .dollarIdentifier(_, let expectedStr)): - XCTAssert( - actualStr == expectedStr, - "For marker \(marker), actual identifier \(actualStr) doesn't match expected \(expectedStr)" - ) case (.equivalentNames(let actualNames), .equivalentNames(let expectedNames)): XCTAssert( actualNames.count == expectedNames.count, diff --git a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift index 711b6950baf..9594ec5e350 100644 --- a/Tests/SwiftLexicalLookupTest/NameLookupTests.swift +++ b/Tests/SwiftLexicalLookupTest/NameLookupTests.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import Foundation -@_spi(Experimental) import SwiftLexicalLookup +import SwiftLexicalLookup import SwiftSyntax import XCTest @@ -148,17 +148,17 @@ final class testNameLookup: XCTestCase { """, references: [ "2️⃣": [ - .mightIntroduceDollarIdentifiers, + .lookForImplicitClosureParameters, .fromScope(CodeBlockSyntax.self, expectedNames: ["0️⃣"]), ], "3️⃣": [ - .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.dollarIdentifier("5️⃣", "$0")]) + .lookForImplicitClosureParameters ], "4️⃣": [ - .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.dollarIdentifier("5️⃣", "$123")]) + .lookForImplicitClosureParameters ], "6️⃣": [ - .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.dollarIdentifier("5️⃣", "$1")]) + .lookForImplicitClosureParameters ], ] ) @@ -180,20 +180,20 @@ final class testNameLookup: XCTestCase { references: [ "5️⃣": [ .fromScope(ClosureExprSyntax.self, expectedNames: [NameExpectation.identifier("2️⃣")]), - .mightIntroduceDollarIdentifiers, + .lookForImplicitClosureParameters, .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("9️⃣"))]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "6️⃣": [ .fromScope(ClosureExprSyntax.self, expectedNames: ["3️⃣"]), - .mightIntroduceDollarIdentifiers, + .lookForImplicitClosureParameters, .fromScope(CodeBlockSyntax.self, expectedNames: ["1️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "8️⃣": [ .fromScope(ClosureExprSyntax.self, expectedNames: ["4️⃣"]), - .mightIntroduceDollarIdentifiers, - .lookInMembers(ClassDeclSyntax.self), + .lookForImplicitClosureParameters, + .lookForMembers(ClassDeclSyntax.self), ], ], expectedResultTypes: .all( @@ -343,22 +343,22 @@ final class testNameLookup: XCTestCase { """, references: [ "2️⃣": [ - .lookInMembers(ClassDeclSyntax.self) + .lookForMembers(ClassDeclSyntax.self) ], "0️⃣": [ - .lookInMembers(ClassDeclSyntax.self) + .lookForMembers(ClassDeclSyntax.self) ], "4️⃣": [ - .lookInMembers(ClassDeclSyntax.self) + .lookForMembers(ClassDeclSyntax.self) ], "6️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: ["3️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "8️⃣": [ .fromScope(IfExprSyntax.self, expectedNames: ["5️⃣"]), .fromScope(CodeBlockSyntax.self, expectedNames: ["3️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], ], expectedResultTypes: .all( @@ -418,7 +418,7 @@ final class testNameLookup: XCTestCase { FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("3️⃣"))] ), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "0️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: ["8️⃣", "9️⃣"]), @@ -428,7 +428,7 @@ final class testNameLookup: XCTestCase { FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("3️⃣"))] ), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], ], expectedResultTypes: .all( @@ -553,26 +553,26 @@ final class testNameLookup: XCTestCase { """, references: [ "3️⃣": [ - .lookInMembers(StructDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), .fromScope(ExtensionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.Self("7️⃣"))]), - .lookInGenericParametersOfExtendedType, - .lookInMembers(ExtensionDeclSyntax.self), + .lookForGenericParameters, + .lookForMembers(ExtensionDeclSyntax.self), ], "4️⃣": [ .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("2️⃣"))]), - .lookInMembers(StructDeclSyntax.self), - .lookInGenericParametersOfExtendedType, - .lookInMembers(ExtensionDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), + .lookForGenericParameters, + .lookForMembers(ExtensionDeclSyntax.self), ], "5️⃣": [ .fromScope(ExtensionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.Self("7️⃣"))]), - .lookInGenericParametersOfExtendedType, - .lookInMembers(ExtensionDeclSyntax.self), + .lookForGenericParameters, + .lookForMembers(ExtensionDeclSyntax.self), ], "6️⃣": [ .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("1️⃣"))]), - .lookInGenericParametersOfExtendedType, - .lookInMembers(ExtensionDeclSyntax.self), + .lookForGenericParameters, + .lookForMembers(ExtensionDeclSyntax.self), ], ] ) @@ -643,21 +643,21 @@ final class testNameLookup: XCTestCase { "3️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("2️⃣")]), .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("1️⃣"))]), - .lookInMembers(StructDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), ], "4️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("2️⃣")]), .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("1️⃣"))]), - .lookInMembers(StructDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), ], "6️⃣": [ .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("5️⃣"))]), - .lookInMembers(StructDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), ], "8️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("7️⃣")]), .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("5️⃣"))]), - .lookInMembers(StructDeclSyntax.self), + .lookForMembers(StructDeclSyntax.self), ], ] ) @@ -679,12 +679,12 @@ final class testNameLookup: XCTestCase { references: [ "3️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("2️⃣")]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "5️⃣": [ .fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("4️⃣")]), .fromScope(FunctionDeclSyntax.self, expectedNames: [NameExpectation.implicit(.self("1️⃣"))]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], ] ) @@ -863,21 +863,21 @@ final class testNameLookup: XCTestCase { references: [ "3️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "4️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "6️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["5️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "8️⃣": [ - .lookInMembers(ClassDeclSyntax.self), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], ], expectedResultTypes: .all(GenericParameterSyntax.self) @@ -923,11 +923,11 @@ final class testNameLookup: XCTestCase { references: [ "1️⃣": [ .fromScope(MemberBlockSyntax.self, expectedNames: ["3️⃣"]), - .lookInMembers(ProtocolDeclSyntax.self), + .lookForMembers(ProtocolDeclSyntax.self), ], "2️⃣": [ .fromScope(MemberBlockSyntax.self, expectedNames: ["4️⃣"]), - .lookInMembers(ProtocolDeclSyntax.self), + .lookForMembers(ProtocolDeclSyntax.self), ], ], expectedResultTypes: .all( @@ -954,23 +954,23 @@ final class testNameLookup: XCTestCase { "6️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["3️⃣"]), .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "8️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "9️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["4️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "0️⃣": [ .fromScope(FunctionDeclSyntax.self, expectedNames: ["5️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "🔟": [ .fromScope(FunctionDeclSyntax.self, expectedNames: ["7️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], ], expectedResultTypes: .all( @@ -997,26 +997,26 @@ final class testNameLookup: XCTestCase { references: [ "4️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["1️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "6️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "7️⃣": [ .fromScope(GenericParameterClauseSyntax.self, expectedNames: ["2️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "8️⃣": [ .fromScope(SubscriptDeclSyntax.self, expectedNames: ["3️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "9️⃣": [ .fromScope(SubscriptDeclSyntax.self, expectedNames: ["5️⃣"]), - .lookInMembers(ClassDeclSyntax.self), + .lookForMembers(ClassDeclSyntax.self), ], "🔟": [ - .lookInMembers(ClassDeclSyntax.self) + .lookForMembers(ClassDeclSyntax.self) ], ], expectedResultTypes: .all( diff --git a/Tests/SwiftLexicalLookupTest/ResultExpectation.swift b/Tests/SwiftLexicalLookupTest/ResultExpectation.swift index 68e38e194a2..5b32ad56020 100644 --- a/Tests/SwiftLexicalLookupTest/ResultExpectation.swift +++ b/Tests/SwiftLexicalLookupTest/ResultExpectation.swift @@ -17,20 +17,17 @@ import XCTest /// Used to define lookup result assertion. enum ResultExpectation { case fromScope(ScopeSyntax.Type, expectedNames: [ExpectedName]) - case fromFileScope(expectedNames: [ExpectedName]) - case lookInMembers(LookInMembersScopeSyntax.Type) - case lookInGenericParametersOfExtendedType - case mightIntroduceDollarIdentifiers + case lookForMembers(LookInMembersScopeSyntax.Type) + case lookForGenericParameters + case lookForImplicitClosureParameters var expectedNames: [ExpectedName] { switch self { case .fromScope(_, let expectedNames): return expectedNames - case .fromFileScope(expectedNames: let expectedNames): - return expectedNames - case .lookInMembers, - .lookInGenericParametersOfExtendedType, - .mightIntroduceDollarIdentifiers: + case .lookForMembers, + .lookForGenericParameters, + .lookForImplicitClosureParameters: return [] } } @@ -39,14 +36,12 @@ enum ResultExpectation { switch self { case .fromScope: return "fromScope" - case .fromFileScope: - return "fromFileScope" - case .lookInMembers: - return "lookInMembers" - case .lookInGenericParametersOfExtendedType: - return "lookInGenericParametersOfExtendedType" - case .mightIntroduceDollarIdentifiers: - return "mightIntroduceDollarIdentifiers" + case .lookForMembers: + return "lookForMembers" + case .lookForGenericParameters: + return "lookForGenericParameters" + case .lookForImplicitClosureParameters: + return "lookForImplicitClosureParameters" } } @@ -68,16 +63,14 @@ enum ResultExpectation { ) NameExpectation.assertNames(marker: marker, acutalNames: actualNames, expectedNames: expectedNames) - case (.fromFileScope(_, let actualNames), .fromFileScope(let expectedNames)): - NameExpectation.assertNames(marker: marker, acutalNames: actualNames, expectedNames: expectedNames) - case (.lookInMembers(let scope), .lookInMembers(let expectedType)): + case (.lookForMembers(let scope), .lookForMembers(let expectedType)): XCTAssert( scope.syntaxNodeType == expectedType, "For marker \(marker), scope result type of \(scope.syntaxNodeType) doesn't match expected \(expectedType)" ) - case (.lookInGenericParametersOfExtendedType, .lookInGenericParametersOfExtendedType): + case (.lookForGenericParameters, .lookForGenericParameters): break - case (.mightIntroduceDollarIdentifiers, .mightIntroduceDollarIdentifiers): + case (.lookForImplicitClosureParameters, .lookForImplicitClosureParameters): break default: XCTFail(