Skip to content

Commit 876682f

Browse files
committed
[SourceKit] Add "Active Regions" request
`source.request.activeregions` is a new request that reports all `#if`, `#else` and `#elseif` decls in the response with an `is_active` flag. This is mainly useful for a client to know which branches are active, but for completeness sake, the active decls are also reported. Note: it only reports the positions of the decls, not the entire ranges. It's up to clients to map this to the syntactic structure of a tree. Reporting the ranges of the decls could be confusing in the case of nested `#if`s, where the ranges can overlap.
1 parent 45edfc3 commit 876682f

File tree

11 files changed

+279
-0
lines changed

11 files changed

+279
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#if FLAG_1
2+
class Foo {
3+
#if FLAG_2
4+
func debugOnly() {
5+
6+
}
7+
#endif
8+
}
9+
#elseif FLAG_3
10+
class GracefulFallback {
11+
12+
}
13+
#else
14+
class Fallback {
15+
16+
}
17+
#endif
18+
19+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
20+
// CHECK1: START IF CONFIGS
21+
// CHECK1-NEXT: 1:1 - active
22+
// CHECK1-NEXT: 3:5 - inactive
23+
// CHECK1-NEXT: 9:1 - inactive
24+
// CHECK1-NEXT: 13:1 - inactive
25+
// CHECK1-NEXT: END IF CONFIGS
26+
27+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_2 -module-name active_regions %s | %FileCheck -check-prefix=CHECK2 %s
28+
// CHECK2: START IF CONFIGS
29+
// CHECK2-NEXT: 1:1 - inactive
30+
// CHECK2-NEXT: 9:1 - inactive
31+
// CHECK2-NEXT: 13:1 - active
32+
// CHECK2-NEXT: END IF CONFIGS
33+
34+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -D FLAG_2 -module-name active_regions %s | %FileCheck -check-prefix=CHECK3 %s
35+
// CHECK3: START IF CONFIGS
36+
// CHECK3-NEXT: 1:1 - active
37+
// CHECK3-NEXT: 3:5 - active
38+
// CHECK3-NEXT: 9:1 - inactive
39+
// CHECK3-NEXT: 13:1 - inactive
40+
// CHECK3-NEXT: END IF CONFIGS
41+
42+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_3 -module-name active_regions %s | %FileCheck -check-prefix=CHECK4 %s
43+
// CHECK4: START IF CONFIGS
44+
// CHECK4-NEXT: 1:1 - inactive
45+
// CHECK4-NEXT: 9:1 - active
46+
// CHECK4-NEXT: 13:1 - inactive
47+
// CHECK4-NEXT: END IF CONFIGS
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if FOO > 10
2+
class Foo {
3+
}
4+
#else
5+
class Fallback {
6+
}
7+
#endif
8+
// RUN: %sourcekitd-test -req=active-regions %s -- -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
9+
// CHECK1: START IF CONFIGS
10+
// CHECK1-NEXT: 1:1 - inactive
11+
// CHECK1-NEXT: 4:1 - active
12+
// CHECK1-NEXT: END IF CONFIGS
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if FLAG_1
2+
class Foo {
3+
}
4+
#else
5+
class Fallback {
6+
}
7+
8+
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
9+
// CHECK1: START IF CONFIGS
10+
// CHECK1-NEXT: 1:1 - active
11+
// CHECK1-NEXT: 4:1 - inactive
12+
// CHECK1-NEXT: END IF CONFIGS

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,20 @@ struct RelatedIdentsInfo {
648648
ArrayRef<std::pair<unsigned, unsigned>> Ranges;
649649
};
650650

651+
/// Represent one branch of an if config.
652+
/// Either `#if`, `#else` or `#elseif`.
653+
struct IfConfigInfo {
654+
unsigned Offset;
655+
bool IsActive;
656+
657+
IfConfigInfo(unsigned Offset, bool IsActive)
658+
: Offset(Offset), IsActive(IsActive) {}
659+
};
660+
661+
struct ActiveRegionsInfo {
662+
ArrayRef<IfConfigInfo> Configs;
663+
};
664+
651665
/// Filled out by LangSupport::findInterfaceDocument().
652666
struct InterfaceDocInfo {
653667
/// Non-empty if a generated interface editor document has previously been
@@ -993,6 +1007,12 @@ class LangSupport {
9931007
std::function<void(const RequestResult<RelatedIdentsInfo> &)>
9941008
Receiver) = 0;
9951009

1010+
virtual void findActiveRegionsInFile(
1011+
StringRef Filename, ArrayRef<const char *> Args,
1012+
SourceKitCancellationToken CancellationToken,
1013+
std::function<void(const RequestResult<ActiveRegionsInfo> &)>
1014+
Receiver) = 0;
1015+
9961016
virtual llvm::Optional<std::pair<unsigned, unsigned>>
9971017
findUSRRange(StringRef DocumentName, StringRef USR) = 0;
9981018

tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,12 @@ class SwiftLangSupport : public LangSupport {
672672
std::function<void(const RequestResult<RelatedIdentsInfo> &)> Receiver)
673673
override;
674674

675+
void findActiveRegionsInFile(
676+
StringRef Filename, ArrayRef<const char *> Args,
677+
SourceKitCancellationToken CancellationToken,
678+
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver)
679+
override;
680+
675681
void syntacticRename(llvm::MemoryBuffer *InputBuf,
676682
ArrayRef<RenameLocations> RenameLocations,
677683
ArrayRef<const char*> Args,

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,6 +2572,101 @@ void SwiftLangSupport::findRelatedIdentifiersInFile(
25722572
llvm::vfs::getRealFileSystem());
25732573
}
25742574

2575+
//===----------------------------------------------------------------------===//
2576+
// SwiftLangSupport::findActiveRegionsInFile
2577+
//===----------------------------------------------------------------------===//
2578+
2579+
namespace {
2580+
class IfConfigScanner : public SourceEntityWalker {
2581+
SmallVectorImpl<IfConfigInfo> &Infos;
2582+
SourceManager &SourceMgr;
2583+
unsigned BufferID = -1;
2584+
bool Cancelled = false;
2585+
2586+
public:
2587+
explicit IfConfigScanner(SourceFile &SrcFile, unsigned BufferID,
2588+
SmallVectorImpl<IfConfigInfo> &Infos)
2589+
: Infos(Infos), SourceMgr(SrcFile.getASTContext().SourceMgr),
2590+
BufferID(BufferID) {}
2591+
2592+
private:
2593+
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
2594+
if (Cancelled)
2595+
return false;
2596+
2597+
if (auto *IfDecl = dyn_cast<IfConfigDecl>(D)) {
2598+
for (auto &Clause : IfDecl->getClauses()) {
2599+
unsigned Offset = SourceMgr.getLocOffsetInBuffer(Clause.Loc, BufferID);
2600+
Infos.emplace_back(Offset, Clause.isActive);
2601+
}
2602+
}
2603+
2604+
return true;
2605+
}
2606+
};
2607+
2608+
} // end anonymous namespace
2609+
2610+
void SwiftLangSupport::findActiveRegionsInFile(
2611+
StringRef InputFile, ArrayRef<const char *> Args,
2612+
SourceKitCancellationToken CancellationToken,
2613+
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver) {
2614+
2615+
std::string Error;
2616+
SwiftInvocationRef Invok =
2617+
ASTMgr->getTypecheckInvocation(Args, InputFile, Error);
2618+
if (!Invok) {
2619+
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
2620+
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
2621+
return;
2622+
}
2623+
2624+
class IfConfigConsumer : public SwiftASTConsumer {
2625+
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver;
2626+
SwiftInvocationRef Invok;
2627+
2628+
public:
2629+
IfConfigConsumer(
2630+
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver,
2631+
SwiftInvocationRef Invok)
2632+
: Receiver(std::move(Receiver)), Invok(Invok) {}
2633+
2634+
void handlePrimaryAST(ASTUnitRef AstUnit) override {
2635+
auto &SrcFile = AstUnit->getPrimarySourceFile();
2636+
SmallVector<IfConfigInfo> Configs;
2637+
auto BufferID = SrcFile.getBufferID();
2638+
if (!BufferID)
2639+
return;
2640+
IfConfigScanner Scanner(SrcFile, *BufferID, Configs);
2641+
Scanner.walk(SrcFile);
2642+
2643+
// Sort by offset so nested decls are reported
2644+
// in source order (not tree order).
2645+
llvm::sort(Configs,
2646+
[](const IfConfigInfo &LHS, const IfConfigInfo &RHS) -> bool {
2647+
return LHS.Offset < RHS.Offset;
2648+
});
2649+
ActiveRegionsInfo Info;
2650+
Info.Configs = Configs;
2651+
Receiver(RequestResult<ActiveRegionsInfo>::fromResult(Info));
2652+
}
2653+
2654+
void cancelled() override {
2655+
Receiver(RequestResult<ActiveRegionsInfo>::cancelled());
2656+
}
2657+
2658+
void failed(StringRef Error) override {
2659+
LOG_WARN_FUNC("inactive ranges failed: " << Error);
2660+
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
2661+
}
2662+
};
2663+
2664+
auto Consumer = std::make_shared<IfConfigConsumer>(Receiver, Invok);
2665+
ASTMgr->processASTAsync(Invok, std::move(Consumer),
2666+
/*OncePerASTToken=*/nullptr, CancellationToken,
2667+
llvm::vfs::getRealFileSystem());
2668+
}
2669+
25752670
//===----------------------------------------------------------------------===//
25762671
// SwiftLangSupport::semanticRefactoring
25772672
//===----------------------------------------------------------------------===//

tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
120120
.Case("conformingmethods", SourceKitRequest::ConformingMethodList)
121121
.Case("cursor", SourceKitRequest::CursorInfo)
122122
.Case("related-idents", SourceKitRequest::RelatedIdents)
123+
.Case("active-regions", SourceKitRequest::ActiveRegions)
123124
.Case("syntax-map", SourceKitRequest::SyntaxMap)
124125
.Case("structure", SourceKitRequest::Structure)
125126
.Case("format", SourceKitRequest::Format)

tools/SourceKit/tools/sourcekitd-test/TestOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum class SourceKitRequest {
3535
CodeCompleteSetPopularAPI,
3636
TypeContextInfo,
3737
ConformingMethodList,
38+
ActiveRegions,
3839
CursorInfo,
3940
RangeInfo,
4041
RelatedIdents,

tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ static void printSemanticInfo();
7676
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
7777
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
7878
llvm::raw_ostream &OS);
79+
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
80+
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
81+
llvm::raw_ostream &OS);
7982
static void printFoundInterface(sourcekitd_variant_t Info,
8083
llvm::raw_ostream &OS);
8184
static void printFoundUSR(sourcekitd_variant_t Info,
@@ -879,6 +882,10 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
879882
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestRelatedIdents);
880883
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
881884
break;
885+
886+
case SourceKitRequest::ActiveRegions:
887+
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestActiveRegions);
888+
break;
882889

883890
case SourceKitRequest::SyntaxMap:
884891
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
@@ -1339,6 +1346,10 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
13391346
printRelatedIdents(Info, SourceFile, Opts.VFSFiles, llvm::outs());
13401347
break;
13411348

1349+
case SourceKitRequest::ActiveRegions:
1350+
printActiveRegions(Info, SourceFile, Opts.VFSFiles, llvm::outs());
1351+
break;
1352+
13421353
case SourceKitRequest::CursorInfo:
13431354
printCursorInfo(Info, SourceFile, Opts.VFSFiles, llvm::outs());
13441355
break;
@@ -2336,6 +2347,22 @@ static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
23362347
OS << "END RANGES\n";
23372348
}
23382349

2350+
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
2351+
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
2352+
llvm::raw_ostream &OS) {
2353+
OS << "START IF CONFIGS\n";
2354+
sourcekitd_variant_t Res =
2355+
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
2356+
for (unsigned i=0, e = sourcekitd_variant_array_get_count(Res); i != e; ++i) {
2357+
sourcekitd_variant_t IfConfig = sourcekitd_variant_array_get_value(Res, i);
2358+
int64_t Offset = sourcekitd_variant_dictionary_get_int64(IfConfig, KeyOffset);
2359+
auto LineCol = resolveToLineCol(Offset, Filename, VFSFiles);
2360+
bool IsActive = sourcekitd_variant_dictionary_get_bool(IfConfig, KeyIsActive);
2361+
OS << LineCol.first << ':' << LineCol.second << " - " << (IsActive ? "active" : "inactive") << '\n';
2362+
}
2363+
OS << "END IF CONFIGS\n";
2364+
}
2365+
23392366
static void prepareDemangleRequest(sourcekitd_object_t Req,
23402367
const TestOptions &Opts) {
23412368
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDemangle);

tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ static void findRelatedIdents(StringRef Filename, int64_t Offset,
210210
SourceKitCancellationToken CancellationToken,
211211
ResponseReceiver Rec);
212212
213+
static void findActiveRegions(StringRef Filename, ArrayRef<const char *> Args,
214+
SourceKitCancellationToken CancellationToken,
215+
ResponseReceiver Rec);
216+
213217
static sourcekitd_response_t
214218
codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset,
215219
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
@@ -1682,6 +1686,25 @@ handleRequestRelatedIdents(const RequestDict &Req,
16821686
});
16831687
}
16841688
1689+
static void
1690+
handleRequestActiveRegions(const RequestDict &Req,
1691+
SourceKitCancellationToken CancellationToken,
1692+
ResponseReceiver Rec) {
1693+
if (checkVFSNotSupported(Req, Rec))
1694+
return;
1695+
1696+
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
1697+
auto SourceFile = getSourceFileNameForRequestOrEmitError(Req, Rec);
1698+
if (!SourceFile)
1699+
return;
1700+
SmallVector<const char *> Args;
1701+
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
1702+
return;
1703+
1704+
return findActiveRegions(*SourceFile, Args, CancellationToken, Rec);
1705+
});
1706+
}
1707+
16851708
static void
16861709
handleRequestDiagnostics(const RequestDict &Req,
16871710
SourceKitCancellationToken CancellationToken,
@@ -1805,6 +1828,7 @@ void handleRequestImpl(sourcekitd_object_t ReqObj,
18051828
handleRequestFindLocalRenameRanges)
18061829
HANDLE_REQUEST(RequestNameTranslation, handleRequestNameTranslation)
18071830
HANDLE_REQUEST(RequestRelatedIdents, handleRequestRelatedIdents)
1831+
HANDLE_REQUEST(RequestActiveRegions, handleRequestActiveRegions)
18081832
HANDLE_REQUEST(RequestDiagnostics, handleRequestDiagnostics)
18091833

18101834
{
@@ -2576,6 +2600,38 @@ static void findRelatedIdents(StringRef Filename, int64_t Offset,
25762600
});
25772601
}
25782602

2603+
//===----------------------------------------------------------------------===//
2604+
// FindActiveRegions
2605+
//===----------------------------------------------------------------------===//
2606+
2607+
static void findActiveRegions(StringRef Filename, ArrayRef<const char *> Args,
2608+
SourceKitCancellationToken CancellationToken,
2609+
ResponseReceiver Rec) {
2610+
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
2611+
2612+
Lang.findActiveRegionsInFile(
2613+
Filename, Args, CancellationToken,
2614+
[Rec](const RequestResult<ActiveRegionsInfo> &Result) {
2615+
if (Result.isCancelled())
2616+
return Rec(createErrorRequestCancelled());
2617+
if (Result.isError())
2618+
return Rec(createErrorRequestFailed(Result.getError()));
2619+
2620+
const ActiveRegionsInfo &Info = Result.value();
2621+
2622+
ResponseBuilder RespBuilder;
2623+
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
2624+
for (auto Config : Info.Configs) {
2625+
auto Elem = Arr.appendDictionary();
2626+
Elem.set(KeyOffset, Config.Offset);
2627+
if (Config.IsActive)
2628+
Elem.setBool(KeyIsActive, true);
2629+
}
2630+
2631+
Rec(RespBuilder.createResponse());
2632+
});
2633+
}
2634+
25792635
//===----------------------------------------------------------------------===//
25802636
// CodeComplete
25812637
//===----------------------------------------------------------------------===//

utils/gyb_sourcekit_support/UIDs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def __init__(self, internal_name, external_name):
8383
KEY('BodyLength', 'key.bodylength'),
8484
KEY('DocOffset', 'key.docoffset'),
8585
KEY('DocLength', 'key.doclength'),
86+
KEY('IsActive', 'key.is_active'),
8687
KEY('IsLocal', 'key.is_local'),
8788
KEY('InheritedTypes', 'key.inheritedtypes'),
8889
KEY('Attributes', 'key.attributes'),
@@ -224,6 +225,7 @@ def __init__(self, internal_name, external_name):
224225
REQUEST('CodeCompleteSetCustom', 'source.request.codecomplete.setcustom'),
225226
REQUEST('TypeContextInfo', 'source.request.typecontextinfo'),
226227
REQUEST('ConformingMethodList', 'source.request.conformingmethods'),
228+
REQUEST('ActiveRegions', 'source.request.activeregions'),
227229
REQUEST('CursorInfo', 'source.request.cursorinfo'),
228230
REQUEST('RangeInfo', 'source.request.rangeinfo'),
229231
REQUEST('RelatedIdents', 'source.request.relatedidents'),

0 commit comments

Comments
 (0)