Skip to content

Commit 2abddcb

Browse files
committed
[SourceKit] Use recored #if regions in "active regions" request
* Record each IfConfig clause location info in SourceFile * Update SILProfiler to handle them * Update SwiftLangSupport::findActiveRegionsInFile() to use the recorded regions instead of walking into AST to find #if regions rdar://118082146
1 parent 38ca3cc commit 2abddcb

File tree

7 files changed

+182
-121
lines changed

7 files changed

+182
-121
lines changed

include/swift/AST/SourceFile.h

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,36 +49,41 @@ enum class RestrictedImportKind {
4949
using ImportAccessLevel = std::optional<AttributedImport<ImportedModule>>;
5050

5151
/// Stores range information for a \c #if block in a SourceFile.
52-
class IfConfigRangeInfo final {
53-
/// The range of the entire \c #if block, including \c #else and \c #endif.
54-
CharSourceRange WholeRange;
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;
5570

56-
/// The range of the active selected body, if there is one. This does not
57-
/// include the outer syntax of the \c #if. This may be invalid, which
58-
/// indicates there is no active body.
59-
CharSourceRange ActiveBodyRange;
71+
ClauseKind Kind;
6072

6173
public:
62-
IfConfigRangeInfo(CharSourceRange wholeRange, CharSourceRange activeBodyRange)
63-
: WholeRange(wholeRange), ActiveBodyRange(activeBodyRange) {
64-
assert(wholeRange.getByteLength() > 0 && "Range must be non-empty");
65-
assert(activeBodyRange.isInvalid() || wholeRange.contains(activeBodyRange));
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());
6679
}
6780

68-
CharSourceRange getWholeRange() const { return WholeRange; }
69-
SourceLoc getStartLoc() const { return WholeRange.getStart(); }
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;
7085

71-
friend bool operator==(const IfConfigRangeInfo &lhs,
72-
const IfConfigRangeInfo &rhs) {
73-
return lhs.WholeRange == rhs.WholeRange &&
74-
lhs.ActiveBodyRange == rhs.ActiveBodyRange;
75-
}
76-
77-
/// Retrieve the ranges produced by subtracting the active body range from
78-
/// the whole range. This includes both inactive branches as well as the
79-
/// other syntax of the \c #if.
80-
SmallVector<CharSourceRange, 2>
81-
getRangesWithoutActiveBody(const SourceManager &SM) const;
86+
ClauseKind getKind() const { return Kind; }
8287
};
8388

8489
/// A file containing Swift source code.
@@ -244,9 +249,9 @@ class SourceFile final : public FileUnit {
244249
ParserStatePtr DelayedParserState =
245250
ParserStatePtr(/*ptr*/ nullptr, /*deleter*/ nullptr);
246251

247-
struct IfConfigRangesData {
252+
struct IfConfigClauseRangesData {
248253
/// All the \c #if source ranges in this file.
249-
std::vector<IfConfigRangeInfo> Ranges;
254+
std::vector<IfConfigClauseRangeInfo> Ranges;
250255

251256
/// Whether the elemnts in \c Ranges are sorted in source order within
252257
/// this file. We flip this to \c false any time a new range gets recorded,
@@ -255,7 +260,7 @@ class SourceFile final : public FileUnit {
255260
};
256261

257262
/// Stores all the \c #if source range info in this file.
258-
mutable IfConfigRangesData IfConfigRanges;
263+
mutable IfConfigClauseRangesData IfConfigClauseRanges;
259264

260265
friend class HasImportsMatchingFlagRequest;
261266

@@ -500,12 +505,16 @@ class SourceFile final : public FileUnit {
500505
const_cast<SourceFile *>(this)->MissingImportedModules.insert(module);
501506
}
502507

503-
/// Record the source range info for a parsed \c #if block.
504-
void recordIfConfigRangeInfo(IfConfigRangeInfo ranges);
508+
/// Record the source range info for a parsed \c #if clause.
509+
void recordIfConfigClauseRangeInfo(const IfConfigClauseRangeInfo &range);
510+
511+
/// Retrieve the source range info for any \c #if clauses in the file.
512+
ArrayRef<IfConfigClauseRangeInfo> getIfConfigClauseRanges() const;
505513

506-
/// Retrieve the source range infos for any \c #if blocks contained within a
514+
/// Retrieve the source range infos for any \c #if clauses contained within a
507515
/// given source range of this file.
508-
ArrayRef<IfConfigRangeInfo> getIfConfigsWithin(SourceRange outer) const;
516+
ArrayRef<IfConfigClauseRangeInfo>
517+
getIfConfigClausesWithin(SourceRange outer) const;
509518

510519
void getMissingImportedModules(
511520
SmallVectorImpl<ImportedModule> &imports) const override;

lib/AST/Module.cpp

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,56 +2778,66 @@ SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
27782778
return restrictiveImport;
27792779
}
27802780

2781-
SmallVector<CharSourceRange, 2>
2782-
IfConfigRangeInfo::getRangesWithoutActiveBody(const SourceManager &SM) const {
2783-
SmallVector<CharSourceRange, 2> result;
2784-
if (ActiveBodyRange.isValid()) {
2785-
// Split the whole range by the active range.
2786-
result.emplace_back(SM, WholeRange.getStart(), ActiveBodyRange.getStart());
2787-
result.emplace_back(SM, ActiveBodyRange.getEnd(), WholeRange.getEnd());
2788-
} else {
2789-
// No active body, we just return the whole range.
2790-
result.push_back(WholeRange);
2791-
}
2792-
return result;
2781+
CharSourceRange
2782+
IfConfigClauseRangeInfo::getDirectiveRange(const SourceManager &SM) const {
2783+
return CharSourceRange(SM, DirectiveLoc, BodyLoc);
27932784
}
27942785

2795-
void SourceFile::recordIfConfigRangeInfo(IfConfigRangeInfo ranges) {
2796-
IfConfigRanges.Ranges.push_back(ranges);
2797-
IfConfigRanges.IsSorted = false;
2786+
CharSourceRange
2787+
IfConfigClauseRangeInfo::getBodyRange(const SourceManager &SM) const {
2788+
return CharSourceRange(SM, BodyLoc, EndLoc);
27982789
}
27992790

2800-
ArrayRef<IfConfigRangeInfo>
2801-
SourceFile::getIfConfigsWithin(SourceRange outer) const {
2802-
auto &SM = getASTContext().SourceMgr;
2803-
assert(SM.getRangeForBuffer(BufferID).contains(outer.Start) &&
2804-
"Range not within this file?");
2791+
CharSourceRange
2792+
IfConfigClauseRangeInfo::getWholeRange(const SourceManager &SM) const {
2793+
return CharSourceRange(SM, DirectiveLoc, EndLoc);
2794+
}
2795+
2796+
void SourceFile::recordIfConfigClauseRangeInfo(
2797+
const IfConfigClauseRangeInfo &range) {
2798+
IfConfigClauseRanges.Ranges.push_back(range);
2799+
IfConfigClauseRanges.IsSorted = false;
2800+
}
28052801

2806-
if (!IfConfigRanges.IsSorted) {
2802+
ArrayRef<IfConfigClauseRangeInfo> SourceFile::getIfConfigClauseRanges() const {
2803+
if (!IfConfigClauseRanges.IsSorted) {
2804+
auto &SM = getASTContext().SourceMgr;
28072805
// Sort the ranges if we need to.
2808-
llvm::sort(IfConfigRanges.Ranges, [&](IfConfigRangeInfo lhs,
2809-
IfConfigRangeInfo rhs) {
2810-
return SM.isBeforeInBuffer(lhs.getStartLoc(), rhs.getStartLoc());
2811-
});
2806+
llvm::sort(
2807+
IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs,
2808+
const IfConfigClauseRangeInfo &rhs) {
2809+
return SM.isBeforeInBuffer(lhs.getStartLoc(), rhs.getStartLoc());
2810+
});
28122811

28132812
// Be defensive and eliminate duplicates in case we've parsed twice.
2814-
auto newEnd = std::unique(
2815-
IfConfigRanges.Ranges.begin(), IfConfigRanges.Ranges.end(),
2816-
[&](const IfConfigRangeInfo &lhs, const IfConfigRangeInfo &rhs) {
2817-
if (lhs.getWholeRange() != rhs.getWholeRange())
2813+
auto newEnd = llvm::unique(
2814+
IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs,
2815+
const IfConfigClauseRangeInfo &rhs) {
2816+
if (lhs.getStartLoc() != rhs.getStartLoc())
28182817
return false;
2819-
2820-
assert(lhs == rhs && "Active ranges changed on a re-parse?");
2818+
assert(lhs.getBodyRange(SM) == rhs.getBodyRange(SM) &&
2819+
"range changed on a re-parse?");
28212820
return true;
28222821
});
2823-
IfConfigRanges.Ranges.erase(newEnd, IfConfigRanges.Ranges.end());
2824-
IfConfigRanges.IsSorted = true;
2822+
IfConfigClauseRanges.Ranges.erase(newEnd,
2823+
IfConfigClauseRanges.Ranges.end());
2824+
IfConfigClauseRanges.IsSorted = true;
28252825
}
28262826

2827+
return IfConfigClauseRanges.Ranges;
2828+
}
2829+
2830+
ArrayRef<IfConfigClauseRangeInfo>
2831+
SourceFile::getIfConfigClausesWithin(SourceRange outer) const {
2832+
auto &SM = getASTContext().SourceMgr;
2833+
assert(SM.getRangeForBuffer(BufferID).contains(outer.Start) &&
2834+
"Range not within this file?");
2835+
28272836
// First let's find the first #if that is after the outer start loc.
2828-
auto ranges = llvm::ArrayRef(IfConfigRanges.Ranges);
2837+
auto ranges = getIfConfigClauseRanges();
28292838
auto lower = llvm::lower_bound(
2830-
ranges, outer.Start, [&](IfConfigRangeInfo range, SourceLoc loc) {
2839+
ranges, outer.Start,
2840+
[&](const IfConfigClauseRangeInfo &range, SourceLoc loc) {
28312841
return SM.isBeforeInBuffer(range.getStartLoc(), loc);
28322842
});
28332843
if (lower == ranges.end() ||
@@ -2836,7 +2846,8 @@ SourceFile::getIfConfigsWithin(SourceRange outer) const {
28362846
}
28372847
// Next let's find the first #if that's after the outer end loc.
28382848
auto upper = llvm::upper_bound(
2839-
ranges, outer.End, [&](SourceLoc loc, IfConfigRangeInfo range) {
2849+
ranges, outer.End,
2850+
[&](SourceLoc loc, const IfConfigClauseRangeInfo &range) {
28402851
return SM.isBeforeInBuffer(loc, range.getStartLoc());
28412852
});
28422853
return llvm::ArrayRef(lower, upper - lower);

lib/Parse/ParseIfConfig.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -889,10 +889,12 @@ Result Parser::parseIfConfigRaw(
889889
ClauseLoc, Condition, isActive, IfConfigElementsRole::Skipped);
890890
}
891891

892-
// Record the active body range for the SourceManager.
893-
if (shouldEvaluate && isActive) {
894-
assert(!activeBodyRange.isValid() && "Multiple active regions?");
895-
activeBodyRange = CharSourceRange(SourceMgr, bodyStart, Tok.getLoc());
892+
// Record the clause range info in SourceFile.
893+
if (shouldEvaluate) {
894+
auto kind = isActive ? IfConfigClauseRangeInfo::ActiveClause
895+
: IfConfigClauseRangeInfo::InactiveClause;
896+
SF.recordIfConfigClauseRangeInfo(
897+
{ClauseLoc, bodyStart, Tok.getLoc(), kind});
896898
}
897899

898900
if (Tok.isNot(tok::pound_elseif, tok::pound_else))
@@ -905,11 +907,11 @@ Result Parser::parseIfConfigRaw(
905907
SourceLoc EndLoc;
906908
bool HadMissingEnd = parseEndIfDirective(EndLoc);
907909

908-
// Record the #if ranges on the SourceManager.
910+
// Record the '#end' ranges in SourceFile.
909911
if (!HadMissingEnd && shouldEvaluate) {
910-
auto wholeRange = Lexer::getCharSourceRangeFromSourceRange(
911-
SourceMgr, SourceRange(startLoc, EndLoc));
912-
SF.recordIfConfigRangeInfo({wholeRange, activeBodyRange});
912+
SourceLoc EndOfEndLoc = getEndOfPreviousLoc();
913+
SF.recordIfConfigClauseRangeInfo({EndLoc, EndOfEndLoc, EndOfEndLoc,
914+
IfConfigClauseRangeInfo::EndDirective});
913915
}
914916
return finish(EndLoc, HadMissingEnd);
915917
}

lib/SIL/IR/SILProfiler.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,14 +1165,37 @@ struct CoverageMapping : public ASTWalker {
11651165
Counter.getLLVMCounter()));
11661166
}
11671167
// Add any skipped regions present in the outer range.
1168-
for (auto IfConfig : SF->getIfConfigsWithin(OuterRange)) {
1169-
for (auto SkipRange : IfConfig.getRangesWithoutActiveBody(SM)) {
1170-
auto Start = SM.getLineAndColumnInBuffer(SkipRange.getStart());
1171-
auto End = SM.getLineAndColumnInBuffer(SkipRange.getEnd());
1172-
assert(Start.first <= End.first && "region start and end out of order");
1173-
Regions.push_back(MappedRegion::skipped(Start.first, Start.second,
1174-
End.first, End.second));
1168+
for (auto clause : SF->getIfConfigClausesWithin(OuterRange)) {
1169+
CharSourceRange SkipRange;
1170+
switch (clause.getKind()) {
1171+
case IfConfigClauseRangeInfo::ActiveClause:
1172+
case IfConfigClauseRangeInfo::EndDirective:
1173+
SkipRange = clause.getDirectiveRange(SM);
1174+
break;
1175+
case IfConfigClauseRangeInfo::InactiveClause:
1176+
SkipRange = clause.getWholeRange(SM);
1177+
break;
1178+
}
1179+
if (SkipRange.getByteLength() == 0)
1180+
continue;
1181+
1182+
auto Start = SM.getLineAndColumnInBuffer(SkipRange.getStart());
1183+
auto End = SM.getLineAndColumnInBuffer(SkipRange.getEnd());
1184+
assert(Start.first <= End.first && "region start and end out of order");
1185+
1186+
// If this is consecutive with the last one, expand it.
1187+
if (!Regions.empty()) {
1188+
auto &last = Regions.back();
1189+
if (last.RegionKind == MappedRegion::Kind::Skipped &&
1190+
last.EndLine == Start.first && last.EndCol == Start.second) {
1191+
last.EndLine = End.first;
1192+
last.EndCol = End.second;
1193+
continue;
1194+
}
11751195
}
1196+
1197+
Regions.push_back(MappedRegion::skipped(Start.first, Start.second,
1198+
End.first, End.second));
11761199
}
11771200
return SILCoverageMap::create(M, SF, Filename, Name, PGOFuncName, Hash,
11781201
Regions, CounterBuilder.getExpressions());
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
func foo() -> Int {
2+
#if true
3+
#if true
4+
return 1
5+
#else
6+
return 2
7+
#endif
8+
#else
9+
return 3
10+
#endif
11+
}
12+
13+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
14+
// CHECK1: START IF CONFIGS
15+
// CHECK1-NEXT: 2:3 - active
16+
// CHECK1-NEXT: 3:3 - active
17+
// CHECK1-NEXT: 5:3 - inactive
18+
// CHECK1-NEXT: 8:3 - inactive
19+
// CHECK1-NEXT: END IF CONFIGS
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
struct Value {
2+
var prop1: Int = 0
3+
var prop2: Int = 0
4+
}
5+
func test(value: Value) {
6+
let _ = value
7+
#if false
8+
.prop1
9+
#else
10+
.prop2
11+
#endif
12+
}
13+
14+
// RUN: %sourcekitd-test -req=active-regions %s -- -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
15+
// CHECK1: START IF CONFIGS
16+
// CHECK1-NEXT: 7:1 - inactive
17+
// CHECK1-NEXT: 9:1 - active
18+
// CHECK1-NEXT: END IF CONFIGS

0 commit comments

Comments
 (0)