Skip to content

Commit c00cc6a

Browse files
authored
Merge pull request #2849 from MAJKFL/unqualified-lookup-rules-documentation
[SwiftLexicalLookup] Add unqualified lookup rules documentation.
2 parents 084005f + dcfcc47 commit c00cc6a

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Unqualified Lookup Rules
2+
3+
## Overview
4+
Unqualified lookup can be initiated from any source position, optionally targeting a specific identifier for name comparison. Unqualified lookup traverses the syntax tree from bottom-up, collecting names that match the lookup criteria. A name matches the lookup if the specified identifier (if present) refers to the introduced name and the position from which the unqualified lookup was triggered is preceded by the position where the name was introduced, provided the name enforces position based availability by itself (such exceptions are for example type declarations).
5+
6+
## Default Scope Behavior
7+
Names are introduced and their availabilities are set by special syntax nodes referred to as syntax scopes or simply scopes. By default, each scope for lookup produces a result object that associates the scope of origin with the matched names. Lookup is then invoked on the parent scope, i.e., the closest syntax scope ancestor of the original scope in the syntax tree.
8+
9+
## Name Kinds
10+
There are three distinct kinds of names identified by unqualified lookup:
11+
- **Identifier**: Represents `IdentifiableSyntax` syntax nodes introducing names such as `IdentifierPatternSyntax`, `ClosureParameterSyntax`, or `FunctionParameterSyntax`.
12+
- **Declaration**: Represents `NamedDeclSyntax` syntax nodes introducing names such as `ClassDeclSyntax`, `FunctionDeclSyntax`, or `ProtocolDeclSyntax`.
13+
- **Implicit**: Represents names introduced by some scopes implicitly, without an associated token representation. Such names include `self`, `newValue`, or `error`.
14+
15+
## Specialized Scopes
16+
Some scopes share common behavior and can be further generalized as follows:
17+
- **Type Scope** (`TypeScopeSyntax`): Implicitly introduces `self` and `Self` names.
18+
- **Sequential Scope** (`SequentialScopeSyntax`): Interleaves its own results with results produced by *Introducing to Sequential Parent Scopes* when encountered during name extraction. Results are ordered from closest to furthest from the unqualified lookup position.
19+
- **Introducing to Sequential Parent Scope** (`IntroducingToSequentialParentScopeSyntax`): Produces a result that is later interleaved with the results produced by its sequential parent.
20+
21+
> Example:
22+
> Sequential Scope behavior (code block):
23+
> ```swift
24+
> func foo(x: Int?) {
25+
> // Guard scope
26+
> guard let a = x else { return }
27+
>
28+
> // In code block scope
29+
> let a: Int? = a
30+
>
31+
> // Guard scope
32+
> guard let b = x, let a else { return }
33+
>
34+
> // In code block scope
35+
> let a = b
36+
>
37+
> a // <-- lookup here
38+
> b // <-- lookup here
39+
> }
40+
> ```
41+
> When looking up the reference `a`, we get 4 results in total, going from the bottom to the top of the source code: the `a` declaration associated with the function's code block body scope, the `a` variable associated with the bottom-most `guard`, another `a` declaration associated with the code block, and finally the `a` introduced in the top-most `guard`.
42+
> When looking up `b`, on the other hand, we get just one result: the `b` declaration inside the bottom-most `guard`.
43+
44+
- **With Generic Parameters Scope** (`WithGenericParametersScopeSyntax`): Instead of invoking lookup on its parent scope, it first tries to pass it to the *Generic Parameter Scope* if available.
45+
- **Generic Parameter Scope** (`GenericParameterScopeSyntax`): Instead of invoking lookup on its parent scope, it first tries to invoke it on the *With Generic Parameters Scope's* parent.
46+
47+
> Example:
48+
> Generic Parameter Scope Behavior:
49+
> ```swift
50+
> class Foo<A, B: A> { // <-- lookup here
51+
> func bar<A, C>() {
52+
> X // <-- lookup here
53+
> }
54+
> }
55+
> ```
56+
> When starting the lookup from the location marked `X` without identifier matching, we get two results. The first is associated with the generic parameter scope of the `bar` function, containing the names `A` and `C` in that exact order. The second is associated with the class `Foo`, containing the names `A` and `B` in that exact order.
57+
> When performing lookup at the `A` reference in the `Foo` class generic parameter clause, the generic parameter `A` introduced inside the clause is returned as in the result.
58+
59+
## Particular Scope Implementations
60+
- **File Scope** (`SourceFileSyntax`): Conditional sequential scope syntax. Depending on user-defined configuration, the file scope can behave in two ways: either as a member block scope or as a sequential lookup for all declarations up to the first non-declaration statement.
61+
- **Code Block Scope** (`CodeBlockSyntax`): Sequential scope. Introduces names from its statements with position-based availability.
62+
- **For Statement Scope** (`ForStmtSyntax`): Introduces names from inside its pattern.
63+
- **Closure Expression Scope** (`ClosureExprSyntax`): Introduces names from closure captures and parameters.
64+
- **While Statement Scope** (`WhileStmtSyntax`): Introduces names from its conditions.
65+
- **If Expression Scope** (`IfExprSyntax`): Introduces names from its conditions and specifies custom parent behavior. For nested `else if` statements, the parent scope is set to the enclosing scope for the entire `if` expression. Additionally, lookup from inside the `else` clause is routed directly to the parent scope.
66+
- **Member Block Scope** (`MemberBlockSyntax`): Introduces names from its members.
67+
- **Guard Statement Scope** (`GuardStmtSyntax`): Introduces names from its conditions to its sequential parent. For lookup from inside its `else` clause, no results are introduced.
68+
- **Actor Declaration Scope** (`ActorDeclSyntax`): Type scope with generic parameters.
69+
- **Class Declaration Scope** (`ClassDeclSyntax`): Type scope with generic parameters.
70+
- **Struct Declaration Scope** (`StructDeclSyntax`): Type scope with generic parameters.
71+
- **Enum Declaration Scope** (`EnumDeclSyntax`): Type scope with generic parameters.
72+
- **Extension Declaration Scope** (`ExtensionDeclSyntax`): Type scope.
73+
- **Accessor Declaration Syntax** (`AccessorDeclSyntax`): Introduces names from its parameters. If not present, and if the accessor is of `set` or `willSet` kind, it introduces the implicit `newValue` name. If of `didSet` kind, it introduces the implicit `oldValue` name.
74+
- **Catch Clause Scope** (`CatchClauseSyntax`): Introduces the implicit `error` name if catch items are not specified.
75+
- **Switch Case Scope** (`SwitchCaseSyntax`): Introduces names from its case items.
76+
- **Protocol Declaration Scope** (`ProtocolDeclSyntax`): Does not introduce any names. For lookup initiated in its primary associated type clause, it performs lookup on its member scope exclusively for associated type declarations.
77+
- **Generic Parameter Clause Scope** (`GenericParameterClauseSyntax`): Generic parameter scope syntax. Introduces names from its generic parameters with position-based availability.
78+
- **Function Declaration Scope** (`FunctionDeclSyntax`): With generic parameters scope. Introduces names from its parameters.
79+
- **Subscript Declaration Syntax** (`SubscriptDeclSyntax`): With generic parameters scope. Introduces names from its parameters.
80+
- **Type Alias Declaration Scope** (`TypeAliasDeclSyntax`): With generic parameters scope. Does not introduce any names.

0 commit comments

Comments
 (0)