-
Notifications
You must be signed in to change notification settings - Fork 440
[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
Merged
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
e15bfaa
Format.
MAJKFL a68f00a
Merge branch 'main'
MAJKFL 99b5294
Add guard support to file scope.
MAJKFL c31046a
Add implicit names and proper guard handling.
MAJKFL 8b5fbd2
Separate lookup state from config.
MAJKFL 93cb3ea
Create a new enum representing all possible implicit name cases. Simp…
MAJKFL 9945bf0
Add suggested changes from the previous PR.
MAJKFL 6759b05
Make `IntroducingToSequentialParentScopeSyntax` and `SequentialScope`…
MAJKFL 2d109e5
Merge branch 'main'
MAJKFL 940b06e
Add proper guard scope and implicit name lookup.
MAJKFL c5988ed
Improve documentation.
MAJKFL 0ef6a21
Add identifier based name lookup.
MAJKFL 9a28d02
Fix handling inline variable declaration lists.
MAJKFL d547606
Merge branch 'guard-scope'
MAJKFL 32fec70
Use IdentifierKind enum to represend different identifiers internally.
MAJKFL e96a324
Merge remote-tracking branch 'upstream/main' into guard-and-implicit-…
MAJKFL 0e5009a
Fix merge artifacts.
MAJKFL 166fdb7
Merge branch 'identifier-based-lookup' into guard-and-implicit-name-l…
MAJKFL a255fc9
Fix merge artifacts.
MAJKFL fbf48bb
Fix merge artifacts
MAJKFL dda31f9
Merge changes from #2755 and add Identifier based lookup.
MAJKFL 12a5380
Revert "Merge changes from #2755 and add Identifier based lookup."
MAJKFL b2e5748
Merge branch 'guard-and-implicit-name-lookup'
MAJKFL 5705b96
Add internal `_lookup` function that passes state.
MAJKFL 539b1d7
Add suggested changes.
MAJKFL 5461b07
Add catch clause implicit `error`. Fix declaration name hoisting in s…
MAJKFL 059860a
Break early from sequential for loop when over lookup origin position.
MAJKFL 252d3be
Simplify lookup from `guard ... else` handling.
MAJKFL be2e588
Format.
MAJKFL 01f2887
Add suggestions
MAJKFL cf273f8
Format.
MAJKFL c83b3c6
Improve documentation. Improve sequential scope test case.
MAJKFL fcaecc7
Remove optional chaining from identifier test.
MAJKFL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
Sources/SwiftLexicalLookup/IntroducingToSequentialParentScopeSyntax.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 all names introduced to parent. | ||
var namesIntroducedToSequentialParent: [LookupName] { get } | ||
|
||
/// Returns results matching lookup that should be | ||
/// interleaved with results of the sequential parent. | ||
func lookupFromSequentialParent( | ||
for identifier: Identifier?, | ||
at origin: AbsolutePosition, | ||
with config: LookupConfig | ||
) -> [LookupResult] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,108 @@ | |
|
||
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. | ||
/// | ||
/// Note that `self` and `Self` are treated as identifiers for name lookup purposes | ||
/// and that a variable named `self` can shadow the `self` keyword. For example. | ||
/// ```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. | ||
var identifier: Identifier { | ||
switch self { | ||
case .self: | ||
return Identifier("self") | ||
case .Self: | ||
return Identifier("Self") | ||
case .error: | ||
return Identifier("error") | ||
case .newValue: | ||
return Identifier("newValue") | ||
case .oldValue: | ||
return Identifier("oldValue") | ||
} | ||
} | ||
} | ||
|
||
@_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) | ||
/// 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 { | ||
|
@@ -27,40 +122,52 @@ import SwiftSyntax | |
return syntax | ||
case .declaration(let syntax): | ||
return syntax | ||
case .implicit(let implicitName): | ||
return implicitName.syntax | ||
case .self(let syntax, _), .Self(let syntax, _): | ||
return syntax | ||
} | ||
} | ||
|
||
/// Introduced name. | ||
/// Identifier used for name comparison. | ||
@_spi(Experimental) public var identifier: Identifier? { | ||
switch self { | ||
case .identifier(let syntax, _): | ||
return Identifier(syntax.identifier) | ||
case .declaration(let syntax): | ||
return Identifier(syntax.name) | ||
case .implicit(let kind): | ||
return kind.identifier | ||
case .self: | ||
return Identifier("self") | ||
case .Self: | ||
return Identifier("Self") | ||
} | ||
} | ||
|
||
/// 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? { | ||
switch self { | ||
case .identifier(_, let absolutePosition): | ||
case .identifier(_, let absolutePosition), | ||
.self(_, let absolutePosition), | ||
.Self(_, let absolutePosition): | ||
return absolutePosition | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
/// Checks if this name was introduced before the syntax used for lookup. | ||
func isAccessible(at lookedUpSyntax: SyntaxProtocol) -> Bool { | ||
func isAccessible(at origin: AbsolutePosition) -> Bool { | ||
ahoppen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
guard let accessibleAfter else { return true } | ||
return accessibleAfter <= lookedUpSyntax.position | ||
return accessibleAfter <= origin | ||
} | ||
|
||
/// 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 +212,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,12 +224,21 @@ import SwiftSyntax | |
} | ||
|
||
/// Extracts name introduced by `IdentifiableSyntax` node. | ||
private static func handle(identifiable: IdentifiableSyntax, accessibleAfter: AbsolutePosition? = nil) -> [LookupName] | ||
{ | ||
if identifiable.identifier.tokenKind != .wildcard { | ||
return [.identifier(identifiable, accessibleAfter: accessibleAfter)] | ||
} else { | ||
return [] | ||
private static func handle( | ||
identifiable: IdentifiableSyntax, | ||
accessibleAfter: AbsolutePosition? = nil | ||
) -> [LookupName] { | ||
switch identifiable.identifier.tokenKind { | ||
case .keyword(.self): | ||
return [.self(identifiable, accessibleAfter: accessibleAfter)] | ||
case .keyword(.Self): | ||
return [.Self(identifiable, accessibleAfter: accessibleAfter)] | ||
default: | ||
if identifiable.identifier.tokenKind != .wildcard { | ||
return [.identifier(identifiable, accessibleAfter: accessibleAfter)] | ||
} else { | ||
return [] | ||
} | ||
ahoppen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.