Skip to content

Commit e27f195

Browse files
authored
Merge pull request #75937 from DougGregor/astgen-swiftifconfig
2 parents 08721cf + afd365b commit e27f195

File tree

14 files changed

+266
-59
lines changed

14 files changed

+266
-59
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ SWIFT_NAME("BridgedASTContext.langOptsGetCompilerVersion(self:_:)")
246246
SwiftInt BridgedASTContext_langOptsGetCompilerVersion(BridgedASTContext cContext,
247247
SwiftInt* _Nullable * _Nonnull cComponents);
248248

249+
/* Deallocate an array of Swift int values that was allocated in C++. */
250+
void deallocateIntBuffer(SwiftInt * _Nullable cComponents);
251+
249252
enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedCanImportVersion : size_t {
250253
CanImportUnversioned,
251254
CanImportVersion,

include/swift/Bridging/ASTGen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ int swift_ASTGen_roundTripCheck(void *_Nonnull sourceFile);
4848
/// Emit parser diagnostics for given source file.. Returns non-zero if any
4949
/// diagnostics were emitted.
5050
int swift_ASTGen_emitParserDiagnostics(
51+
BridgedASTContext astContext,
5152
void *_Nonnull diagEngine, void *_Nonnull sourceFile, int emitOnlyErrors,
5253
int downgradePlaceholderErrorsToWarnings);
5354

lib/AST/ASTBridging.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ namespace {
195195
}
196196
}
197197

198+
void deallocateIntBuffer(SwiftInt * _Nullable cComponents) {
199+
free(cComponents);
200+
}
201+
198202
SwiftInt BridgedASTContext_langOptsGetLanguageVersion(BridgedASTContext cContext,
199203
SwiftInt** cComponents) {
200204
auto theVersion = cContext.unbridged().LangOpts.EffectiveLanguageVersion;

lib/ASTGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ endif()
2020

2121
add_pure_swift_host_library(swiftASTGen STATIC
2222
Sources/ASTGen/ASTGen.swift
23+
Sources/ASTGen/ASTGen+CompilerBuildConfiguration.swift
2324
Sources/ASTGen/Bridge.swift
2425
Sources/ASTGen/CompilerBuildConfiguration.swift
2526
Sources/ASTGen/DeclAttrs.swift
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===--- ASTGen+CompilerBuildConfiguration.swift --------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024-2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftDiagnostics
14+
import SwiftIfConfig
15+
import SwiftSyntax
16+
17+
/// Enumeration that separates an #if config decl from the underlying element.
18+
enum IfConfigOrUnderlying<Element> {
19+
case ifConfigDecl(IfConfigDeclSyntax)
20+
case underlying(Element)
21+
}
22+
23+
extension ASTGenVisitor {
24+
/// Determine the active clause for the given #if, emitting any diagnostics
25+
/// produced due to the evaluation.
26+
func activeClause(in node: IfConfigDeclSyntax) -> IfConfigClauseSyntax? {
27+
// Determine the active clause.
28+
var buildConfiguration = self.buildConfiguration
29+
buildConfiguration.conditionLoc = generateSourceLoc(node)
30+
let (activeClause, diagnostics) = node.activeClause(in: buildConfiguration)
31+
diagnoseAll(diagnostics)
32+
33+
return activeClause
34+
}
35+
36+
/// Visit a collection of elements that may contain #if clauses within it
37+
/// within it, recursing into the active clauses and to ensure that every
38+
/// active element is visited.
39+
/// - Parameters:
40+
/// - elements: A syntax collection that can config `IfConfigDeclSyntax`
41+
/// nodes in it.
42+
/// - flatElementType: The element type within the syntax collection for the
43+
/// non-#if case.
44+
/// - split: Splits an element in elements into the two cases: #if config
45+
/// or a flat element.
46+
/// - body: The body that handles each syntax node of type FlatElement.
47+
func visitIfConfigElements<Elements, FlatElement>(
48+
_ elements: Elements,
49+
of flatElementType: FlatElement.Type,
50+
split: (Elements.Element) -> IfConfigOrUnderlying<FlatElement>,
51+
body: (FlatElement) -> Void
52+
) where Elements: SyntaxCollection, FlatElement: SyntaxProtocol {
53+
for element in elements {
54+
switch split(element) {
55+
case .ifConfigDecl(let ifConfigDecl):
56+
if let activeClause = self.activeClause(in: ifConfigDecl),
57+
let activeElements = activeClause.elements {
58+
guard let activeElements = activeElements._syntaxNode.as(Elements.self) else {
59+
fatalError("Parser produced invalid nesting of #if decls")
60+
}
61+
62+
visitIfConfigElements(
63+
activeElements,
64+
of: FlatElement.self,
65+
split: split,
66+
body: body
67+
)
68+
}
69+
70+
case .underlying(let element):
71+
body(element)
72+
}
73+
}
74+
}
75+
}
76+
77+

lib/ASTGen/Sources/ASTGen/ASTGen.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ struct ASTGenVisitor {
7676

7777
let ctx: BridgedASTContext
7878

79+
let buildConfiguration: CompilerBuildConfiguration
80+
7981
fileprivate let allocator: SwiftSyntax.BumpPtrAllocator = .init(initialSlabSize: 256)
8082

8183
/// Fallback legacy parser used when ASTGen doesn't have the generate(_:)
@@ -94,12 +96,20 @@ struct ASTGenVisitor {
9496
self.declContext = declContext
9597
self.ctx = astContext
9698
self.legacyParse = legacyParser
99+
self.buildConfiguration = CompilerBuildConfiguration(
100+
ctx: ctx,
101+
conditionLoc: BridgedSourceLoc(at: AbsolutePosition(utf8Offset: 0), in: sourceBuffer)
102+
)
97103
}
98104

99105
func generate(sourceFile node: SourceFileSyntax) -> [BridgedDecl] {
100106
var out = [BridgedDecl]()
101107

102-
for element in node.statements {
108+
visitIfConfigElements(
109+
node.statements,
110+
of: CodeBlockItemSyntax.self,
111+
split: Self.splitCodeBlockItemIfConfig
112+
) { element in
103113
let loc = self.generateSourceLoc(element)
104114
let swiftASTNodes = generate(codeBlockItem: element)
105115
switch swiftASTNodes {
@@ -260,6 +270,11 @@ extension ASTGenVisitor {
260270
diagnosticSeverity: diagnostic.diagMessage.severity
261271
)
262272
}
273+
274+
/// Emits the given diagnostics via the C++ diagnostic engine.
275+
func diagnoseAll(_ diagnostics: [Diagnostic]) {
276+
diagnostics.forEach(diagnose)
277+
}
263278
}
264279

265280
// Forwarding overloads that take optional syntax nodes. These are defined on demand to achieve a consistent

lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import SwiftSyntax
2020
/// queries.
2121
struct CompilerBuildConfiguration: BuildConfiguration {
2222
let ctx: BridgedASTContext
23-
let conditionLoc: BridgedSourceLoc
23+
var conditionLoc: BridgedSourceLoc
24+
25+
init(ctx: BridgedASTContext, conditionLoc: BridgedSourceLoc) {
26+
self.ctx = ctx
27+
self.conditionLoc = conditionLoc
28+
}
2429

2530
func isCustomConditionSet(name: String) throws -> Bool {
2631
var name = name
@@ -118,7 +123,7 @@ struct CompilerBuildConfiguration: BuildConfiguration {
118123
var bitWidthsBuf: UnsafeMutablePointer<SwiftInt>? = nil
119124
let count = ctx.langOptsGetTargetAtomicBitWidths(&bitWidthsBuf)
120125
let bitWidths = Array(UnsafeMutableBufferPointer(start: bitWidthsBuf, count: count))
121-
bitWidthsBuf?.deallocate()
126+
deallocateIntBuffer(bitWidthsBuf);
122127
return bitWidths
123128
}
124129

@@ -135,7 +140,7 @@ struct CompilerBuildConfiguration: BuildConfiguration {
135140
let version = VersionTuple(
136141
components: Array(UnsafeMutableBufferPointer(start: componentsBuf, count: count))
137142
)
138-
componentsBuf?.deallocate()
143+
deallocateIntBuffer(componentsBuf);
139144
return version
140145
}
141146

@@ -145,7 +150,7 @@ struct CompilerBuildConfiguration: BuildConfiguration {
145150
let version = VersionTuple(
146151
components: Array(UnsafeMutableBufferPointer(start: componentsBuf, count: count))
147152
)
148-
componentsBuf?.deallocate()
153+
deallocateIntBuffer(componentsBuf);
149154
return version
150155
}
151156
}

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import ASTBridging
1414
import BasicBridging
1515
import SwiftDiagnostics
16+
import SwiftIfConfig
17+
1618
@_spi(ExperimentalLanguageFeatures) @_spi(RawSyntax) import SwiftSyntax
1719

1820
extension ASTGenVisitor {
@@ -52,17 +54,15 @@ extension ASTGenVisitor {
5254
}
5355

5456
// '@' attributes.
55-
for node in node.attributes {
56-
switch node {
57-
case .attribute(let node):
58-
addAttribute(self.generateDeclAttribute(attribute: node))
59-
case .ifConfigDecl:
60-
fatalError("unimplemented")
61-
#if RESILIENT_SWIFT_SYNTAX
62-
@unknown default:
63-
fatalError()
64-
#endif
57+
visitIfConfigElements(node.attributes, of: AttributeSyntax.self) { element in
58+
switch element {
59+
case .ifConfigDecl(let ifConfigDecl):
60+
return .ifConfigDecl(ifConfigDecl)
61+
case .attribute(let attribute):
62+
return .underlying(attribute)
6563
}
64+
} body: { attribute in
65+
addAttribute(self.generateDeclAttribute(attribute: attribute))
6666
}
6767

6868
func genStatic(node: DeclModifierSyntax, spelling: BridgedStaticSpelling) {

lib/ASTGen/Sources/ASTGen/Decls.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ extension ASTGenVisitor {
4141
case .functionDecl(let node):
4242
return self.generate(functionDecl: node).asDecl
4343
case .ifConfigDecl:
44-
break
44+
fatalError("Should have been handled by the caller")
4545
case .importDecl(let node):
4646
return self.generate(importDecl: node).asDecl
4747
case .initializerDecl(let node):
@@ -838,7 +838,19 @@ extension ASTGenVisitor {
838838
extension ASTGenVisitor {
839839
@inline(__always)
840840
func generate(memberBlockItemList node: MemberBlockItemListSyntax) -> BridgedArrayRef {
841-
node.lazy.map(self.generate).bridgedArray(in: self)
841+
var allBridged: [BridgedDecl] = []
842+
visitIfConfigElements(node, of: MemberBlockItemSyntax.self) { element in
843+
if let ifConfigDecl = element.decl.as(IfConfigDeclSyntax.self) {
844+
return .ifConfigDecl(ifConfigDecl)
845+
}
846+
847+
return .underlying(element)
848+
} body: { member in
849+
// TODO: Set semicolon loc.
850+
allBridged.append(self.generate(decl: member.decl))
851+
}
852+
853+
return allBridged.lazy.bridgedArray(in: self)
842854
}
843855

844856
@inline(__always)

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import ASTBridging
1414
import SwiftDiagnostics
15+
import SwiftIfConfig
1516
@_spi(ExperimentalLanguageFeatures) import SwiftParser
1617
import SwiftParserDiagnostics
1718
import SwiftSyntax
@@ -142,20 +143,10 @@ public func roundTripCheck(
142143
}
143144
}
144145

145-
extension Syntax {
146-
/// Whether this syntax node is or is enclosed within a #if.
147-
fileprivate var isInIfConfig: Bool {
148-
if self.is(IfConfigDeclSyntax.self) {
149-
return true
150-
}
151-
152-
return parent?.isInIfConfig ?? false
153-
}
154-
}
155-
156146
/// Emit diagnostics within the given source file.
157147
@_cdecl("swift_ASTGen_emitParserDiagnostics")
158148
public func emitParserDiagnostics(
149+
ctx: BridgedASTContext,
159150
diagEnginePtr: UnsafeMutableRawPointer,
160151
sourceFilePtr: UnsafeMutablePointer<UInt8>,
161152
emitOnlyErrors: CInt,
@@ -172,11 +163,18 @@ public func emitParserDiagnostics(
172163
)
173164

174165
let diagnosticEngine = BridgedDiagnosticEngine(raw: diagEnginePtr)
166+
let buildConfiguration = CompilerBuildConfiguration(
167+
ctx: ctx,
168+
conditionLoc:
169+
BridgedSourceLoc(
170+
at: AbsolutePosition(utf8Offset: 0),
171+
in: sourceFile.pointee.buffer
172+
)
173+
)
174+
175175
for diag in diags {
176-
// Skip over diagnostics within #if, because we don't know whether
177-
// we are in an active region or not.
178-
// FIXME: This heuristic could be improved.
179-
if diag.node.isInIfConfig {
176+
// If the diagnostic is in an unparsed #if region, don't emit it.
177+
if diag.node.isActive(in: buildConfiguration).state == .unparsed {
180178
continue
181179
}
182180

lib/ASTGen/Sources/ASTGen/Stmts.swift

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,28 @@ extension ASTGenVisitor {
7777

7878
@inline(__always)
7979
func generate(codeBlockItemList node: CodeBlockItemListSyntax) -> BridgedArrayRef {
80-
node.lazy.map { self.generate(codeBlockItem: $0).bridged }.bridgedArray(in: self)
80+
var allItems: [BridgedASTNode] = []
81+
visitIfConfigElements(
82+
node,
83+
of: CodeBlockItemSyntax.self,
84+
split: Self.splitCodeBlockItemIfConfig
85+
) { codeBlockItem in
86+
allItems.append(self.generate(codeBlockItem: codeBlockItem).bridged)
87+
}
88+
89+
return allItems.lazy.bridgedArray(in: self)
90+
}
91+
92+
/// Function that splits a code block item into either an #if or the item.
93+
static func splitCodeBlockItemIfConfig(
94+
_ element: CodeBlockItemSyntax
95+
) -> IfConfigOrUnderlying<CodeBlockItemSyntax> {
96+
if case .decl(let decl) = element.item,
97+
let ifConfigDecl = decl.as(IfConfigDeclSyntax.self) {
98+
return .ifConfigDecl(ifConfigDecl)
99+
}
100+
101+
return .underlying(element)
81102
}
82103

83104
func generate(codeBlock node: CodeBlockSyntax) -> BridgedBraceStmt {
@@ -461,18 +482,21 @@ extension ASTGenVisitor {
461482
}
462483

463484
func generate(switchCaseList node: SwitchCaseListSyntax) -> BridgedArrayRef {
464-
return node.lazy.map({ node -> BridgedASTNode in
465-
switch node {
466-
case .switchCase(let node):
467-
return ASTNode.stmt(self.generate(switchCase: node).asStmt).bridged
468-
case .ifConfigDecl(_):
469-
fatalError("unimplemented")
470-
#if RESILIENT_SWIFT_SYNTAX
471-
@unknown default:
472-
fatalError()
473-
#endif
485+
var allBridgedCases: [BridgedASTNode] = []
486+
visitIfConfigElements(node, of: SwitchCaseSyntax.self) { element in
487+
switch element {
488+
case .ifConfigDecl(let ifConfigDecl):
489+
return .ifConfigDecl(ifConfigDecl)
490+
case .switchCase(let switchCase):
491+
return .underlying(switchCase)
474492
}
475-
}).bridgedArray(in: self)
493+
} body: { caseNode in
494+
allBridgedCases.append(
495+
ASTNode.stmt(self.generate(switchCase: caseNode).asStmt).bridged
496+
)
497+
}
498+
499+
return allBridgedCases.lazy.bridgedArray(in: self)
476500
}
477501

478502
func generateSwitchStmt(switchExpr node: SwitchExprSyntax, labelInfo: BridgedLabeledStmtInfo = nil)

0 commit comments

Comments
 (0)