mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #63816 from apple/louisd/sourcekit-inactive-ranges-request
[SourceKit] Add "Active Regions" request
This commit is contained in:
47
test/SourceKit/ActiveRegions/nested.swift
Normal file
47
test/SourceKit/ActiveRegions/nested.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
#if FLAG_1
|
||||
class Foo {
|
||||
#if FLAG_2
|
||||
func debugOnly() {
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#elseif FLAG_3
|
||||
class GracefulFallback {
|
||||
|
||||
}
|
||||
#else
|
||||
class Fallback {
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
|
||||
// CHECK1: START IF CONFIGS
|
||||
// CHECK1-NEXT: 1:1 - active
|
||||
// CHECK1-NEXT: 3:5 - inactive
|
||||
// CHECK1-NEXT: 9:1 - inactive
|
||||
// CHECK1-NEXT: 13:1 - inactive
|
||||
// CHECK1-NEXT: END IF CONFIGS
|
||||
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_2 -module-name active_regions %s | %FileCheck -check-prefix=CHECK2 %s
|
||||
// CHECK2: START IF CONFIGS
|
||||
// CHECK2-NEXT: 1:1 - inactive
|
||||
// CHECK2-NEXT: 9:1 - inactive
|
||||
// CHECK2-NEXT: 13:1 - active
|
||||
// CHECK2-NEXT: END IF CONFIGS
|
||||
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -D FLAG_2 -module-name active_regions %s | %FileCheck -check-prefix=CHECK3 %s
|
||||
// CHECK3: START IF CONFIGS
|
||||
// CHECK3-NEXT: 1:1 - active
|
||||
// CHECK3-NEXT: 3:5 - active
|
||||
// CHECK3-NEXT: 9:1 - inactive
|
||||
// CHECK3-NEXT: 13:1 - inactive
|
||||
// CHECK3-NEXT: END IF CONFIGS
|
||||
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_3 -module-name active_regions %s | %FileCheck -check-prefix=CHECK4 %s
|
||||
// CHECK4: START IF CONFIGS
|
||||
// CHECK4-NEXT: 1:1 - inactive
|
||||
// CHECK4-NEXT: 9:1 - active
|
||||
// CHECK4-NEXT: 13:1 - inactive
|
||||
// CHECK4-NEXT: END IF CONFIGS
|
||||
12
test/SourceKit/ActiveRegions/syntactically_invalid.swift
Normal file
12
test/SourceKit/ActiveRegions/syntactically_invalid.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
#if FOO > 10
|
||||
class Foo {
|
||||
}
|
||||
#else
|
||||
class Fallback {
|
||||
}
|
||||
#endif
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
|
||||
// CHECK1: START IF CONFIGS
|
||||
// CHECK1-NEXT: 1:1 - inactive
|
||||
// CHECK1-NEXT: 4:1 - active
|
||||
// CHECK1-NEXT: END IF CONFIGS
|
||||
12
test/SourceKit/ActiveRegions/unterminated.swift
Normal file
12
test/SourceKit/ActiveRegions/unterminated.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
#if FLAG_1
|
||||
class Foo {
|
||||
}
|
||||
#else
|
||||
class Fallback {
|
||||
}
|
||||
|
||||
// RUN: %sourcekitd-test -req=active-regions %s -- -D FLAG_1 -module-name active_regions %s | %FileCheck -check-prefix=CHECK1 %s
|
||||
// CHECK1: START IF CONFIGS
|
||||
// CHECK1-NEXT: 1:1 - active
|
||||
// CHECK1-NEXT: 4:1 - inactive
|
||||
// CHECK1-NEXT: END IF CONFIGS
|
||||
@@ -648,6 +648,20 @@ struct RelatedIdentsInfo {
|
||||
ArrayRef<std::pair<unsigned, unsigned>> Ranges;
|
||||
};
|
||||
|
||||
/// Represent one branch of an if config.
|
||||
/// Either `#if`, `#else` or `#elseif`.
|
||||
struct IfConfigInfo {
|
||||
unsigned Offset;
|
||||
bool IsActive;
|
||||
|
||||
IfConfigInfo(unsigned Offset, bool IsActive)
|
||||
: Offset(Offset), IsActive(IsActive) {}
|
||||
};
|
||||
|
||||
struct ActiveRegionsInfo {
|
||||
ArrayRef<IfConfigInfo> Configs;
|
||||
};
|
||||
|
||||
/// Filled out by LangSupport::findInterfaceDocument().
|
||||
struct InterfaceDocInfo {
|
||||
/// Non-empty if a generated interface editor document has previously been
|
||||
@@ -999,6 +1013,12 @@ public:
|
||||
std::function<void(const RequestResult<RelatedIdentsInfo> &)>
|
||||
Receiver) = 0;
|
||||
|
||||
virtual void findActiveRegionsInFile(
|
||||
StringRef Filename, ArrayRef<const char *> Args,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
std::function<void(const RequestResult<ActiveRegionsInfo> &)>
|
||||
Receiver) = 0;
|
||||
|
||||
virtual llvm::Optional<std::pair<unsigned, unsigned>>
|
||||
findUSRRange(StringRef DocumentName, StringRef USR) = 0;
|
||||
|
||||
|
||||
@@ -673,6 +673,12 @@ public:
|
||||
std::function<void(const RequestResult<RelatedIdentsInfo> &)> Receiver)
|
||||
override;
|
||||
|
||||
void findActiveRegionsInFile(
|
||||
StringRef Filename, ArrayRef<const char *> Args,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver)
|
||||
override;
|
||||
|
||||
void syntacticRename(llvm::MemoryBuffer *InputBuf,
|
||||
ArrayRef<RenameLocations> RenameLocations,
|
||||
ArrayRef<const char*> Args,
|
||||
|
||||
@@ -2566,6 +2566,101 @@ void SwiftLangSupport::findRelatedIdentifiersInFile(
|
||||
llvm::vfs::getRealFileSystem());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SwiftLangSupport::findActiveRegionsInFile
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
class IfConfigScanner : public SourceEntityWalker {
|
||||
SmallVectorImpl<IfConfigInfo> &Infos;
|
||||
SourceManager &SourceMgr;
|
||||
unsigned BufferID = -1;
|
||||
bool Cancelled = false;
|
||||
|
||||
public:
|
||||
explicit IfConfigScanner(SourceFile &SrcFile, unsigned BufferID,
|
||||
SmallVectorImpl<IfConfigInfo> &Infos)
|
||||
: Infos(Infos), SourceMgr(SrcFile.getASTContext().SourceMgr),
|
||||
BufferID(BufferID) {}
|
||||
|
||||
private:
|
||||
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
|
||||
if (Cancelled)
|
||||
return false;
|
||||
|
||||
if (auto *IfDecl = dyn_cast<IfConfigDecl>(D)) {
|
||||
for (auto &Clause : IfDecl->getClauses()) {
|
||||
unsigned Offset = SourceMgr.getLocOffsetInBuffer(Clause.Loc, BufferID);
|
||||
Infos.emplace_back(Offset, Clause.isActive);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
void SwiftLangSupport::findActiveRegionsInFile(
|
||||
StringRef InputFile, ArrayRef<const char *> Args,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver) {
|
||||
|
||||
std::string Error;
|
||||
SwiftInvocationRef Invok =
|
||||
ASTMgr->getTypecheckInvocation(Args, InputFile, Error);
|
||||
if (!Invok) {
|
||||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error);
|
||||
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
|
||||
return;
|
||||
}
|
||||
|
||||
class IfConfigConsumer : public SwiftASTConsumer {
|
||||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver;
|
||||
SwiftInvocationRef Invok;
|
||||
|
||||
public:
|
||||
IfConfigConsumer(
|
||||
std::function<void(const RequestResult<ActiveRegionsInfo> &)> Receiver,
|
||||
SwiftInvocationRef Invok)
|
||||
: Receiver(std::move(Receiver)), Invok(Invok) {}
|
||||
|
||||
void handlePrimaryAST(ASTUnitRef AstUnit) override {
|
||||
auto &SrcFile = AstUnit->getPrimarySourceFile();
|
||||
SmallVector<IfConfigInfo> Configs;
|
||||
auto BufferID = SrcFile.getBufferID();
|
||||
if (!BufferID)
|
||||
return;
|
||||
IfConfigScanner Scanner(SrcFile, *BufferID, Configs);
|
||||
Scanner.walk(SrcFile);
|
||||
|
||||
// Sort by offset so nested decls are reported
|
||||
// in source order (not tree order).
|
||||
llvm::sort(Configs,
|
||||
[](const IfConfigInfo &LHS, const IfConfigInfo &RHS) -> bool {
|
||||
return LHS.Offset < RHS.Offset;
|
||||
});
|
||||
ActiveRegionsInfo Info;
|
||||
Info.Configs = Configs;
|
||||
Receiver(RequestResult<ActiveRegionsInfo>::fromResult(Info));
|
||||
}
|
||||
|
||||
void cancelled() override {
|
||||
Receiver(RequestResult<ActiveRegionsInfo>::cancelled());
|
||||
}
|
||||
|
||||
void failed(StringRef Error) override {
|
||||
LOG_WARN_FUNC("inactive ranges failed: " << Error);
|
||||
Receiver(RequestResult<ActiveRegionsInfo>::fromError(Error));
|
||||
}
|
||||
};
|
||||
|
||||
auto Consumer = std::make_shared<IfConfigConsumer>(Receiver, Invok);
|
||||
ASTMgr->processASTAsync(Invok, std::move(Consumer),
|
||||
/*OncePerASTToken=*/nullptr, CancellationToken,
|
||||
llvm::vfs::getRealFileSystem());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SwiftLangSupport::semanticRefactoring
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -120,6 +120,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
|
||||
.Case("conformingmethods", SourceKitRequest::ConformingMethodList)
|
||||
.Case("cursor", SourceKitRequest::CursorInfo)
|
||||
.Case("related-idents", SourceKitRequest::RelatedIdents)
|
||||
.Case("active-regions", SourceKitRequest::ActiveRegions)
|
||||
.Case("syntax-map", SourceKitRequest::SyntaxMap)
|
||||
.Case("structure", SourceKitRequest::Structure)
|
||||
.Case("format", SourceKitRequest::Format)
|
||||
|
||||
@@ -35,6 +35,7 @@ enum class SourceKitRequest {
|
||||
CodeCompleteSetPopularAPI,
|
||||
TypeContextInfo,
|
||||
ConformingMethodList,
|
||||
ActiveRegions,
|
||||
CursorInfo,
|
||||
RangeInfo,
|
||||
RelatedIdents,
|
||||
|
||||
@@ -76,6 +76,9 @@ static void printSemanticInfo();
|
||||
static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
|
||||
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
|
||||
llvm::raw_ostream &OS);
|
||||
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
|
||||
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
|
||||
llvm::raw_ostream &OS);
|
||||
static void printFoundInterface(sourcekitd_variant_t Info,
|
||||
llvm::raw_ostream &OS);
|
||||
static void printFoundUSR(sourcekitd_variant_t Info,
|
||||
@@ -880,6 +883,10 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
|
||||
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
|
||||
break;
|
||||
|
||||
case SourceKitRequest::ActiveRegions:
|
||||
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestActiveRegions);
|
||||
break;
|
||||
|
||||
case SourceKitRequest::SyntaxMap:
|
||||
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestEditorOpen);
|
||||
sourcekitd_request_dictionary_set_string(Req, KeyName, SemaName.c_str());
|
||||
@@ -1339,6 +1346,10 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
|
||||
printRelatedIdents(Info, SourceFile, Opts.VFSFiles, llvm::outs());
|
||||
break;
|
||||
|
||||
case SourceKitRequest::ActiveRegions:
|
||||
printActiveRegions(Info, SourceFile, Opts.VFSFiles, llvm::outs());
|
||||
break;
|
||||
|
||||
case SourceKitRequest::CursorInfo:
|
||||
printCursorInfo(Info, SourceFile, Opts.VFSFiles, llvm::outs());
|
||||
break;
|
||||
@@ -2352,6 +2363,22 @@ static void printRelatedIdents(sourcekitd_variant_t Info, StringRef Filename,
|
||||
OS << "END RANGES\n";
|
||||
}
|
||||
|
||||
static void printActiveRegions(sourcekitd_variant_t Info, StringRef Filename,
|
||||
const llvm::StringMap<TestOptions::VFSFile> &VFSFiles,
|
||||
llvm::raw_ostream &OS) {
|
||||
OS << "START IF CONFIGS\n";
|
||||
sourcekitd_variant_t Res =
|
||||
sourcekitd_variant_dictionary_get_value(Info, KeyResults);
|
||||
for (unsigned i=0, e = sourcekitd_variant_array_get_count(Res); i != e; ++i) {
|
||||
sourcekitd_variant_t IfConfig = sourcekitd_variant_array_get_value(Res, i);
|
||||
int64_t Offset = sourcekitd_variant_dictionary_get_int64(IfConfig, KeyOffset);
|
||||
auto LineCol = resolveToLineCol(Offset, Filename, VFSFiles);
|
||||
bool IsActive = sourcekitd_variant_dictionary_get_bool(IfConfig, KeyIsActive);
|
||||
OS << LineCol.first << ':' << LineCol.second << " - " << (IsActive ? "active" : "inactive") << '\n';
|
||||
}
|
||||
OS << "END IF CONFIGS\n";
|
||||
}
|
||||
|
||||
static void prepareDemangleRequest(sourcekitd_object_t Req,
|
||||
const TestOptions &Opts) {
|
||||
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestDemangle);
|
||||
|
||||
@@ -210,6 +210,10 @@ static void findRelatedIdents(StringRef Filename, int64_t Offset,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
ResponseReceiver Rec);
|
||||
|
||||
static void findActiveRegions(StringRef Filename, ArrayRef<const char *> Args,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
ResponseReceiver Rec);
|
||||
|
||||
static sourcekitd_response_t
|
||||
codeComplete(llvm::MemoryBuffer *InputBuf, int64_t Offset,
|
||||
Optional<RequestDict> optionsDict, ArrayRef<const char *> Args,
|
||||
@@ -1682,6 +1686,25 @@ handleRequestRelatedIdents(const RequestDict &Req,
|
||||
});
|
||||
}
|
||||
|
||||
static void
|
||||
handleRequestActiveRegions(const RequestDict &Req,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
ResponseReceiver Rec) {
|
||||
if (checkVFSNotSupported(Req, Rec))
|
||||
return;
|
||||
|
||||
handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() {
|
||||
auto SourceFile = getSourceFileNameForRequestOrEmitError(Req, Rec);
|
||||
if (!SourceFile)
|
||||
return;
|
||||
SmallVector<const char *> Args;
|
||||
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
|
||||
return;
|
||||
|
||||
return findActiveRegions(*SourceFile, Args, CancellationToken, Rec);
|
||||
});
|
||||
}
|
||||
|
||||
static void
|
||||
handleRequestDiagnostics(const RequestDict &Req,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
@@ -1805,6 +1828,7 @@ void handleRequestImpl(sourcekitd_object_t ReqObj,
|
||||
handleRequestFindLocalRenameRanges)
|
||||
HANDLE_REQUEST(RequestNameTranslation, handleRequestNameTranslation)
|
||||
HANDLE_REQUEST(RequestRelatedIdents, handleRequestRelatedIdents)
|
||||
HANDLE_REQUEST(RequestActiveRegions, handleRequestActiveRegions)
|
||||
HANDLE_REQUEST(RequestDiagnostics, handleRequestDiagnostics)
|
||||
|
||||
{
|
||||
@@ -2576,6 +2600,38 @@ static void findRelatedIdents(StringRef Filename, int64_t Offset,
|
||||
});
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FindActiveRegions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static void findActiveRegions(StringRef Filename, ArrayRef<const char *> Args,
|
||||
SourceKitCancellationToken CancellationToken,
|
||||
ResponseReceiver Rec) {
|
||||
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
|
||||
|
||||
Lang.findActiveRegionsInFile(
|
||||
Filename, Args, CancellationToken,
|
||||
[Rec](const RequestResult<ActiveRegionsInfo> &Result) {
|
||||
if (Result.isCancelled())
|
||||
return Rec(createErrorRequestCancelled());
|
||||
if (Result.isError())
|
||||
return Rec(createErrorRequestFailed(Result.getError()));
|
||||
|
||||
const ActiveRegionsInfo &Info = Result.value();
|
||||
|
||||
ResponseBuilder RespBuilder;
|
||||
auto Arr = RespBuilder.getDictionary().setArray(KeyResults);
|
||||
for (auto Config : Info.Configs) {
|
||||
auto Elem = Arr.appendDictionary();
|
||||
Elem.set(KeyOffset, Config.Offset);
|
||||
if (Config.IsActive)
|
||||
Elem.setBool(KeyIsActive, true);
|
||||
}
|
||||
|
||||
Rec(RespBuilder.createResponse());
|
||||
});
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeComplete
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -83,6 +83,7 @@ UID_KEYS = [
|
||||
KEY('BodyLength', 'key.bodylength'),
|
||||
KEY('DocOffset', 'key.docoffset'),
|
||||
KEY('DocLength', 'key.doclength'),
|
||||
KEY('IsActive', 'key.is_active'),
|
||||
KEY('IsLocal', 'key.is_local'),
|
||||
KEY('InheritedTypes', 'key.inheritedtypes'),
|
||||
KEY('Attributes', 'key.attributes'),
|
||||
@@ -225,6 +226,7 @@ UID_REQUESTS = [
|
||||
REQUEST('CodeCompleteSetCustom', 'source.request.codecomplete.setcustom'),
|
||||
REQUEST('TypeContextInfo', 'source.request.typecontextinfo'),
|
||||
REQUEST('ConformingMethodList', 'source.request.conformingmethods'),
|
||||
REQUEST('ActiveRegions', 'source.request.activeregions'),
|
||||
REQUEST('CursorInfo', 'source.request.cursorinfo'),
|
||||
REQUEST('RangeInfo', 'source.request.rangeinfo'),
|
||||
REQUEST('RelatedIdents', 'source.request.relatedidents'),
|
||||
|
||||
Reference in New Issue
Block a user