Skip to content

Commit 444a9d6

Browse files
committed
Re-implement SourceFile::getIfConfigClauseRanges on top of SwiftIfConfig
The SwiftIfConfig library provides APIs for evaluating and extracting the active #if regions in source code. Use its "configured regions" API along with the ASTContext-backed build configuration to reimplement the extraction of active/inactive regions from the source. This approach has the benefit of being effectively stateless: where the existing solution relies on the C++ parser recording all of the `#if` clauses it sees as it is parsing (and then might have to sort them later), this version does a scan of source to collect the list without requiring any other state. The newer implementation is also conceptually cleaner, and can be shared with other clients that have their own take on the build configuration. The primary client of this information is the SourceKit request that identifies "inactive" regions within the source file, which IDEs can use to grey out inactive code within the current build configuration. There is also some profiling information that uses it. Those clients should be unaffected by this under-the-hood change. For the moment, I'm leaving the old code path in place for compiler builds that don't have swift-syntax. This should be considered temporary, and that code should be removed in favor of request'ifying this function and removing the incrementally-built state entirely.
1 parent 530c29e commit 444a9d6

File tree

7 files changed

+185
-38
lines changed

7 files changed

+185
-38
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/AST/Attr.h"
2727
#include "swift/AST/DiagnosticConsumer.h"
2828
#include "swift/AST/DiagnosticEngine.h"
29+
#include "swift/AST/IfConfigClauseRangeInfo.h"
2930
#include "swift/AST/Stmt.h"
3031
#endif
3132

@@ -1912,6 +1913,49 @@ BridgedParameterList BridgedParameterList_createParsed(
19121913
BridgedASTContext cContext, BridgedSourceLoc cLeftParenLoc,
19131914
BridgedArrayRef cParameters, BridgedSourceLoc cRightParenLoc);
19141915

1916+
//===----------------------------------------------------------------------===//
1917+
// MARK: #if handling
1918+
//===----------------------------------------------------------------------===//
1919+
1920+
/// Bridged version of IfConfigClauseRangeInfo::ClauseKind.
1921+
enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedIfConfigClauseKind : size_t {
1922+
IfConfigActive,
1923+
IfConfigInactive,
1924+
IfConfigEnd
1925+
};
1926+
1927+
/// Bridged version of IfConfigClauseRangeInfo.
1928+
struct BridgedIfConfigClauseRangeInfo {
1929+
BridgedSourceLoc directiveLoc;
1930+
BridgedSourceLoc bodyLoc;
1931+
BridgedSourceLoc endLoc;
1932+
BridgedIfConfigClauseKind kind;
1933+
1934+
#ifdef USED_IN_CPP_SOURCE
1935+
swift::IfConfigClauseRangeInfo unbridged() const {
1936+
swift::IfConfigClauseRangeInfo::ClauseKind clauseKind;
1937+
switch (kind) {
1938+
case IfConfigActive:
1939+
clauseKind = swift::IfConfigClauseRangeInfo::ActiveClause;
1940+
break;
1941+
1942+
case IfConfigInactive:
1943+
clauseKind = swift::IfConfigClauseRangeInfo::InactiveClause;
1944+
break;
1945+
1946+
case IfConfigEnd:
1947+
clauseKind = swift::IfConfigClauseRangeInfo::EndDirective;
1948+
break;
1949+
}
1950+
1951+
return swift::IfConfigClauseRangeInfo(directiveLoc.unbridged(),
1952+
bodyLoc.unbridged(),
1953+
endLoc.unbridged(),
1954+
clauseKind);
1955+
}
1956+
#endif
1957+
};
1958+
19151959
//===----------------------------------------------------------------------===//
19161960
// MARK: Plugins
19171961
//===----------------------------------------------------------------------===//
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===--- ASTBridging.h - header for the swift SILBridging module ----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 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+
#ifndef SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H
14+
#define SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H
15+
16+
#include "swift/Basic/SourceLoc.h"
17+
18+
namespace swift {
19+
20+
class SourceManager;
21+
22+
/// Stores range information for a \c #if block in a SourceFile.
23+
class IfConfigClauseRangeInfo final {
24+
public:
25+
enum ClauseKind {
26+
// Active '#if', '#elseif', or '#else' clause.
27+
ActiveClause,
28+
// Inactive '#if', '#elseif', or '#else' clause.
29+
InactiveClause,
30+
// '#endif' directive.
31+
EndDirective,
32+
};
33+
34+
private:
35+
/// Source location of '#if', '#elseif', etc.
36+
SourceLoc DirectiveLoc;
37+
/// Character source location of body starts.
38+
SourceLoc BodyLoc;
39+
/// Location of the end of the body.
40+
SourceLoc EndLoc;
41+
42+
ClauseKind Kind;
43+
44+
public:
45+
IfConfigClauseRangeInfo(SourceLoc DirectiveLoc, SourceLoc BodyLoc,
46+
SourceLoc EndLoc, ClauseKind Kind)
47+
: DirectiveLoc(DirectiveLoc), BodyLoc(BodyLoc), EndLoc(EndLoc),
48+
Kind(Kind) {
49+
assert(DirectiveLoc.isValid() && BodyLoc.isValid() && EndLoc.isValid());
50+
}
51+
52+
SourceLoc getStartLoc() const { return DirectiveLoc; }
53+
CharSourceRange getDirectiveRange(const SourceManager &SM) const;
54+
CharSourceRange getWholeRange(const SourceManager &SM) const;
55+
CharSourceRange getBodyRange(const SourceManager &SM) const;
56+
57+
ClauseKind getKind() const { return Kind; }
58+
};
59+
60+
}
61+
62+
#endif /* SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H */

include/swift/AST/SourceFile.h

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "swift/AST/ASTNode.h"
1717
#include "swift/AST/FileUnit.h"
18+
#include "swift/AST/IfConfigClauseRangeInfo.h"
1819
#include "swift/AST/Import.h"
1920
#include "swift/AST/SynthesizedFileUnit.h"
2021
#include "swift/Basic/Debug.h"
@@ -48,44 +49,6 @@ enum class RestrictedImportKind {
4849
/// Import that limits the access level of imported entities.
4950
using ImportAccessLevel = std::optional<AttributedImport<ImportedModule>>;
5051

51-
/// Stores range information for a \c #if block in a SourceFile.
52-
class IfConfigClauseRangeInfo final {
53-
public:
54-
enum ClauseKind {
55-
// Active '#if', '#elseif', or '#else' clause.
56-
ActiveClause,
57-
// Inactive '#if', '#elseif', or '#else' clause.
58-
InactiveClause,
59-
// '#endif' directive.
60-
EndDirective,
61-
};
62-
63-
private:
64-
/// Source location of '#if', '#elseif', etc.
65-
SourceLoc DirectiveLoc;
66-
/// Character source location of body starts.
67-
SourceLoc BodyLoc;
68-
/// Location of the end of the body.
69-
SourceLoc EndLoc;
70-
71-
ClauseKind Kind;
72-
73-
public:
74-
IfConfigClauseRangeInfo(SourceLoc DirectiveLoc, SourceLoc BodyLoc,
75-
SourceLoc EndLoc, ClauseKind Kind)
76-
: DirectiveLoc(DirectiveLoc), BodyLoc(BodyLoc), EndLoc(EndLoc),
77-
Kind(Kind) {
78-
assert(DirectiveLoc.isValid() && BodyLoc.isValid() && EndLoc.isValid());
79-
}
80-
81-
SourceLoc getStartLoc() const { return DirectiveLoc; }
82-
CharSourceRange getDirectiveRange(const SourceManager &SM) const;
83-
CharSourceRange getWholeRange(const SourceManager &SM) const;
84-
CharSourceRange getBodyRange(const SourceManager &SM) const;
85-
86-
ClauseKind getKind() const { return Kind; }
87-
};
88-
8952
/// A file containing Swift source code.
9053
///
9154
/// This is a .swift or .sil file (or a virtual file, such as the contents of

include/swift/Bridging/ASTGen.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ bool swift_ASTGen_parseRegexLiteral(BridgedStringRef inputPtr,
151151
BridgedSourceLoc diagLoc,
152152
BridgedDiagnosticEngine diagEngine);
153153

154+
intptr_t swift_ASTGen_configuredRegions(
155+
BridgedASTContext astContext,
156+
void *_Nonnull sourceFile,
157+
BridgedIfConfigClauseRangeInfo *_Nullable *_Nonnull);
158+
154159
#ifdef __cplusplus
155160
}
156161
#endif

lib/AST/Module.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "swift/Basic/SourceManager.h"
4848
#include "swift/Basic/Statistic.h"
4949
#include "swift/Basic/StringExtras.h"
50+
#include "swift/Bridging/ASTGen.h"
5051
#include "swift/Demangling/ManglingMacros.h"
5152
#include "swift/Parse/Token.h"
5253
#include "swift/Strings.h"
@@ -2954,6 +2955,21 @@ void SourceFile::recordIfConfigClauseRangeInfo(
29542955
}
29552956

29562957
ArrayRef<IfConfigClauseRangeInfo> SourceFile::getIfConfigClauseRanges() const {
2958+
#if SWIFT_BUILD_SWIFT_SYNTAX
2959+
if (!IfConfigClauseRanges.IsSorted) {
2960+
IfConfigClauseRanges.Ranges.clear();
2961+
2962+
BridgedIfConfigClauseRangeInfo *regions;
2963+
intptr_t numRegions = swift_ASTGen_configuredRegions(
2964+
getASTContext(), getExportedSourceFile(), &regions);
2965+
IfConfigClauseRanges.Ranges.reserve(numRegions);
2966+
for (intptr_t i = 0; i != numRegions; ++i)
2967+
IfConfigClauseRanges.Ranges.push_back(regions[i].unbridged());
2968+
free(regions);
2969+
2970+
IfConfigClauseRanges.IsSorted = true;
2971+
}
2972+
#else
29572973
if (!IfConfigClauseRanges.IsSorted) {
29582974
auto &SM = getASTContext().SourceMgr;
29592975
// Sort the ranges if we need to.
@@ -2977,6 +2993,7 @@ ArrayRef<IfConfigClauseRangeInfo> SourceFile::getIfConfigClauseRanges() const {
29772993
IfConfigClauseRanges.Ranges.end());
29782994
IfConfigClauseRanges.IsSorted = true;
29792995
}
2996+
#endif
29802997

29812998
return IfConfigClauseRanges.Ranges;
29822999
}

lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift

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

1313
import ASTBridging
1414
import SwiftIfConfig
15+
import SwiftSyntax
1516

1617
/// A build configuration that uses the compiler's ASTContext to answer
1718
/// queries.
@@ -144,3 +145,52 @@ struct CompilerBuildConfiguration: BuildConfiguration {
144145
return version
145146
}
146147
}
148+
149+
/// Extract the #if clause range information for the given source file.
150+
@_cdecl("swift_ASTGen_configuredRegions")
151+
public func configuredRegions(
152+
astContext: BridgedASTContext,
153+
sourceFilePtr: UnsafeRawPointer,
154+
cRegionsOut: UnsafeMutablePointer<UnsafeMutablePointer<BridgedIfConfigClauseRangeInfo>?>
155+
) -> Int {
156+
let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1)
157+
let configuration = CompilerBuildConfiguration(ctx: astContext)
158+
let regions = sourceFilePtr.pointee.syntax.configuredRegions(in: configuration)
159+
160+
var cRegions: [BridgedIfConfigClauseRangeInfo] = []
161+
for (ifConfig, state) in regions {
162+
let kind: BridgedIfConfigClauseKind
163+
switch state {
164+
case .active: kind = .IfConfigActive
165+
case .inactive, .unparsed: kind = .IfConfigInactive
166+
}
167+
168+
let bodyLoc: AbsolutePosition
169+
if let elements = ifConfig.elements {
170+
bodyLoc = elements.position
171+
} else if let condition = ifConfig.condition {
172+
bodyLoc = condition.endPosition
173+
} else {
174+
bodyLoc = ifConfig.endPosition
175+
}
176+
177+
cRegions.append(
178+
.init(
179+
directiveLoc: sourceFilePtr.pointee.sourceLoc(
180+
at: ifConfig.poundKeyword.positionAfterSkippingLeadingTrivia
181+
),
182+
bodyLoc: sourceFilePtr.pointee.sourceLoc(at: bodyLoc),
183+
endLoc: sourceFilePtr.pointee.sourceLoc(
184+
at: ifConfig.endPosition
185+
),
186+
kind: kind
187+
)
188+
)
189+
}
190+
191+
let cRegionsBuf: UnsafeMutableBufferPointer<BridgedIfConfigClauseRangeInfo> =
192+
.allocate(capacity: cRegions.count)
193+
_ = cRegionsBuf.initialize(from: cRegions)
194+
cRegionsOut.pointee = cRegionsBuf.baseAddress
195+
return cRegionsBuf.count
196+
}

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ public struct ExportedSourceFile {
4444
}
4545
return AbsolutePosition(utf8Offset: opaqueValue - sourceFileBaseAddress)
4646
}
47+
48+
/// Retrieve a bridged source location for the given absolute position in
49+
/// this source file.
50+
public func sourceLoc(at position: AbsolutePosition) -> BridgedSourceLoc {
51+
BridgedSourceLoc(at: position, in: buffer)
52+
}
4753
}
4854

4955
extension Parser.ExperimentalFeatures {

0 commit comments

Comments
 (0)