blob: 872e083f0cace794a7a3b51efd16fccb0fa14a92 [file] [log] [blame]
//===--- complete-test.cpp ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "sourcekitd/sourcekitd.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/FileSystem.h"
#include <fstream>
#include <regex>
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/param.h>
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#endif
// FIXME: Platform compatibility.
#include <dispatch/dispatch.h>
using namespace llvm;
#if defined(_WIN32)
namespace {
int STDOUT_FILENO = _fileno(stdout);
}
#endif
namespace {
struct TestOptions {
StringRef sourceFile;
StringRef completionToken;
StringRef popularAPI;
StringRef unpopularAPI;
Optional<bool> sortByName;
Optional<bool> useImportDepth;
Optional<bool> groupOverloads;
Optional<bool> groupStems;
Optional<bool> includeExactMatch;
Optional<bool> addInnerResults;
Optional<bool> addInnerOperators;
Optional<bool> addInitsToTopLevel;
Optional<unsigned> requestStart;
Optional<unsigned> requestLimit;
Optional<unsigned> hideUnderscores;
Optional<bool> hideByName;
Optional<bool> hideLowPriority;
Optional<unsigned> showTopNonLiteral;
Optional<bool> fuzzyMatching;
Optional<unsigned> fuzzyWeight;
Optional<unsigned> popularityBonus;
StringRef filterRulesJSON;
bool rawOutput = false;
bool structureOutput = false;
ArrayRef<const char *> compilerArgs;
};
} // end anonymous namespace
static int handleTestInvocation(TestOptions &options);
static sourcekitd_uid_t KeyRequest;
static sourcekitd_uid_t KeyCompilerArgs;
static sourcekitd_uid_t KeyOffset;
static sourcekitd_uid_t KeyLength;
static sourcekitd_uid_t KeySourceFile;
static sourcekitd_uid_t KeySourceText;
static sourcekitd_uid_t KeyName;
static sourcekitd_uid_t KeyNameOffset;
static sourcekitd_uid_t KeyNameLength;
static sourcekitd_uid_t KeyBodyOffset;
static sourcekitd_uid_t KeyBodyLength;
static sourcekitd_uid_t KeyThrowOffset;
static sourcekitd_uid_t KeyThrowLength;
static sourcekitd_uid_t KeyIsLocal;
static sourcekitd_uid_t KeyDescription;
static sourcekitd_uid_t KeyCodeCompleteOptions;
static sourcekitd_uid_t KeySortByName;
static sourcekitd_uid_t KeyUseImportDepth;
static sourcekitd_uid_t KeyGroupOverloads;
static sourcekitd_uid_t KeyGroupStems;
static sourcekitd_uid_t KeyFilterText;
static sourcekitd_uid_t KeyFilterRules;
static sourcekitd_uid_t KeyRequestStart;
static sourcekitd_uid_t KeyRequestLimit;
static sourcekitd_uid_t KeyHideUnderscores;
static sourcekitd_uid_t KeyHideLowPriority;
static sourcekitd_uid_t KeyHideByName;
static sourcekitd_uid_t KeyIncludeExactMatch;
static sourcekitd_uid_t KeyAddInnerResults;
static sourcekitd_uid_t KeyAddInnerOperators;
static sourcekitd_uid_t KeyAddInitsToTopLevel;
static sourcekitd_uid_t KeyFuzzyMatching;
static sourcekitd_uid_t KeyFuzzyWeight;
static sourcekitd_uid_t KeyPopularityBonus;
static sourcekitd_uid_t KeyTopNonLiteral;
static sourcekitd_uid_t KeyKind;
static sourcekitd_uid_t KeyResults;
static sourcekitd_uid_t KeyPopular;
static sourcekitd_uid_t KeyUnpopular;
static sourcekitd_uid_t KeySubStructure;
// Returns false and sets 'error' on failure.
static bool parseOptions(ArrayRef<const char *> args, TestOptions &options,
std::string &error) {
for (unsigned i = 0; i < args.size(); ++i) {
StringRef opt, value;
std::tie(opt, value) = StringRef(args[i]).split('=');
if (opt == "--") {
options.compilerArgs = args.slice(i + 1);
break;
}
if (opt == "-" || !opt.startswith("-")) {
options.sourceFile = args[i];
continue;
}
if (opt.startswith("--")) {
error = std::string("unrecognized option '") + args[i] + "'";
return false;
}
opt = opt.ltrim("-");
if (opt == "tok") {
options.completionToken = value;
} else if (opt == "sort") {
if (value == "context") {
options.sortByName = false;
} else if (value == "name") {
options.sortByName = true;
} else {
error = "unrecognized argument for -sort=";
return false;
}
} else if (opt == "add-inits-to-top-level") {
options.addInitsToTopLevel = true;
} else if (opt == "include-exact-match") {
options.includeExactMatch = true;
} else if (opt == "no-include-exact-match") {
options.includeExactMatch = false;
} else if (opt == "add-inner-results") {
options.addInnerResults = true;
} else if (opt == "no-inner-results") {
options.addInnerResults = false;
} else if (opt == "inner-operators") {
options.addInnerOperators = true;
} else if (opt == "no-inner-operators") {
options.addInnerOperators = false;
} else if (opt == "depth") {
options.useImportDepth = true;
} else if (opt == "no-depth") {
options.useImportDepth = false;
} else if (opt == "fuzz") {
options.fuzzyMatching = true;
} else if (opt == "no-fuzz") {
options.fuzzyMatching = false;
} else if (opt == "fuzzy-weight") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -fuzzy-weight=";
return false;
}
options.fuzzyWeight = uval;
} else if (opt == "popularity-bonus") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -popularity-bonus=";
return false;
}
options.popularityBonus = uval;
} else if (opt == "group") {
if (value == "overloads") {
options.groupOverloads = true;
if (!options.groupStems)
options.groupStems = false;
} else if (value == "stems") {
options.groupStems = true;
} else if (value == "none") {
options.groupStems = false;
options.groupOverloads = false;
} else {
error = "unrecognized argument for -group=";
return false;
}
} else if (opt == "start") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -start=";
return false;
}
options.requestStart = uval;
} else if (opt == "limit") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -limit=";
return false;
}
options.requestLimit = uval;
} else if (opt == "raw") {
options.rawOutput = true;
} else if (opt == "structure") {
options.structureOutput = true;
} else if (opt == "hide-underscores") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -hide-underscores=";
return false;
}
options.hideUnderscores = uval;
} else if (opt == "hide-low-priority") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -hide-low-priority=";
return false;
}
options.hideLowPriority = uval;
} else if (opt == "hide-by-name") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -hide-by-name=";
return false;
}
options.hideByName = uval;
} else if (opt == "hide-none") {
options.hideUnderscores = 0;
options.hideLowPriority = false;
options.hideByName = false;
} else if (opt == "popular") {
options.popularAPI = value;
} else if (opt == "unpopular") {
options.unpopularAPI = value;
} else if (opt == "filter-rules") {
options.filterRulesJSON = value;
} else if (opt == "top") {
unsigned uval;
if (value.getAsInteger(10, uval)) {
error = "unrecognized integer value for -tope=";
return false;
}
options.showTopNonLiteral = uval;
}
}
if (options.sourceFile.empty()) {
error = "missing <source-file>";
return false;
}
if (options.completionToken.empty()) {
error = "missing -tok=<completion-token>";
return false;
}
return true;
}
static int skt_main(int argc, const char **argv);
int main(int argc, const char **argv) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
int ret = skt_main(argc, argv);
exit(ret);
});
dispatch_main();
}
static void notification_receiver(sourcekitd_response_t resp) {
if (sourcekitd_response_is_error(resp)) {
sourcekitd_response_description_dump(resp);
exit(1);
}
}
static int skt_main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
sourcekitd_initialize();
sourcekitd_set_notification_handler(^(sourcekitd_response_t resp) {
notification_receiver(resp);
});
KeyRequest = sourcekitd_uid_get_from_cstr("key.request");
KeyCompilerArgs = sourcekitd_uid_get_from_cstr("key.compilerargs");
KeyOffset = sourcekitd_uid_get_from_cstr("key.offset");
KeyLength = sourcekitd_uid_get_from_cstr("key.length");
KeyKind = sourcekitd_uid_get_from_cstr("key.kind");
KeyCodeCompleteOptions =
sourcekitd_uid_get_from_cstr("key.codecomplete.options");
KeySortByName = sourcekitd_uid_get_from_cstr("key.codecomplete.sort.byname");
KeyUseImportDepth =
sourcekitd_uid_get_from_cstr("key.codecomplete.sort.useimportdepth");
KeyGroupOverloads =
sourcekitd_uid_get_from_cstr("key.codecomplete.group.overloads");
KeyGroupStems = sourcekitd_uid_get_from_cstr("key.codecomplete.group.stems");
KeyFilterText = sourcekitd_uid_get_from_cstr("key.codecomplete.filtertext");
KeyFilterRules = sourcekitd_uid_get_from_cstr("key.codecomplete.filterrules");
KeyRequestLimit =
sourcekitd_uid_get_from_cstr("key.codecomplete.requestlimit");
KeyRequestStart =
sourcekitd_uid_get_from_cstr("key.codecomplete.requeststart");
KeyHideUnderscores =
sourcekitd_uid_get_from_cstr("key.codecomplete.hideunderscores");
KeyHideLowPriority =
sourcekitd_uid_get_from_cstr("key.codecomplete.hidelowpriority");
KeyHideByName = sourcekitd_uid_get_from_cstr("key.codecomplete.hidebyname");
KeyIncludeExactMatch =
sourcekitd_uid_get_from_cstr("key.codecomplete.includeexactmatch");
KeyAddInnerResults =
sourcekitd_uid_get_from_cstr("key.codecomplete.addinnerresults");
KeyAddInnerOperators =
sourcekitd_uid_get_from_cstr("key.codecomplete.addinneroperators");
KeyAddInitsToTopLevel =
sourcekitd_uid_get_from_cstr("key.codecomplete.addinitstotoplevel");
KeyFuzzyMatching =
sourcekitd_uid_get_from_cstr("key.codecomplete.fuzzymatching");
KeyFuzzyWeight =
sourcekitd_uid_get_from_cstr("key.codecomplete.sort.fuzzyweight");
KeyPopularityBonus =
sourcekitd_uid_get_from_cstr("key.codecomplete.sort.popularitybonus");
KeyTopNonLiteral =
sourcekitd_uid_get_from_cstr("key.codecomplete.showtopnonliteralresults");
KeySourceFile = sourcekitd_uid_get_from_cstr("key.sourcefile");
KeySourceText = sourcekitd_uid_get_from_cstr("key.sourcetext");
KeyName = sourcekitd_uid_get_from_cstr("key.name");
KeyNameOffset = sourcekitd_uid_get_from_cstr("key.nameoffset");
KeyNameLength = sourcekitd_uid_get_from_cstr("key.namelength");
KeyBodyOffset = sourcekitd_uid_get_from_cstr("key.bodyoffset");
KeyBodyLength = sourcekitd_uid_get_from_cstr("key.bodylength");
KeyThrowOffset = sourcekitd_uid_get_from_cstr("key.throwoffset");
KeyThrowLength = sourcekitd_uid_get_from_cstr("key.throwlength");
KeyIsLocal = sourcekitd_uid_get_from_cstr("key.is_local");
KeyDescription = sourcekitd_uid_get_from_cstr("key.description");
KeyResults = sourcekitd_uid_get_from_cstr("key.results");
KeyPopular = sourcekitd_uid_get_from_cstr("key.popular");
KeyUnpopular = sourcekitd_uid_get_from_cstr("key.unpopular");
KeySubStructure = sourcekitd_uid_get_from_cstr("key.substructure");
auto Args = llvm::makeArrayRef(argv + 1, argc - 1);
TestOptions options;
std::string error;
if (!parseOptions(Args, options, error)) {
llvm::errs() << "usage: complete-test -tok=A file\n" << error << "\n";
return 1;
}
int ret = handleTestInvocation(options);
sourcekitd_shutdown();
return ret;
}
static std::string
removeCodeCompletionTokens(StringRef Input, StringRef TokenName,
SmallVectorImpl<std::string> &prefixes,
unsigned *CompletionOffset) {
assert(TokenName.size() >= 1);
*CompletionOffset = ~0U;
std::string CleanFile;
CleanFile.reserve(Input.size());
std::regex tokenRegex(R"(#\^([^^,]+)(,[^^]*)?\^#)");
auto pos = Input.begin();
while (pos != Input.end()) {
std::match_results<StringRef::iterator> match;
std::regex_search(pos, Input.end(), match, tokenRegex);
if (match.empty()) {
CleanFile.append(pos, Input.end()); // remaining text
pos = Input.end();
break;
}
// Update the buffer.
CleanFile.append(pos, match.prefix().second);
pos = match.suffix().first;
// Check the token.
assert(match.size() == 2 || match.size() == 3);
if (match[1].str() != TokenName)
continue;
*CompletionOffset = CleanFile.size();
CleanFile.push_back('\0');
if (match.size() == 2 || !match[2].matched)
continue;
std::string fullMatch = match[2].str();
assert(fullMatch[0] == ',');
StringRef next = StringRef(fullMatch).split(',').second;
while (next != "") {
auto split = next.split(',');
prefixes.push_back(split.first);
next = split.second;
}
}
return CleanFile;
}
namespace {
class ResponsePrinter {
llvm::raw_ostream &OS;
unsigned indentWidth;
unsigned currentIndentation;
bool structuredOutput = false;
public:
ResponsePrinter(llvm::raw_ostream &OS, unsigned indentWidth,
unsigned startingIndent, bool structure)
: OS(OS), indentWidth(indentWidth), currentIndentation(startingIndent),
structuredOutput(structure) {}
void printResponse(sourcekitd_response_t resp) {
auto dict = sourcekitd_response_get_value(resp);
printGroup(dict);
}
void printArray(sourcekitd_variant_t array) {
sourcekitd_variant_array_apply(
array, ^bool(size_t index, sourcekitd_variant_t value) {
printGroupOrCompletion(value);
return true;
});
}
void printGroupOrCompletion(sourcekitd_variant_t value) {
static sourcekitd_uid_t GroupUID =
sourcekitd_uid_get_from_cstr("source.lang.swift.codecomplete.group");
if (GroupUID == sourcekitd_variant_dictionary_get_uid(value, KeyKind)) {
printGroup(value);
} else {
printCompletion(value);
}
}
void printCompletion(sourcekitd_variant_t completion) {
assert(sourcekitd_variant_get_type(completion) ==
SOURCEKITD_VARIANT_TYPE_DICTIONARY);
// FIXME: kind, semantic context?
StringRef desc = dictGetString(completion, KeyDescription);
if (!structuredOutput) {
indent() << desc << "\n";
return;
}
auto structure =
sourcekitd_variant_dictionary_get_value(completion, KeySubStructure);
if (sourcekitd_variant_get_type(structure) ==
SOURCEKITD_VARIANT_TYPE_NULL) {
indent() << "(no structure)\n";
return;
}
unsigned index = 0;
auto printUntil =
[desc, &index, this](unsigned end) -> llvm::raw_ostream & {
for (; index != end; ++index)
OS.write(desc[index]);
return OS;
};
auto getUInt = [](sourcekitd_variant_t dict, sourcekitd_uid_t key) {
auto value = sourcekitd_variant_dictionary_get_int64(dict, key);
assert(0 <= value && value <= UINT_MAX);
return value;
};
auto baseStart = getUInt(structure, KeyNameOffset);
auto baseLen = getUInt(structure, KeyNameLength);
// {name:basename}([params:{p:{t:Int}}, {p:label:{t: String}}])
indent();
if (baseLen) {
printUntil(baseStart) << "{name:";
printUntil(baseStart + baseLen) << "}";
}
auto paramStart = getUInt(structure, KeyBodyOffset);
auto paramLen = getUInt(structure, KeyBodyLength);
auto params =
sourcekitd_variant_dictionary_get_value(structure, KeySubStructure);
if (sourcekitd_variant_get_type(params) != SOURCEKITD_VARIANT_TYPE_NULL) {
assert(paramStart >= baseStart + baseLen);
printUntil(paramStart) << "{params:";
sourcekitd_variant_array_apply(
params, ^bool(size_t index, sourcekitd_variant_t param) {
auto start = getUInt(param, KeyNameOffset);
auto len = getUInt(param, KeyNameLength);
auto tStart = getUInt(param, KeyBodyOffset);
auto tLen = getUInt(param, KeyBodyLength);
bool isLocalName = getUInt(param, KeyIsLocal);
if (len) {
assert(start >= paramStart);
assert(start + len <= paramStart + paramLen);
printUntil(start) << "{" << (isLocalName ? "l" : "n") << ":";
printUntil(start + len) << "}";
}
if (tLen != 0) {
assert(tStart >= start);
assert(tStart + tLen <= paramStart + paramLen);
printUntil(tStart) << "{t:";
printUntil(tStart + tLen) << "}";
}
return true;
});
printUntil(paramStart + paramLen) << "}";
} else if (paramLen != 0) {
printUntil(paramStart) << "{params:";
printUntil(paramStart + paramLen) << "}";
}
auto throwStart = getUInt(structure, KeyThrowOffset);
auto throwLength = getUInt(structure, KeyThrowLength);
if (throwLength != 0) {
printUntil(throwStart) << "{throws:";
printUntil(throwStart + throwLength) << "}";
}
printUntil(desc.size()) << "\n";
}
void printGroup(sourcekitd_variant_t dict) {
struct RestoreInt {
unsigned old;
unsigned &value;
RestoreInt(unsigned &value) : old(value), value(value) {}
~RestoreInt() { value = old; }
} restoreIndent(currentIndentation);
StringRef name = dictGetString(dict, KeyName);
if (!name.empty()) {
indent() << name << ":\n";
currentIndentation += indentWidth;
}
auto results = sourcekitd_variant_dictionary_get_value(dict, KeyResults);
printArray(results);
}
StringRef dictGetString(sourcekitd_variant_t dict, sourcekitd_uid_t key) {
auto val = sourcekitd_variant_dictionary_get_value(dict, key);
assert(sourcekitd_variant_get_type(val) == SOURCEKITD_VARIANT_TYPE_STRING);
StringRef str(sourcekitd_variant_string_get_ptr(val),
sourcekitd_variant_string_get_length(val));
return str;
}
llvm::raw_ostream &indent() {
for (unsigned i = 0; i < currentIndentation; ++i)
OS.write(' ');
return OS;
}
};
} // end anonymous namespace
static void printResponse(sourcekitd_response_t resp, bool raw, bool structure,
unsigned indentation) {
if (raw) {
sourcekitd_response_description_dump_filedesc(resp, STDOUT_FILENO);
return;
}
ResponsePrinter p(llvm::outs(), 4, indentation, structure);
p.printResponse(resp);
llvm::outs().flush();
}
static std::unique_ptr<llvm::MemoryBuffer>
getBufferForFilename(StringRef name) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
llvm::MemoryBuffer::getFile(name);
if (!buffer) {
llvm::errs() << "error reading '" << name
<< "': " << buffer.getError().message() << "\n";
return nullptr;
}
return std::move(buffer.get());
}
static sourcekitd_object_t createBaseRequest(sourcekitd_uid_t requestUID,
const char *name,
unsigned offset) {
sourcekitd_object_t request =
sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_uid(request, KeyRequest, requestUID);
sourcekitd_request_dictionary_set_int64(request, KeyOffset, offset);
sourcekitd_request_dictionary_set_string(request, KeyName, name);
return request;
}
using HandlerFunc = std::function<bool(sourcekitd_response_t)>;
static bool sendRequestSync(sourcekitd_object_t request, HandlerFunc func) {
auto response = sourcekitd_send_request_sync(request);
bool result = func(response);
sourcekitd_response_dispose(response);
return result;
}
static bool codeCompleteRequest(sourcekitd_uid_t requestUID, const char *name,
unsigned offset, const char *sourceText,
const char *filterText, TestOptions &options,
HandlerFunc func) {
auto request = createBaseRequest(requestUID, name, offset);
sourcekitd_request_dictionary_set_string(request, KeySourceFile, name);
sourcekitd_request_dictionary_set_string(request, KeySourceText, sourceText);
auto opts = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
{
auto addBoolOption = [&](sourcekitd_uid_t key, Optional<bool> option) {
if (option)
sourcekitd_request_dictionary_set_int64(opts, key, *option);
};
addBoolOption(KeySortByName, options.sortByName);
addBoolOption(KeyUseImportDepth, options.useImportDepth);
addBoolOption(KeyGroupOverloads, options.groupOverloads);
addBoolOption(KeyGroupStems, options.groupStems);
addBoolOption(KeyIncludeExactMatch, options.includeExactMatch);
addBoolOption(KeyAddInnerResults, options.addInnerResults);
addBoolOption(KeyAddInnerOperators, options.addInnerOperators);
addBoolOption(KeyAddInitsToTopLevel, options.addInitsToTopLevel);
addBoolOption(KeyFuzzyMatching, options.fuzzyMatching);
addBoolOption(KeyHideLowPriority, options.hideLowPriority);
addBoolOption(KeyHideByName, options.hideByName);
auto addIntOption = [&](sourcekitd_uid_t key, Optional<unsigned> option) {
if (option)
sourcekitd_request_dictionary_set_int64(opts, key, *option);
};
addIntOption(KeyRequestStart, options.requestStart);
addIntOption(KeyRequestLimit, options.requestLimit);
addIntOption(KeyHideUnderscores, options.hideUnderscores);
addIntOption(KeyFuzzyWeight, options.fuzzyWeight);
addIntOption(KeyPopularityBonus, options.popularityBonus);
addIntOption(KeyTopNonLiteral, options.showTopNonLiteral);
if (filterText)
sourcekitd_request_dictionary_set_string(opts, KeyFilterText, filterText);
if (!options.filterRulesJSON.empty()) {
auto buffer = getBufferForFilename(options.filterRulesJSON);
if (!buffer)
return 1;
char *err = nullptr;
auto dict =
sourcekitd_request_create_from_yaml(buffer->getBuffer().data(), &err);
if (!dict) {
assert(err);
llvm::errs() << err;
free(err);
return 1;
}
sourcekitd_request_dictionary_set_value(opts, KeyFilterRules, dict);
}
}
sourcekitd_request_dictionary_set_value(request, KeyCodeCompleteOptions,opts);
sourcekitd_request_release(opts);
auto args = sourcekitd_request_array_create(nullptr, 0);
{
sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, name);
if (const char *sdk = getenv("SDKROOT")) {
sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND,"-sdk");
sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, sdk);
}
// Add -- options.
for (const char *arg : options.compilerArgs)
sourcekitd_request_array_set_string(args, SOURCEKITD_ARRAY_APPEND, arg);
}
sourcekitd_request_dictionary_set_value(request, KeyCompilerArgs, args);
sourcekitd_request_release(args);
// Send the request!
bool result = sendRequestSync(request, func);
sourcekitd_request_release(request);
return result;
}
static bool readPopularAPIList(StringRef filename,
std::vector<std::string> &result) {
std::ifstream in(filename);
if (!in.is_open()) {
llvm::errs() << "error opening '" << filename << "'\n";
return true;
}
std::string line;
while (std::getline(in, line)) {
result.emplace_back();
std::swap(result.back(), line);
}
return false;
}
static bool setupPopularAPI(const TestOptions &options) {
if (options.popularAPI.empty() && options.unpopularAPI.empty())
return false;
sourcekitd_uid_t RequestCodeCompleteSetPopularAPI =
sourcekitd_uid_get_from_cstr("source.request.codecomplete.setpopularapi");
auto req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0);
sourcekitd_request_dictionary_set_uid(req, KeyRequest,
RequestCodeCompleteSetPopularAPI);
auto addPopularList = [&req](StringRef filename, sourcekitd_uid_t key) {
std::vector<std::string> names;
if (readPopularAPIList(filename, names))
return true;
sourcekitd_object_t popular = sourcekitd_request_array_create(nullptr, 0);
for (auto name : names)
sourcekitd_request_array_set_string(popular, SOURCEKITD_ARRAY_APPEND,
name.c_str());
sourcekitd_request_dictionary_set_value(req, key, popular);
return false;
};
if (!options.popularAPI.empty() &&
addPopularList(options.popularAPI, KeyPopular))
return true;
if (!options.unpopularAPI.empty() &&
addPopularList(options.unpopularAPI, KeyUnpopular))
return true;
auto resp = sourcekitd_send_request_sync(req);
bool fail = false;
if (sourcekitd_response_is_error(resp)) {
fail = true;
sourcekitd_response_description_dump(resp);
}
sourcekitd_response_dispose(resp);
sourcekitd_request_release(req);
return fail;
}
static int handleTestInvocation(TestOptions &options) {
StringRef SourceFilename = options.sourceFile;
auto SourceBuf = getBufferForFilename(SourceFilename);
if (!SourceBuf)
return 1;
unsigned CodeCompletionOffset;
SmallVector<std::string, 4> prefixes;
std::string CleanFile = removeCodeCompletionTokens(
SourceBuf->getBuffer(), options.completionToken, prefixes,
&CodeCompletionOffset);
if (CodeCompletionOffset == ~0U) {
llvm::errs() << "cannot find code completion token in source file\n";
return 1;
}
sourcekitd_uid_t RequestCodeCompleteOpen =
sourcekitd_uid_get_from_cstr("source.request.codecomplete.open");
sourcekitd_uid_t RequestCodeCompleteClose =
sourcekitd_uid_get_from_cstr("source.request.codecomplete.close");
sourcekitd_uid_t RequestCodeCompleteUpdate =
sourcekitd_uid_get_from_cstr("source.request.codecomplete.update");
if (setupPopularAPI(options))
return 1;
// Open the connection and get the first set of results.
bool isError = codeCompleteRequest(
RequestCodeCompleteOpen, SourceFilename.data(), CodeCompletionOffset,
CleanFile.c_str(), /*filterText*/ nullptr, options,
[&](sourcekitd_object_t response) -> bool {
if (sourcekitd_response_is_error(response)) {
sourcekitd_response_description_dump(response);
return true;
}
// If there are no prefixes, just dump all the results.
if (prefixes.empty())
printResponse(response, options.rawOutput, options.structureOutput,
/*indentation*/ 0);
return false;
});
if (isError)
return isError;
for (auto &prefix : prefixes) {
isError |= codeCompleteRequest(
RequestCodeCompleteUpdate, SourceFilename.data(), CodeCompletionOffset,
CleanFile.c_str(), prefix.c_str(), options,
[&](sourcekitd_object_t response) -> bool {
if (sourcekitd_response_is_error(response)) {
sourcekitd_response_description_dump(response);
return true;
}
llvm::outs() << "Results for filterText: " << prefix << " [\n";
llvm::outs().flush();
printResponse(response, options.rawOutput, options.structureOutput,
/*indentation*/ 4);
llvm::outs() << "]\n";
llvm::outs().flush();
return false;
});
if (isError)
break;
}
// Close the code completion connection.
auto request = createBaseRequest(RequestCodeCompleteClose,
SourceFilename.data(), CodeCompletionOffset);
isError |= sendRequestSync(request, [&](sourcekitd_object_t response) {
if (sourcekitd_response_is_error(response)) {
sourcekitd_response_description_dump(response);
return true;
}
return false;
});
sourcekitd_request_release(request);
return isError;
}