-
Notifications
You must be signed in to change notification settings - Fork 441
[SwiftLexicalLookup][GSoC] Add proper guard scope and implicit name lookup #2748
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 29 commits
e15bfaa
a68f00a
99b5294
c31046a
8b5fbd2
93cb3ea
9945bf0
6759b05
2d109e5
940b06e
c5988ed
0ef6a21
9a28d02
d547606
32fec70
e96a324
0e5009a
166fdb7
a255fc9
fbf48bb
dda31f9
12a5380
b2e5748
5705b96
539b1d7
5461b07
059860a
252d3be
be2e588
01f2887
cf273f8
c83b3c6
fcaecc7
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 |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 IntroducingToSequentialParentScopeSyntax: ScopeSyntax { | ||
/// Returns names matching lookup that should be | ||
/// handled by it's parent sequential scope. | ||
func introducesToSequentialParent( | ||
for identifier: Identifier?, | ||
at origin: SyntaxProtocol, | ||
with config: LookupConfig, | ||
state: LookupState | ||
) -> [LookupResult] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,93 @@ | |
|
||
import SwiftSyntax | ||
|
||
/// An entity that is implicitly declared based on the syntactic structure of the program. | ||
@_spi(Experimental) public enum ImplicitDecl { | ||
/// `self` keyword representing object instance. | ||
/// Could be associated with type declaration, extension, | ||
/// or closure captures. | ||
case `self`(DeclSyntaxProtocol) | ||
/// `Self` keyword representing object type. | ||
/// Could be associated with type declaration or extension. | ||
case `Self`(DeclSyntaxProtocol) | ||
/// `error` value caught by a `catch` | ||
/// block that does not specify a catch pattern. | ||
case error(CatchClauseSyntax) | ||
/// `newValue` available by default inside `set` and `willSet`. | ||
case newValue(AccessorDeclSyntax) | ||
/// `oldValue` available by default inside `didSet`. | ||
case oldValue(AccessorDeclSyntax) | ||
|
||
/// Syntax associated with this name. | ||
@_spi(Experimental) public var syntax: SyntaxProtocol { | ||
switch self { | ||
case .self(let syntax): | ||
return syntax | ||
case .Self(let syntax): | ||
return syntax | ||
case .error(let syntax): | ||
return syntax | ||
case .newValue(let syntax): | ||
return syntax | ||
case .oldValue(let syntax): | ||
return syntax | ||
} | ||
} | ||
|
||
/// The name of the implicit declaration. | ||
private var name: String { | ||
switch self { | ||
case .self: | ||
return "self" | ||
case .Self: | ||
return "Self" | ||
case .error: | ||
return "error" | ||
case .newValue: | ||
return "newValue" | ||
case .oldValue: | ||
return "oldValue" | ||
} | ||
} | ||
|
||
/// Identifier used for name comparison. | ||
/// | ||
/// | ||
MAJKFL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// ```swift | ||
/// class Foo { | ||
/// func test() { | ||
/// let `Self` = "abc" | ||
/// print(Self.self) | ||
/// | ||
/// let `self` = "def" | ||
/// print(self) | ||
/// } | ||
/// } | ||
/// | ||
/// Foo().test() | ||
/// ``` | ||
/// prints: | ||
/// ``` | ||
/// abc | ||
/// def | ||
/// ``` | ||
/// `self` and `Self` identifers override | ||
/// implicit `self` and `Self` introduced by | ||
/// the `Foo` class declaration. | ||
MAJKFL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var identifier: Identifier { | ||
Identifier(name) | ||
} | ||
} | ||
|
||
@_spi(Experimental) 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?) | ||
/// Declaration associated with the name. | ||
/// Could be class, struct, actor, protocol, function and more. | ||
case declaration(NamedDeclSyntax) | ||
/// Name introduced implicitly by certain syntax nodes. | ||
case implicit(ImplicitDecl) | ||
|
||
/// Syntax associated with this name. | ||
@_spi(Experimental) public var syntax: SyntaxProtocol { | ||
|
@@ -27,16 +107,20 @@ import SwiftSyntax | |
return syntax | ||
case .declaration(let syntax): | ||
return syntax | ||
case .implicit(let implicitName): | ||
return implicitName.syntax | ||
} | ||
} | ||
|
||
/// Introduced name. | ||
/// Identifier used for name comparison. | ||
@_spi(Experimental) public var identifier: Identifier? { | ||
switch self { | ||
case .identifier(let syntax, _): | ||
return Identifier(syntax.identifier) | ||
return Identifier(syntax.identifier) ?? Identifier(syntax.identifier.text) | ||
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. Do we need 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. I added new |
||
case .declaration(let syntax): | ||
return Identifier(syntax.name) | ||
case .implicit(let kind): | ||
return kind.identifier | ||
} | ||
} | ||
|
||
|
@@ -58,9 +142,9 @@ import SwiftSyntax | |
} | ||
|
||
/// Checks if this name refers to the looked up phrase. | ||
func refersTo(_ lookedUpName: String) -> Bool { | ||
guard let name = identifier?.name else { return false } | ||
return name == lookedUpName | ||
func refersTo(_ lookedUpIdentifier: Identifier) -> Bool { | ||
guard let identifier else { return false } | ||
return identifier == lookedUpIdentifier | ||
Comment on lines
+168
to
+170
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. Does this method provide any value anymore? I think you can remove the |
||
} | ||
|
||
/// Extracts names introduced by the given `syntax` structure. | ||
|
@@ -105,10 +189,6 @@ import SwiftSyntax | |
return functionCallExpr.arguments.flatMap { argument in | ||
getNames(from: argument.expression, accessibleAfter: accessibleAfter) | ||
} | ||
case .guardStmt(let guardStmt): | ||
return guardStmt.conditions.flatMap { cond in | ||
getNames(from: cond.condition, accessibleAfter: cond.endPosition) | ||
} | ||
default: | ||
if let namedDecl = Syntax(syntax).asProtocol(SyntaxProtocol.self) as? NamedDeclSyntax { | ||
return handle(namedDecl: namedDecl, accessibleAfter: accessibleAfter) | ||
|
@@ -121,8 +201,10 @@ import SwiftSyntax | |
} | ||
|
||
/// Extracts name introduced by `IdentifiableSyntax` node. | ||
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] | ||
{ | ||
private static func handle( | ||
identifiable: IdentifiableSyntax, | ||
accessibleAfter: AbsolutePosition? = nil | ||
) -> [LookupName] { | ||
if identifiable.identifier.tokenKind != .wildcard { | ||
return [.identifier(identifiable, accessibleAfter: accessibleAfter)] | ||
} else { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 Foundation | ||
|
||
/// Represents internal state for lookup. | ||
/// It shouldn't be used by clients. | ||
@_spi(Experimental) public struct LookupState { | ||
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. Please document this, even if it's just to say that this is internal state for lookup that isn't used by clients. 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. I added an appropriate doc comment. On a side note, thanks to @ahoppen suggestion, I've simplified how we handle lookup from inside Do you think we should remove 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. If we don't need it now, let's remove it! The fewer parameters and less state we need to track, the better. If we find we need to bring it back... well, we know how to do it. 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. I agree that we should remove 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. Ok. I removed |
||
init() {} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it a little surprising that
ScopeSyntax
has an API that returns all names introduce in its scope (introducedNames
) while this API contains a filter. I think it would be more consistent if this also returned all the names introduced to the parent scope and then be namednamesIntroducedToParentScope
. As far as I can tell it wouldn’t need to take any parameters at all then and could be a computed property as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that was poor naming on my part as
introducesToSequentialParent
worked more likelookup
producing[LookupResult]
to be interleaved with results of the sequential parent, rather thanintroducedNames
that just return allLookupName
s available at that scope. I renamedintroducesToSequentialParent
to less misleadinglookupFromSequentialParent
and addednamesIntroducedToSequentialParent
that closely resemblesintroducedNames
of a normal scope.