Skip to content

Commit 5461b07

Browse files
committed
Add catch clause implicit error. Fix declaration name hoisting in sequential scopes. Fix partitioning with non matching results from guard scopes.
1 parent 539b1d7 commit 5461b07

File tree

4 files changed

+132
-9
lines changed

4 files changed

+132
-9
lines changed

Sources/SwiftLexicalLookup/ScopeImplementations.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ import SwiftSyntax
355355

356356
@_spi(Experimental) extension AccessorDeclSyntax: ScopeSyntax {
357357
/// Implicit and/or explicit names introduced
358-
/// withing the accessor..
358+
/// within the accessor.
359359
@_spi(Experimental) public var introducedNames: [LookupName] {
360360
if let parameters {
361361
return LookupName.getNames(from: parameters)
@@ -371,3 +371,10 @@ import SwiftSyntax
371371
}
372372
}
373373
}
374+
375+
@_spi(Experimental) extension CatchClauseSyntax: ScopeSyntax {
376+
/// Implicit `error` when there are no catch items.
377+
public var introducedNames: [LookupName] {
378+
return catchItems.isEmpty ? [.implicit(.error(self))] : []
379+
}
380+
}

Sources/SwiftLexicalLookup/SequentialScopeSyntax.swift

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,22 @@ extension SequentialScopeSyntax {
4848
) -> [LookupResult] {
4949
var result: [LookupResult] = []
5050
var currentChunk: [LookupName] = []
51+
var itemsWithoutNamedDecl: [CodeBlockItemSyntax] = []
5152

5253
for codeBlockItem in codeBlockItems {
54+
if Syntax(codeBlockItem.item).isProtocol(NamedDeclSyntax.self) {
55+
currentChunk += LookupName.getNames(
56+
from: codeBlockItem.item,
57+
accessibleAfter: codeBlockItem.endPosition
58+
).filter { introducedName in
59+
checkName(identifier, refersTo: introducedName, at: origin)
60+
}
61+
} else {
62+
itemsWithoutNamedDecl.append(codeBlockItem)
63+
}
64+
}
65+
66+
for codeBlockItem in itemsWithoutNamedDecl {
5367
if let introducingToParentScope = Syntax(codeBlockItem.item).asProtocol(SyntaxProtocol.self)
5468
as? IntroducingToSequentialParentScopeSyntax
5569
{
@@ -60,19 +74,24 @@ extension SequentialScopeSyntax {
6074
continue
6175
}
6276

77+
// Get results from encountered scope.
78+
let introducedResults = introducingToParentScope.introducesToSequentialParent(
79+
for: identifier,
80+
at: origin,
81+
with: config,
82+
state: state
83+
)
84+
85+
// Skip, if no results were found.
86+
guard !introducedResults.isEmpty else { continue }
87+
6388
// If there are some names collected, create a new result for this scope.
6489
if !currentChunk.isEmpty {
6590
result.append(getResults(currentChunk))
6691
currentChunk = []
6792
}
6893

69-
// Add names introduced by the encountered scope.
70-
result += introducingToParentScope.introducesToSequentialParent(
71-
for: identifier,
72-
at: origin,
73-
with: config,
74-
state: state
75-
)
94+
result += introducedResults
7695
} else {
7796
// Extract new names from encountered node.
7897
currentChunk += LookupName.getNames(

Tests/SwiftLexicalLookupTest/NameLookupTests.swift

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,8 @@ final class testNameLookup: XCTestCase {
596596
references: [
597597
"4️⃣": [
598598
.fromFileScope(expectedNames: ["1️⃣"]),
599-
.fromFileScope(expectedNames: ["3️⃣"]),
600599
.fromScope(GuardStmtSyntax.self, expectedNames: ["2️⃣"]),
600+
.fromFileScope(expectedNames: ["3️⃣"]),
601601
]
602602
],
603603
expectedResultTypes: .all(IdentifierPatternSyntax.self, except: ["3️⃣": ClassDeclSyntax.self])
@@ -733,4 +733,96 @@ final class testNameLookup: XCTestCase {
733733
]
734734
)
735735
}
736+
737+
func testImplicitErrorInCatchClause() {
738+
assertLexicalNameLookup(
739+
source: """
740+
func foo() {
741+
let 1️⃣error = 0
742+
743+
do {
744+
try x.bar()
745+
2️⃣error
746+
} catch SomeError {
747+
3️⃣error
748+
} 4️⃣catch {
749+
5️⃣error
750+
}
751+
}
752+
""",
753+
references: [
754+
"2️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("1️⃣")])],
755+
"3️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("1️⃣")])],
756+
"5️⃣": [
757+
.fromScope(CatchClauseSyntax.self, expectedNames: [NameExpectation.implicit(.error("4️⃣"))]),
758+
.fromScope(CodeBlockSyntax.self, expectedNames: [NameExpectation.identifier("1️⃣")]),
759+
],
760+
]
761+
)
762+
}
763+
764+
func testTypeDeclAvaialabilityInSequentialScope() {
765+
let declExpectation: [ResultExpectation] = [
766+
.fromScope(
767+
CodeBlockSyntax.self,
768+
expectedNames: [
769+
NameExpectation.declaration("2️⃣"),
770+
NameExpectation.declaration("5️⃣"),
771+
NameExpectation.declaration("8️⃣"),
772+
]
773+
)
774+
]
775+
776+
assertLexicalNameLookup(
777+
source: """
778+
func foo() {
779+
1️⃣a
780+
2️⃣class a {}
781+
3️⃣a
782+
guard let x else { return }
783+
4️⃣a
784+
5️⃣actor a {}
785+
6️⃣a
786+
guard let x else { return }
787+
7️⃣a
788+
8️⃣struct a {}
789+
9️⃣a
790+
}
791+
""",
792+
references: [
793+
"1️⃣": declExpectation,
794+
"3️⃣": declExpectation,
795+
"4️⃣": declExpectation,
796+
"6️⃣": declExpectation,
797+
"7️⃣": declExpectation,
798+
"9️⃣": declExpectation,
799+
]
800+
)
801+
}
802+
803+
func testNonMatchingGuardScopeDoesntPartitionResult() {
804+
assertLexicalNameLookup(
805+
source: """
806+
func foo() {
807+
let 1️⃣a = 1
808+
let 2️⃣b = 2
809+
810+
guard let 3️⃣b = a else { return }
811+
812+
let 4️⃣a = 3
813+
let 5️⃣b = 4
814+
815+
print(6️⃣a, 7️⃣b)
816+
}
817+
""",
818+
references: [
819+
"6️⃣": [.fromScope(CodeBlockSyntax.self, expectedNames: ["1️⃣", "4️⃣"])],
820+
"7️⃣": [
821+
.fromScope(CodeBlockSyntax.self, expectedNames: ["5️⃣"]),
822+
.fromScope(GuardStmtSyntax.self, expectedNames: ["3️⃣"]),
823+
.fromScope(CodeBlockSyntax.self, expectedNames: ["2️⃣"]),
824+
],
825+
]
826+
)
827+
}
736828
}

Tests/SwiftLexicalLookupTest/ResultExpectation.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ enum ResultExpectation {
3838
}
3939

4040
static func assertResult(marker: String, result: [LookupResult], expectedValues: [ResultExpectation]) {
41+
XCTAssert(
42+
result.count == expectedValues.count,
43+
"For marker \(marker), actual result count \(result.count) doesn't match expected \(expectedValues.count)"
44+
)
45+
4146
for (actual, expected) in zip(result, expectedValues) {
4247
switch (actual, expected) {
4348
case (

0 commit comments

Comments
 (0)