-
Notifications
You must be signed in to change notification settings - Fork 440
[SwiftLexicalLookup] Add ASTScope related fixes and lookInMembers result kind. #2852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
d83af9c
7521e4e
fae4d0e
3de7208
433f14b
0f9b2bc
baf6a2f
fc1ab88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ import SwiftSyntax | |
@_spi(Experimental) public enum ImplicitDecl { | ||
/// `self` keyword representing object instance. | ||
/// Could be associated with type declaration, extension, | ||
/// or closure captures. | ||
/// or closure captures. Introduced at function edge. | ||
case `self`(DeclSyntaxProtocol) | ||
/// `Self` keyword representing object type. | ||
/// Could be associated with type declaration or extension. | ||
|
@@ -135,6 +135,50 @@ import SwiftSyntax | |
} | ||
} | ||
|
||
/// Position of this name. | ||
/// | ||
/// For some syntax nodes, their position doesn't reflect | ||
/// the position at which a particular name was introduced at. | ||
/// 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 { | ||
switch self { | ||
case .identifier(let syntax, _): | ||
return syntax.identifier.positionAfterSkippingLeadingTrivia | ||
case .declaration(let syntax): | ||
return syntax.name.position | ||
case .implicit(let implicitName): | ||
switch implicitName { | ||
case .self(let declSyntax): | ||
switch Syntax(declSyntax).as(SyntaxEnum.self) { | ||
case .functionDecl(let functionDecl): | ||
return functionDecl.name.position | ||
case .initializerDecl(let initializerDecl): | ||
return initializerDecl.initKeyword.positionAfterSkippingLeadingTrivia | ||
case .subscriptDecl(let subscriptDecl): | ||
return subscriptDecl.accessorBlock?.position ?? subscriptDecl.endPosition | ||
case .variableDecl(let variableDecl): | ||
return variableDecl.bindings.first?.accessorBlock?.positionAfterSkippingLeadingTrivia | ||
?? variableDecl.endPosition | ||
default: | ||
return declSyntax.positionAfterSkippingLeadingTrivia | ||
ahoppen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
case .Self(let declSyntax): | ||
switch Syntax(declSyntax).as(SyntaxEnum.self) { | ||
case .protocolDecl(let protocolDecl): | ||
return protocolDecl.name.positionAfterSkippingLeadingTrivia | ||
Comment on lines
+170
to
+171
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we only need this for protocols and not other type declarations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this PR, protocol declarations are the only places we are introducing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, you’re not handling class Foo {
func bar() -> Self { fatalError() }
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interestingly, it seems to me like
Nevertheless, there certainly are more places protocol Foo {}
extension Foo {
func bar() -> Self { fatalError() }
}
|
||
default: | ||
return declSyntax.positionAfterSkippingLeadingTrivia | ||
} | ||
case .error(let catchClause): | ||
return catchClause.body.position | ||
default: | ||
return implicitName.syntax.positionAfterSkippingLeadingTrivia | ||
} | ||
} | ||
} | ||
|
||
/// Point, after which the name is available in scope. | ||
/// If set to `nil`, the name is available at any point in scope. | ||
var accessibleAfter: AbsolutePosition? { | ||
|
@@ -166,7 +210,7 @@ import SwiftSyntax | |
) -> [LookupName] { | ||
switch Syntax(syntax).as(SyntaxEnum.self) { | ||
case .variableDecl(let variableDecl): | ||
return variableDecl.bindings.flatMap { binding in | ||
return variableDecl.bindings.reversed().flatMap { binding in | ||
getNames( | ||
from: binding.pattern, | ||
accessibleAfter: accessibleAfter != nil ? binding.endPositionBeforeTrailingTrivia : nil | ||
|
@@ -194,6 +238,8 @@ import SwiftSyntax | |
return functionCallExpr.arguments.flatMap { argument in | ||
getNames(from: argument.expression, accessibleAfter: accessibleAfter) | ||
} | ||
case .optionalChainingExpr(let optionalChainingExpr): | ||
return getNames(from: optionalChainingExpr.expression, accessibleAfter: accessibleAfter) | ||
default: | ||
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax { | ||
return handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter) | ||
|
@@ -210,12 +256,7 @@ import SwiftSyntax | |
identifiable: IdentifiableSyntax, | ||
accessibleAfter: AbsolutePosition? = nil | ||
) -> [LookupName] { | ||
switch identifiable.identifier.tokenKind { | ||
case .wildcard: | ||
return [] | ||
default: | ||
return [.identifier(identifiable, accessibleAfter: accessibleAfter)] | ||
} | ||
[.identifier(identifiable, accessibleAfter: accessibleAfter)] | ||
} | ||
|
||
/// Extracts name introduced by `NamedDeclSyntax` node. | ||
|
@@ -225,4 +266,27 @@ import SwiftSyntax | |
) -> [LookupName] { | ||
[.declaration(namedDecl)] | ||
} | ||
|
||
/// Debug description of this lookup name. | ||
@_spi(Experimental) public var debugDescription: String { | ||
let sourceLocationConverter = SourceLocationConverter(fileName: "", tree: syntax.root) | ||
let location = sourceLocationConverter.location(for: position) | ||
let strName = (identifier != nil ? identifier!.name : "NO-NAME") + " at: \(location.line):\(location.column)" | ||
MAJKFL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
switch self { | ||
case .identifier: | ||
let str = "identifier: \(strName)" | ||
|
||
if let accessibleAfter { | ||
let location = sourceLocationConverter.location(for: accessibleAfter) | ||
return str + " after: \(location.line):\(location.column)" | ||
} else { | ||
return str | ||
} | ||
case .declaration: | ||
return "declaration: \(strName)" | ||
case .implicit: | ||
return "implicit: \(strName)" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
|
||
protocol FunctionScopeSyntax: DeclSyntaxProtocol, WithGenericParametersScopeSyntax { | ||
var signature: FunctionSignatureSyntax { get } | ||
} | ||
|
||
extension FunctionScopeSyntax { | ||
/// Function parameters introduced by this function's signature. | ||
@_spi(Experimental) public var introducedNames: [LookupName] { | ||
signature.parameterClause.parameters.flatMap { parameter in | ||
LookupName.getNames(from: parameter) | ||
} + (parentScope?.is(MemberBlockSyntax.self) ?? false ? [.implicit(.self(self))] : []) | ||
} | ||
|
||
/// Lookup results from this function scope. | ||
/// Routes to generic parameter clause scope if exists. | ||
@_spi(Experimental) public func lookup( | ||
_ identifier: Identifier?, | ||
at lookUpPosition: AbsolutePosition, | ||
with config: LookupConfig | ||
) -> [LookupResult] { | ||
var thisScopeResults: [LookupResult] = [] | ||
|
||
if !signature.range.contains(lookUpPosition) { | ||
thisScopeResults = defaultLookupImplementation( | ||
identifier, | ||
at: position, | ||
with: config, | ||
propagateToParent: false | ||
) | ||
} | ||
|
||
return thisScopeResults | ||
+ lookupThroughGenericParameterScope( | ||
identifier, | ||
at: lookUpPosition, | ||
with: config | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
|
||
protocol NominalTypeDeclSyntax: LookInMembersScopeSyntax, NamedDeclSyntax, WithGenericParametersScopeSyntax { | ||
var genericParameterClause: GenericParameterClauseSyntax? { get } | ||
var inheritanceClause: InheritanceClauseSyntax? { get } | ||
} | ||
|
||
extension NominalTypeDeclSyntax { | ||
@_spi(Experimental) public var lookupMembersPosition: AbsolutePosition { | ||
name.position | ||
} | ||
|
||
/// Nominal type doesn't introduce any names by itself. | ||
@_spi(Experimental) public var introducedNames: [LookupName] { | ||
[] | ||
} | ||
|
||
/// Function used by generic parameter clause | ||
/// scope on return from it's lookup. | ||
func returningLookupFromGenericParameterScope( | ||
_ identifier: Identifier?, | ||
at lookUpPosition: AbsolutePosition, | ||
with config: LookupConfig | ||
) -> [LookupResult] { | ||
if let inheritanceClause, inheritanceClause.range.contains(lookUpPosition) { | ||
return lookupInParent(identifier, at: lookUpPosition, with: config) | ||
} else if let genericParameterClause, genericParameterClause.range.contains(lookUpPosition) { | ||
return lookupInParent(identifier, at: lookUpPosition, with: config) | ||
} else if name.range.contains(lookUpPosition) { | ||
return lookupInParent(identifier, at: lookUpPosition, with: config) | ||
} else { | ||
return [.lookInMembers(self)] + lookupInParent(identifier, at: lookUpPosition, with: config) | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.