Skip to content

Commit df86e80

Browse files
committed
Add SwiftIfConfig to SwiftLexicalLookup for #if handling. Add support for macro declarations. Minor fixes.
1 parent d145cb2 commit df86e80

File tree

9 files changed

+295
-68
lines changed

9 files changed

+295
-68
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 6 additions & 1 deletion
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 buildConfiguration: BuildConfiguration?
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+
buildConfiguration: BuildConfiguration? = nil
4145
) {
4246
self.finishInSequentialScope = finishInSequentialScope
47+
self.buildConfiguration = buildConfiguration
4348
}
4449
}

Sources/SwiftLexicalLookup/LookupName.swift

Lines changed: 13 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 140 additions & 34 deletions
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(
@@ -512,13 +500,19 @@ import SwiftSyntax
512500
@_spi(Experimental) extension CatchClauseSyntax: ScopeSyntax {
513501
/// Implicit `error` when there are no catch items.
514502
@_spi(Experimental) public var defaultIntroducedNames: [LookupName] {
503+
var containsExpressionSyntax = false
504+
515505
let extractedNames = catchItems.flatMap { item in
516506
guard let pattern = item.pattern else { return [LookupName]() }
517507

508+
if !containsExpressionSyntax && pattern.is(ExpressionPatternSyntax.self) {
509+
containsExpressionSyntax = true
510+
}
511+
518512
return LookupName.getNames(from: pattern)
519513
}
520514

521-
return extractedNames.isEmpty ? [.implicit(.error(self))] : extractedNames
515+
return extractedNames.isEmpty && !containsExpressionSyntax ? [.implicit(.error(self))] : extractedNames
522516
}
523517

524518
@_spi(Experimental) public var scopeDebugName: String {
@@ -594,14 +588,27 @@ import SwiftSyntax
594588
checkIdentifier(identifier, refersTo: name, at: lookUpPosition)
595589
}
596590

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))
591+
if label.range.contains(lookUpPosition) {
592+
return config.finishInSequentialScope ? [] : lookupInParent(identifier, at: lookUpPosition, with: config)
593+
} else if config.finishInSequentialScope {
594+
return sequentialLookup(
595+
in: statements,
596+
identifier,
597+
at: lookUpPosition,
598+
with: config,
599+
propagateToParent: false
600+
)
601+
} else {
602+
return sequentialLookup(
603+
in: statements,
604+
identifier,
605+
at: lookUpPosition,
606+
with: config,
607+
propagateToParent: false
608+
)
609+
+ LookupResult.getResultArray(for: self, withNames: filteredNamesFromLabel)
610+
+ lookupInParent(identifier, at: lookUpPosition, with: config)
611+
}
605612
}
606613
}
607614

@@ -697,6 +704,18 @@ import SwiftSyntax
697704
}
698705
}
699706

707+
@_spi(Experimental) extension MacroDeclSyntax: WithGenericParametersScopeSyntax {
708+
public var defaultIntroducedNames: [LookupName] {
709+
signature.parameterClause.parameters.flatMap { parameter in
710+
LookupName.getNames(from: parameter)
711+
}
712+
}
713+
714+
@_spi(Experimental) public var scopeDebugName: String {
715+
"MacroDeclScope"
716+
}
717+
}
718+
700719
@_spi(Experimental)
701720
extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveResultsLaterScopeSyntax {
702721
/// Parameters introduced by this subscript and possibly `self` keyword.
@@ -758,7 +777,7 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
758777
) -> [LookupResult] {
759778
var thisScopeResults: [LookupResult] = []
760779

761-
if !parameterClause.range.contains(lookUpPosition) && !returnClause.range.contains(lookUpPosition) {
780+
if accessorBlock?.range.contains(lookUpPosition) ?? false {
762781
thisScopeResults = defaultLookupImplementation(
763782
identifier,
764783
at: position,
@@ -866,8 +885,6 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
866885
with config: LookupConfig
867886
) -> [LookupResult] {
868887
if bindings.first?.accessorBlock?.range.contains(lookUpPosition) ?? false {
869-
let isMember = parentScope?.is(MemberBlockSyntax.self) ?? false
870-
871888
return defaultLookupImplementation(
872889
in: (isMember ? [.implicit(.self(self))] : LookupName.getNames(from: self)),
873890
identifier,
@@ -887,10 +904,99 @@ extension SubscriptDeclSyntax: WithGenericParametersScopeSyntax, CanInterleaveRe
887904
with config: LookupConfig,
888905
resultsToInterleave: [LookupResult]
889906
) -> [LookupResult] {
890-
guard parentScope?.is(MemberBlockSyntax.self) ?? false else {
907+
guard isMember else {
891908
return lookup(identifier, at: lookUpPosition, with: config)
892909
}
893910

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

Sources/SwiftLexicalLookup/Scopes/ScopeSyntax.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,27 @@ extension SyntaxProtocol {
122122
introducedName.isAccessible(at: lookUpPosition) && introducedName.refersTo(identifier)
123123
}
124124

125+
var isMember: Bool {
126+
if parentScope?.is(MemberBlockSyntax.self) ?? false {
127+
return true
128+
} else if let parentIfConfig = parentScope?.as(IfConfigDeclSyntax.self) {
129+
return parentIfConfig.isMember
130+
} else {
131+
return false
132+
}
133+
}
134+
125135
/// Debug description of this scope.
126136
@_spi(Experimental) public var scopeDebugDescription: String {
127137
scopeDebugName + " " + debugLineWithColumnDescription
128138
}
139+
140+
/// Hierarchy of scopes starting from this scope.
141+
@_spi(Experimental) public var scopeDebugHierarchyDescription: String {
142+
if let parentScope = parentScope {
143+
return parentScope.scopeDebugHierarchyDescription + " <-- " + scopeDebugDescription
144+
} else {
145+
return scopeDebugDescription
146+
}
147+
}
129148
}

0 commit comments

Comments
 (0)