Skip to content

Commit 42809af

Browse files
authored
Merge pull request #76327 from DougGregor/suppress-warnings-in-inactive-regions-without-ifconfigdecl
Suppress certain warnings in inactive `#if` regions without relying on `IfConfigDecl`
2 parents adc80ec + 70c93c6 commit 42809af

File tree

8 files changed

+299
-137
lines changed

8 files changed

+299
-137
lines changed

lib/ASTGen/Sources/ASTGen/ASTGen+CompilerBuildConfiguration.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ extension ASTGenVisitor {
2525
/// produced due to the evaluation.
2626
func activeClause(in node: IfConfigDeclSyntax) -> IfConfigClauseSyntax? {
2727
// Determine the active clause.
28-
let (activeClause, diagnostics) = node.activeClause(in: buildConfiguration)
29-
diagnoseAll(diagnostics)
30-
31-
return activeClause
28+
return configuredRegions.activeClause(for: node)
3229
}
3330

3431
/// Visit a collection of elements that may contain #if clauses within it

lib/ASTGen/Sources/ASTGen/ASTGen.swift

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ASTBridging
1414
import BasicBridging
1515
import ParseBridging
16+
import SwiftIfConfig
1617
// Needed to use BumpPtrAllocator
1718
@_spi(BumpPtrAllocator) @_spi(RawSyntax) import SwiftSyntax
1819

@@ -76,7 +77,7 @@ struct ASTGenVisitor {
7677

7778
let ctx: BridgedASTContext
7879

79-
let buildConfiguration: CompilerBuildConfiguration
80+
let configuredRegions: ConfiguredRegions
8081

8182
fileprivate let allocator: SwiftSyntax.BumpPtrAllocator = .init(initialSlabSize: 256)
8283

@@ -89,17 +90,15 @@ struct ASTGenVisitor {
8990
sourceBuffer: UnsafeBufferPointer<UInt8>,
9091
declContext: BridgedDeclContext,
9192
astContext: BridgedASTContext,
93+
configuredRegions: ConfiguredRegions,
9294
legacyParser: BridgedLegacyParser
9395
) {
9496
self.diagnosticEngine = diagnosticEngine
9597
self.base = sourceBuffer
9698
self.declContext = declContext
9799
self.ctx = astContext
100+
self.configuredRegions = configuredRegions
98101
self.legacyParse = legacyParser
99-
self.buildConfiguration = CompilerBuildConfiguration(
100-
ctx: ctx,
101-
sourceBuffer: sourceBuffer
102-
)
103102
}
104103

105104
func generate(sourceFile node: SourceFileSyntax) -> [BridgedDecl] {
@@ -423,31 +422,36 @@ extension TokenSyntax {
423422
@_cdecl("swift_ASTGen_buildTopLevelASTNodes")
424423
public func buildTopLevelASTNodes(
425424
diagEngine: BridgedDiagnosticEngine,
426-
sourceFilePtr: UnsafeRawPointer,
425+
sourceFilePtr: UnsafeMutableRawPointer,
427426
dc: BridgedDeclContext,
428427
ctx: BridgedASTContext,
429428
legacyParser: BridgedLegacyParser,
430429
outputContext: UnsafeMutableRawPointer,
431430
callback: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void
432431
) {
433432
let sourceFile = sourceFilePtr.assumingMemoryBound(to: ExportedSourceFile.self)
434-
ASTGenVisitor(
433+
let visitor = ASTGenVisitor(
435434
diagnosticEngine: diagEngine,
436435
sourceBuffer: sourceFile.pointee.buffer,
437436
declContext: dc,
438437
astContext: ctx,
438+
configuredRegions: sourceFile.pointee.configuredRegions(astContext: ctx),
439439
legacyParser: legacyParser
440440
)
441-
.generate(sourceFile: sourceFile.pointee.syntax)
442-
.forEach { callback($0.raw, outputContext) }
441+
442+
visitor.generate(sourceFile: sourceFile.pointee.syntax)
443+
.forEach { callback($0.raw, outputContext) }
444+
445+
// Diagnose any errors from evaluating #ifs.
446+
visitor.diagnoseAll(visitor.configuredRegions.diagnostics)
443447
}
444448

445449
/// Generate an AST node at the given source location. Returns the generated
446450
/// ASTNode and mutate the pointee of `endLocPtr` to the end of the node.
447451
private func _build<Node: SyntaxProtocol, Result>(
448452
generator: (ASTGenVisitor) -> (Node) -> Result,
449453
diagEngine: BridgedDiagnosticEngine,
450-
sourceFilePtr: UnsafeRawPointer,
454+
sourceFilePtr: UnsafeMutableRawPointer,
451455
sourceLoc: BridgedSourceLoc,
452456
declContext: BridgedDeclContext,
453457
astContext: BridgedASTContext,
@@ -480,6 +484,7 @@ private func _build<Node: SyntaxProtocol, Result>(
480484
sourceBuffer: sourceFile.pointee.buffer,
481485
declContext: declContext,
482486
astContext: astContext,
487+
configuredRegions: sourceFile.pointee.configuredRegions(astContext: astContext),
483488
legacyParser: legacyParser
484489
)
485490
)(node)
@@ -489,7 +494,7 @@ private func _build<Node: SyntaxProtocol, Result>(
489494
@usableFromInline
490495
func buildTypeRepr(
491496
diagEngine: BridgedDiagnosticEngine,
492-
sourceFilePtr: UnsafeRawPointer,
497+
sourceFilePtr: UnsafeMutableRawPointer,
493498
sourceLoc: BridgedSourceLoc,
494499
declContext: BridgedDeclContext,
495500
astContext: BridgedASTContext,
@@ -512,7 +517,7 @@ func buildTypeRepr(
512517
@usableFromInline
513518
func buildDecl(
514519
diagEngine: BridgedDiagnosticEngine,
515-
sourceFilePtr: UnsafeRawPointer,
520+
sourceFilePtr: UnsafeMutableRawPointer,
516521
sourceLoc: BridgedSourceLoc,
517522
declContext: BridgedDeclContext,
518523
astContext: BridgedASTContext,
@@ -535,7 +540,7 @@ func buildDecl(
535540
@usableFromInline
536541
func buildExpr(
537542
diagEngine: BridgedDiagnosticEngine,
538-
sourceFilePtr: UnsafeRawPointer,
543+
sourceFilePtr: UnsafeMutableRawPointer,
539544
sourceLoc: BridgedSourceLoc,
540545
declContext: BridgedDeclContext,
541546
astContext: BridgedASTContext,
@@ -558,7 +563,7 @@ func buildExpr(
558563
@usableFromInline
559564
func buildStmt(
560565
diagEngine: BridgedDiagnosticEngine,
561-
sourceFilePtr: UnsafeRawPointer,
566+
sourceFilePtr: UnsafeMutableRawPointer,
562567
sourceLoc: BridgedSourceLoc,
563568
declContext: BridgedDeclContext,
564569
astContext: BridgedASTContext,

lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift

Lines changed: 146 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,20 +179,33 @@ enum IfConfigError: Error, CustomStringConvertible {
179179
}
180180
}
181181

182+
extension ExportedSourceFile {
183+
/// Return the configured regions for this source file.
184+
mutating func configuredRegions(astContext: BridgedASTContext) -> ConfiguredRegions {
185+
if let _configuredRegions {
186+
return _configuredRegions
187+
}
188+
189+
let configuration = CompilerBuildConfiguration(
190+
ctx: astContext,
191+
sourceBuffer: buffer
192+
)
193+
194+
let regions = syntax.configuredRegions(in: configuration)
195+
_configuredRegions = regions
196+
return regions
197+
}
198+
}
182199

183200
/// Extract the #if clause range information for the given source file.
184201
@_cdecl("swift_ASTGen_configuredRegions")
185202
public func configuredRegions(
186203
astContext: BridgedASTContext,
187-
sourceFilePtr: UnsafeRawPointer,
204+
sourceFilePtr: UnsafeMutableRawPointer,
188205
cRegionsOut: UnsafeMutablePointer<UnsafeMutablePointer<BridgedIfConfigClauseRangeInfo>?>
189206
) -> Int {
190207
let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1)
191-
let configuration = CompilerBuildConfiguration(
192-
ctx: astContext,
193-
sourceBuffer: sourceFilePtr.pointee.buffer
194-
)
195-
let regions = sourceFilePtr.pointee.syntax.configuredRegions(in: configuration)
208+
let regions = sourceFilePtr.pointee.configuredRegions(astContext: astContext)
196209

197210
var cRegions: [BridgedIfConfigClauseRangeInfo] = []
198211

@@ -321,6 +334,133 @@ public func freeConfiguredRegions(
321334
UnsafeMutableBufferPointer(start: regions, count: numRegions).deallocate()
322335
}
323336

337+
/// Cache used when checking for inactive code that might contain a reference
338+
/// to specific names.
339+
private struct InactiveCodeContainsReferenceCache {
340+
let syntax: SourceFileSyntax
341+
let configuredRegions: ConfiguredRegions
342+
}
343+
344+
/// Search in inactive/unparsed code to look for evidence that something that
345+
/// looks unused is actually used in some configurations.
346+
private enum InactiveCodeChecker {
347+
case name(String)
348+
case tryOrThrow
349+
350+
/// Search for the kind of entity in the given syntax node.
351+
func search(syntax: SourceFileSyntax, configuredRegions: ConfiguredRegions) -> Bool {
352+
// If there are no regions, everything is active. This is the common case.
353+
if configuredRegions.isEmpty {
354+
return false
355+
}
356+
357+
for token in syntax.tokens(viewMode: .sourceAccurate) {
358+
// Match what we're looking for, and continue iterating if it doesn't
359+
// match.
360+
switch self {
361+
case .name(let name):
362+
guard let identifier = token.identifier, identifier.name == name else {
363+
continue
364+
}
365+
366+
break
367+
368+
case .tryOrThrow:
369+
guard let keywordKind = token.keywordKind,
370+
keywordKind == .try || keywordKind == .throw else {
371+
continue
372+
}
373+
374+
break
375+
}
376+
377+
// We matched what we were looking for, now check whether it is in an
378+
// inactive or unparsed region.
379+
if configuredRegions.isActive(token) != .active {
380+
// Found it in a non-active region.
381+
return true
382+
}
383+
}
384+
385+
return false
386+
}
387+
}
388+
389+
/// Determine whether the inactive code within the given search range
390+
/// contains a token referring to the given name.
391+
@_cdecl("swift_ASTGen_inactiveCodeContainsReference")
392+
public func inactiveCodeContainsReference(
393+
astContext: BridgedASTContext,
394+
sourceFileBuffer: BridgedStringRef,
395+
searchRange: BridgedStringRef,
396+
bridgedName: BridgedStringRef,
397+
untypedCachePtr: UnsafeMutablePointer<UnsafeMutableRawPointer?>
398+
) -> Bool {
399+
let syntax: SourceFileSyntax
400+
let configuredRegions: ConfiguredRegions
401+
if let untypedCachePtr = untypedCachePtr.pointee {
402+
// Use the cache.
403+
let cache = untypedCachePtr.assumingMemoryBound(to: InactiveCodeContainsReferenceCache.self)
404+
syntax = cache.pointee.syntax
405+
configuredRegions = cache.pointee.configuredRegions
406+
} else {
407+
// Parse the region.
408+
409+
// FIXME: Use 'ExportedSourceFile' when C++ parser is replaced.
410+
let searchRangeBuffer = UnsafeBufferPointer<UInt8>(start: searchRange.data, count: searchRange.count)
411+
syntax = Parser.parse(source: searchRangeBuffer)
412+
413+
// Compute the configured regions within the search range.
414+
let configuration = CompilerBuildConfiguration(
415+
ctx: astContext,
416+
sourceBuffer: searchRangeBuffer
417+
)
418+
configuredRegions = syntax.configuredRegions(in: configuration)
419+
420+
let cache = UnsafeMutablePointer<InactiveCodeContainsReferenceCache>.allocate(capacity: 1)
421+
cache.initialize(to: .init(syntax: syntax, configuredRegions: configuredRegions))
422+
423+
untypedCachePtr.pointee = .init(cache)
424+
}
425+
426+
return InactiveCodeChecker.name(String(bridged: bridgedName))
427+
.search(syntax: syntax, configuredRegions: configuredRegions)
428+
}
429+
430+
@_cdecl("swift_ASTGen_inactiveCodeContainsTryOrThrow")
431+
public func inactiveCodeContainsTryOrThrow(
432+
astContext: BridgedASTContext,
433+
sourceFileBuffer: BridgedStringRef,
434+
searchRange: BridgedStringRef
435+
) -> Bool {
436+
// Parse the region.
437+
438+
// FIXME: Use 'ExportedSourceFile' when C++ parser is replaced.
439+
let searchRangeBuffer = UnsafeBufferPointer<UInt8>(start: searchRange.data, count: searchRange.count)
440+
let syntax = Parser.parse(source: searchRangeBuffer)
441+
442+
// Compute the configured regions within the search range.
443+
let configuration = CompilerBuildConfiguration(
444+
ctx: astContext,
445+
sourceBuffer: searchRangeBuffer
446+
)
447+
let configuredRegions = syntax.configuredRegions(in: configuration)
448+
449+
return InactiveCodeChecker.tryOrThrow
450+
.search(syntax: syntax, configuredRegions: configuredRegions)
451+
}
452+
453+
@_cdecl("swift_ASTGen_freeInactiveCodeContainsReferenceCache")
454+
public func freeInactiveCodeContainsReferenceCache(pointer: UnsafeMutableRawPointer?) {
455+
guard let pointer else {
456+
return
457+
}
458+
459+
let cache = pointer.assumingMemoryBound(to: InactiveCodeContainsReferenceCache.self)
460+
cache.deinitialize(count: 1)
461+
cache.deallocate()
462+
}
463+
324464
/// Evaluate the #if condition at ifClauseLocationPtr.
325465
@_cdecl("swift_ASTGen_evaluatePoundIfCondition")
326466
public func evaluatePoundIfCondition(

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public struct ExportedSourceFile {
3838
/// Cached so we don't need to re-build the line table every time we need to convert a position.
3939
let sourceLocationConverter: SourceLocationConverter
4040

41+
/// Configured regions for this source file.
42+
///
43+
/// This is a cached value; access via configuredRegions(astContext:).
44+
var _configuredRegions: ConfiguredRegions? = nil
45+
4146
public func position(of location: BridgedSourceLoc) -> AbsolutePosition? {
4247
let sourceFileBaseAddress = UnsafeRawPointer(buffer.baseAddress!)
4348
guard let opaqueValue = location.getOpaquePointerValue() else {
@@ -161,12 +166,7 @@ public func emitParserDiagnostics(
161166
let diags = ParseDiagnosticsGenerator.diagnostics(for: sourceFileSyntax)
162167

163168
let diagnosticEngine = BridgedDiagnosticEngine(raw: diagEnginePtr)
164-
let buildConfiguration = CompilerBuildConfiguration(
165-
ctx: ctx,
166-
sourceBuffer: sourceFile.pointee.buffer
167-
)
168-
169-
let configuredRegions = sourceFileSyntax.configuredRegions(in: buildConfiguration)
169+
let configuredRegions = sourceFile.pointee.configuredRegions(astContext: ctx)
170170
for diag in diags {
171171
// If the diagnostic is in an unparsed #if region, don't emit it.
172172
if configuredRegions.isActive(diag.node) == .unparsed {

0 commit comments

Comments
 (0)