Skip to content

Commit 1cd3534

Browse files
authored
Merge pull request #2883 from MAJKFL/add-swiftIfConfig-to-swiftLexicalLookup
[SwiftLexicalLookup] Add support for `#if` and macro declarations. More `ASTScope` related fixes.
2 parents 39a57d4 + 334fd59 commit 1cd3534

File tree

9 files changed

+315
-69
lines changed

9 files changed

+315
-69
lines changed

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ let package = Package(
184184

185185
.target(
186186
name: "SwiftLexicalLookup",
187-
dependencies: ["SwiftSyntax"]
187+
dependencies: ["SwiftSyntax", "SwiftIfConfig"]
188188
),
189189

190190
.testTarget(

Sources/SwiftLexicalLookup/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ add_swift_syntax_library(SwiftLexicalLookup
2626
)
2727

2828
target_link_swift_syntax_libraries(SwiftLexicalLookup PUBLIC
29-
SwiftSyntax)
29+
SwiftSyntax
30+
SwiftIfConfig)
3031

Sources/SwiftLexicalLookup/LookupConfig.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import SwiftIfConfig
14+
1315
@_spi(Experimental) public struct LookupConfig {
1416
/// Specifies whether lookup should finish in the closest sequential scope.
1517
///
@@ -31,14 +33,17 @@
3133
/// If `finishInSequentialScope` would be set to `false`, the only name
3234
/// returned by lookup would be the `a` declaration from inside function body.
3335
@_spi(Experimental) public var finishInSequentialScope: Bool
36+
@_spi(Experimental) public var configuredRegions: ConfiguredRegions?
3437

3538
/// Creates a new lookup configuration.
3639
///
3740
/// - `finishInSequentialScope` - specifies whether lookup should finish
3841
/// in the closest sequential scope. `false` by default.
3942
@_spi(Experimental) public init(
40-
finishInSequentialScope: Bool = false
43+
finishInSequentialScope: Bool = false,
44+
configuredRegions: ConfiguredRegions? = nil
4145
) {
4246
self.finishInSequentialScope = finishInSequentialScope
47+
self.configuredRegions = configuredRegions
4348
}
4449
}

Sources/SwiftLexicalLookup/LookupName.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -104,20 +104,26 @@ import SwiftSyntax
104104
case .variableDecl(let variableDecl):
105105
return variableDecl.bindings.first?.accessorBlock?.positionAfterSkippingLeadingTrivia
106106
?? variableDecl.endPosition
107+
case .accessorDecl(let accessorDecl):
108+
return accessorDecl.accessorSpecifier.positionAfterSkippingLeadingTrivia
109+
case .deinitializerDecl(let deinitializerDecl):
110+
return deinitializerDecl.deinitKeyword.positionAfterSkippingLeadingTrivia
107111
default:
108112
return declSyntax.positionAfterSkippingLeadingTrivia
109113
}
110114
case .Self(let declSyntax):
111115
switch Syntax(declSyntax).as(SyntaxEnum.self) {
112116
case .protocolDecl(let protocolDecl):
113117
return protocolDecl.name.positionAfterSkippingLeadingTrivia
118+
case .extensionDecl(let extensionDecl):
119+
return extensionDecl.extensionKeyword.positionAfterSkippingLeadingTrivia
114120
default:
115121
return declSyntax.positionAfterSkippingLeadingTrivia
116122
}
123+
case .newValue(let accessorDecl), .oldValue(let accessorDecl):
124+
return accessorDecl.accessorSpecifier.positionAfterSkippingLeadingTrivia
117125
case .error(let catchClause):
118126
return catchClause.catchItems.positionAfterSkippingLeadingTrivia
119-
default:
120-
return syntax.positionAfterSkippingLeadingTrivia
121127
}
122128
}
123129
}
@@ -276,7 +282,11 @@ import SwiftSyntax
276282
identifiable: IdentifiableSyntax,
277283
accessibleAfter: AbsolutePosition? = nil
278284
) -> [LookupName] {
279-
[.identifier(identifiable, accessibleAfter: accessibleAfter)]
285+
if case .wildcard = identifiable.identifier.tokenKind {
286+
return []
287+
}
288+
289+
return [.identifier(identifiable, accessibleAfter: accessibleAfter)]
280290
}
281291

282292
/// Extracts name introduced by `NamedDeclSyntax` node.

Sources/SwiftLexicalLookup/Scopes/FunctionScopeSyntax.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension FunctionScopeSyntax {
2222
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
2323
signature.parameterClause.parameters.flatMap { parameter in
2424
LookupName.getNames(from: parameter)
25-
} + (parentScope?.is(MemberBlockSyntax.self) ?? false ? [.implicit(.self(self))] : [])
25+
} + (isMember ? [.implicit(.self(self))] : [])
2626
}
2727

2828
/// Lookup results from this function scope.

Sources/SwiftLexicalLookup/Scopes/ScopeImplementations.swift

+160-35
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import SwiftIfConfig
1314
import SwiftSyntax
1415

1516
@_spi(Experimental) extension SyntaxProtocol {
@@ -391,7 +392,11 @@ import SwiftSyntax
391392
}
392393
@_spi(Experimental) extension ExtensionDeclSyntax: LookInMembersScopeSyntax {
393394
@_spi(Experimental) public var lookupMembersPosition: AbsolutePosition {
394-
extendedType.position
395+
if let memberType = extendedType.as(MemberTypeSyntax.self) {
396+
return memberType.name.positionAfterSkippingLeadingTrivia
397+
}
398+
399+
return extendedType.positionAfterSkippingLeadingTrivia
395400
}
396401

397402
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
@@ -420,8 +425,8 @@ import SwiftSyntax
420425
+ defaultLookupImplementation(identifier, at: lookUpPosition, with: config, propagateToParent: false)
421426
+ [.lookInMembers(self)]
422427
+ lookupInParent(identifier, at: lookUpPosition, with: config)
423-
} else if !extendedType.range.contains(lookUpPosition) && genericWhereClause != nil {
424-
if inRightTypeOrSameTypeRequirement(lookUpPosition) {
428+
} else if !extendedType.range.contains(lookUpPosition), let genericWhereClause {
429+
if genericWhereClause.range.contains(lookUpPosition) {
425430
return [.lookInGenericParametersOfExtendedType(self)] + [.lookInMembers(self)]
426431
+ defaultLookupImplementation(identifier, at: lookUpPosition, with: config)
427432
}
@@ -433,23 +438,6 @@ import SwiftSyntax
433438
return [.lookInGenericParametersOfExtendedType(self)]
434439
+ lookupInParent(identifier, at: lookUpPosition, with: config)
435440
}
436-
437-
/// Returns `true` if `checkedPosition` is a right type of a
438-
/// conformance requirement or inside a same type requirement.
439-
private func inRightTypeOrSameTypeRequirement(
440-
_ checkedPosition: AbsolutePosition
441-
) -> Bool {
442-
genericWhereClause?.requirements.contains { elem in
443-
switch Syntax(elem.requirement).as(SyntaxEnum.self) {
444-
case .conformanceRequirement(let conformanceRequirement):
445-
return conformanceRequirement.rightType.range.contains(checkedPosition)
446-
case .sameTypeRequirement(let sameTypeRequirement):
447-
return sameTypeRequirement.range.contains(checkedPosition)
448-
default:
449-
return false
450-
}
451-
} ?? false
452-
}
453441
}
454442

455443
@_spi(Experimental) extension AccessorDeclSyntax: ScopeSyntax {
@@ -491,7 +479,7 @@ import SwiftSyntax
491479

492480
let implicitSelf: [LookupName] = [.implicit(.self(self))]
493481
.filter { name in
494-
checkIdentifier(identifier, refersTo: name, at: lookUpPosition)
482+
checkIdentifier(identifier, refersTo: name, at: lookUpPosition) && !attributes.range.contains(lookUpPosition)
495483
}
496484

497485
return defaultLookupImplementation(
@@ -510,15 +498,40 @@ import SwiftSyntax
510498
}
511499

512500
@_spi(Experimental) extension CatchClauseSyntax: ScopeSyntax {
513-
/// Implicit `error` when there are no catch items.
501+
/// Name introduced by the catch clause.
502+
///
503+
/// `defaultIntroducedNames` contains implicit `error` name if
504+
/// no names are declared in catch items and they don't contain any expression patterns.
505+
/// Otherwise, `defaultIntroducedNames` contains names introduced by the clause.
506+
///
507+
/// ### Example
508+
/// ```swift
509+
/// do {
510+
/// // ...
511+
/// } catch SomeError, .x(let a) {
512+
/// // <-- lookup here, result: [a]
513+
/// } catch .x(let a) {
514+
/// // <-- lookup here, result: [a]
515+
/// } catch SomeError {
516+
/// // <-- lookup here, result: [empty]
517+
/// } catch {
518+
/// // <-- lookup here, result: implicit(error)
519+
/// }
520+
/// ```
514521
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
522+
var containsExpressionSyntax = false
523+
515524
let extractedNames = catchItems.flatMap { item in
516525
guard let pattern = item.pattern else { return [LookupName]() }
517526

527+
if !containsExpressionSyntax && pattern.is(ExpressionPatternSyntax.self) {
528+
containsExpressionSyntax = true
529+
}
530+
518531
return LookupName.getNames(from: pattern)
519532
}
520533

521-
return extractedNames.isEmpty ? [.implicit(.error(self))] : extractedNames
534+
return extractedNames.isEmpty && !containsExpressionSyntax ? [.implicit(.error(self))] : extractedNames
522535
}
523536

524537
@_spi(Experimental) public var scopeDebugName: String {
@@ -594,14 +607,27 @@ import SwiftSyntax
594607
checkIdentifier(identifier, refersTo: name, at: lookUpPosition)
595608
}
596609

597-
return sequentialLookup(
598-
in: statements,
599-
identifier,
600-
at: lookUpPosition,
601-
with: config,
602-
propagateToParent: false
603-
) + LookupResult.getResultArray(for: self, withNames: filteredNamesFromLabel)
604-
+ (config.finishInSequentialScope ? [] : lookupInParent(identifier, at: lookUpPosition, with: config))
610+
if label.range.contains(lookUpPosition) {
611+
return config.finishInSequentialScope ? [] : lookupInParent(identifier, at: lookUpPosition, with: config)
612+
} else if config.finishInSequentialScope {
613+
return sequentialLookup(
614+
in: statements,
615+
identifier,
616+
at: lookUpPosition,
617+
with: config,
618+
propagateToParent: false
619+
)
620+
} else {
621+
return sequentialLookup(
622+
in: statements,
623+
identifier,
624+
at: lookUpPosition,
625+
with: config,
626+
propagateToParent: false
627+
)
628+
+ LookupResult.getResultArray(for: self, withNames: filteredNamesFromLabel)
629+
+ lookupInParent(identifier, at: lookUpPosition, with: config)
630+
}
605631
}
606632
}
607633

@@ -697,6 +723,18 @@ import SwiftSyntax
697723
}
698724
}
699725

726+
@_spi(Experimental) extension MacroDeclSyntax: WithGenericParametersScopeSyntax {
727+
public var defaultIntroducedNames: [LookupName] {
728+
signature.parameterClause.parameters.flatMap { parameter in
729+
LookupName.getNames(from: parameter)
730+
}
731+
}
732+
733+
@_spi(Experimental) public var scopeDebugName: String {
734+
"MacroDeclScope"
735+
}
736+
}
737+
700738
@_spi(Experimental)
701739
extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveResultsLaterScopeSyntax {
702740
/// Parameters introduced by this subscript and possibly `self` keyword.
@@ -758,7 +796,7 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
758796
) -> [LookupResult] {
759797
var thisScopeResults: [LookupResult] = []
760798

761-
if !parameterClause.range.contains(lookUpPosition) && !returnClause.range.contains(lookUpPosition) {
799+
if accessorBlock?.range.contains(lookUpPosition) ?? false {
762800
thisScopeResults = defaultLookupImplementation(
763801
identifier,
764802
at: position,
@@ -866,8 +904,6 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
866904
with config: LookupConfig
867905
) -> [LookupResult] {
868906
if bindings.first?.accessorBlock?.range.contains(lookUpPosition) ?? false {
869-
let isMember = parentScope?.is(MemberBlockSyntax.self) ?? false
870-
871907
return defaultLookupImplementation(
872908
in: (isMember ? [.implicit(.self(self))] : LookupName.getNames(from: self)),
873909
identifier,
@@ -887,10 +923,99 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
887923
with config: LookupConfig,
888924
resultsToInterleave: [LookupResult]
889925
) -> [LookupResult] {
890-
guard parentScope?.is(MemberBlockSyntax.self) ?? false else {
926+
guard isMember else {
891927
return lookup(identifier, at: lookUpPosition, with: config)
892928
}
893929

894930
return resultsToInterleave + lookupInParent(identifier, at: lookUpPosition, with: config)
895931
}
896932
}
933+
934+
@_spi(Experimental) extension DeinitializerDeclSyntax: ScopeSyntax {
935+
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
936+
[.implicit(.self(self))]
937+
}
938+
939+
@_spi(Experimental) public var scopeDebugName: String {
940+
"DeinitializerScope"
941+
}
942+
}
943+
944+
@_spi(Experimental) extension IfConfigDeclSyntax: IntroducingToSequentialParentScopeSyntax, SequentialScopeSyntax {
945+
/// Names from all clauses.
946+
var namesIntroducedToSequentialParent: [LookupName] {
947+
clauses.flatMap { clause in
948+
clause.elements.flatMap { element in
949+
LookupName.getNames(from: element, accessibleAfter: element.endPosition)
950+
} ?? []
951+
}
952+
}
953+
954+
/// Performs sequential lookup in the active clause.
955+
/// Active clause is determined by the `BuildConfiguration`
956+
/// inside `config`. If not specified, defaults to the `#else` clause.
957+
func lookupFromSequentialParent(
958+
_ identifier: Identifier?,
959+
at lookUpPosition: AbsolutePosition,
960+
with config: LookupConfig
961+
) -> [LookupResult] {
962+
let clause: IfConfigClauseSyntax?
963+
964+
if let configuredRegions = config.configuredRegions {
965+
clause = configuredRegions.activeClause(for: self)
966+
} else {
967+
clause =
968+
clauses
969+
.first { clause in
970+
clause.poundKeyword.tokenKind == .poundElse
971+
}
972+
}
973+
974+
return sequentialLookup(
975+
in: clause?.elements?.as(CodeBlockItemListSyntax.self) ?? [],
976+
identifier,
977+
at: lookUpPosition,
978+
with: config,
979+
ignoreNamedDecl: true,
980+
propagateToParent: false
981+
)
982+
}
983+
984+
/// Returns all `NamedDeclSyntax` nodes in the active clause specified
985+
/// by `BuildConfiguration` in `config` from bottom-most to top-most.
986+
func getNamedDecls(for config: LookupConfig) -> [NamedDeclSyntax] {
987+
let clause: IfConfigClauseSyntax?
988+
989+
if let configuredRegions = config.configuredRegions {
990+
clause = configuredRegions.activeClause(for: self)
991+
} else {
992+
clause =
993+
clauses
994+
.first { clause in
995+
clause.poundKeyword.tokenKind == .poundElse
996+
}
997+
}
998+
999+
guard let clauseElements = clause?.elements?.as(CodeBlockItemListSyntax.self) else { return [] }
1000+
1001+
var result: [NamedDeclSyntax] = []
1002+
1003+
for elem in clauseElements.reversed() {
1004+
if let namedDecl = elem.item.asProtocol(NamedDeclSyntax.self) {
1005+
result.append(namedDecl)
1006+
} else if let ifConfigDecl = elem.item.as(IfConfigDeclSyntax.self) {
1007+
result += ifConfigDecl.getNamedDecls(for: config)
1008+
}
1009+
}
1010+
1011+
return result
1012+
}
1013+
1014+
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
1015+
[]
1016+
}
1017+
1018+
@_spi(Experimental) public var scopeDebugName: String {
1019+
"IfConfigScope"
1020+
}
1021+
}

0 commit comments

Comments
 (0)