blob: 488539a1c76f9164f616a72d2a45b24ed76c5c78 [file] [log] [blame]
//===--- TestOptions.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 "TestOptions.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace sourcekitd_test;
using llvm::StringRef;
namespace {
// Create enum with OPT_xxx values for each option in Options.td.
enum Opt {
OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META, VALUES) \
OPT_##ID,
#include "Options.inc"
LastOption
#undef OPTION
};
// Create prefix string literals used in Options.td.
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "Options.inc"
#undef PREFIX
// Create table mapping all options defined in Options.td.
static const llvm::opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{PREFIX, NAME, HELPTEXT, \
METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, \
OPT_##ALIAS, ALIASARGS, VALUES},
#include "Options.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class TestOptTable : public llvm::opt::OptTable {
public:
TestOptTable() : OptTable(InfoTable, llvm::array_lengthof(InfoTable)){}
};
} // end anonymous namespace
static std::pair<unsigned, unsigned> parseLineCol(StringRef LineCol) {
unsigned Line, Col;
size_t ColonIdx = LineCol.find(':');
if (ColonIdx == StringRef::npos) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
exit(1);
}
if (LineCol.substr(0, ColonIdx).getAsInteger(10, Line)) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
exit(1);
}
if (LineCol.substr(ColonIdx+1).getAsInteger(10, Col)) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
exit(1);
}
if (Line == 0 || Col == 0) {
llvm::errs() << "wrong pos format, line/col should start from 1\n";
exit(1);
}
return { Line, Col };
}
bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
if (Args.empty())
return false;
// Parse command line options using Options.td
TestOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
llvm::opt::InputArgList ParsedArgs =
Table.ParseArgs(Args, MissingIndex, MissingCount);
if (MissingCount) {
llvm::errs() << "error: missing argument value for '"
<< ParsedArgs.getArgString(MissingIndex) << "', expected "
<< MissingCount << " argument(s)\n";
return true;
}
for (auto InputArg : ParsedArgs) {
switch (InputArg->getOption().getID()) {
case OPT_req:
Request = llvm::StringSwitch<SourceKitRequest>(InputArg->getValue())
.Case("version", SourceKitRequest::ProtocolVersion)
.Case("compiler-version", SourceKitRequest::CompilerVersion)
.Case("demangle", SourceKitRequest::DemangleNames)
.Case("mangle", SourceKitRequest::MangleSimpleClasses)
.Case("index", SourceKitRequest::Index)
.Case("complete", SourceKitRequest::CodeComplete)
.Case("complete.open", SourceKitRequest::CodeCompleteOpen)
.Case("complete.close", SourceKitRequest::CodeCompleteClose)
.Case("complete.update", SourceKitRequest::CodeCompleteUpdate)
.Case("complete.cache.ondisk", SourceKitRequest::CodeCompleteCacheOnDisk)
.Case("complete.setpopularapi", SourceKitRequest::CodeCompleteSetPopularAPI)
.Case("typecontextinfo", SourceKitRequest::TypeContextInfo)
.Case("conformingmethods", SourceKitRequest::ConformingMethodList)
.Case("cursor", SourceKitRequest::CursorInfo)
.Case("related-idents", SourceKitRequest::RelatedIdents)
.Case("syntax-map", SourceKitRequest::SyntaxMap)
.Case("syntax-tree", SourceKitRequest::SyntaxTree)
.Case("structure", SourceKitRequest::Structure)
.Case("format", SourceKitRequest::Format)
.Case("expand-placeholder", SourceKitRequest::ExpandPlaceholder)
.Case("doc-info", SourceKitRequest::DocInfo)
.Case("sema", SourceKitRequest::SemanticInfo)
.Case("interface-gen", SourceKitRequest::InterfaceGen)
.Case("interface-gen-open", SourceKitRequest::InterfaceGenOpen)
.Case("find-usr", SourceKitRequest::FindUSR)
.Case("find-interface", SourceKitRequest::FindInterfaceDoc)
.Case("open", SourceKitRequest::Open)
.Case("close", SourceKitRequest::Close)
.Case("edit", SourceKitRequest::Edit)
.Case("print-annotations", SourceKitRequest::PrintAnnotations)
.Case("print-diags", SourceKitRequest::PrintDiags)
.Case("extract-comment", SourceKitRequest::ExtractComment)
.Case("module-groups", SourceKitRequest::ModuleGroups)
.Case("range", SourceKitRequest::RangeInfo)
.Case("syntactic-rename", SourceKitRequest::SyntacticRename)
.Case("find-rename-ranges", SourceKitRequest::FindRenameRanges)
.Case("find-local-rename-ranges", SourceKitRequest::FindLocalRenameRanges)
.Case("translate", SourceKitRequest::NameTranslation)
.Case("local-rename", SourceKitRequest::LocalRename)
.Case("extract-expr", SourceKitRequest::ExtractExpr)
.Case("extract-repeated", SourceKitRequest::ExtractRepeatedExpr)
.Case("extract-func", SourceKitRequest::ExtractFunction)
.Case("fill-stub", SourceKitRequest::FillProtocolStub)
.Case("expand-default", SourceKitRequest::ExpandDefault)
.Case("localize-string", SourceKitRequest::LocalizeString)
.Case("markup-xml", SourceKitRequest::MarkupToXML)
.Case("stats", SourceKitRequest::Statistics)
.Case("track-compiles", SourceKitRequest::EnableCompileNotifications)
.Case("collect-type", SourceKitRequest::CollectExpresstionType)
.Case("global-config", SourceKitRequest::GlobalConfiguration)
.Default(SourceKitRequest::None);
if (Request == SourceKitRequest::None) {
llvm::errs() << "error: invalid request '" << InputArg->getValue()
<< "'\nexpected one of "
<< "version/demangle/mangle/index/complete/complete.open/complete.cursor/"
"complete.update/complete.cache.ondisk/complete.cache.setpopularapi/"
"cursor/related-idents/syntax-map/structure/format/expand-placeholder/"
"doc-info/sema/interface-gen/interface-gen-openfind-usr/find-interface/"
"open/close/edit/print-annotations/print-diags/extract-comment/module-groups/"
"range/syntactic-rename/find-rename-ranges/translate/markup-xml/stats/"
"track-compiles/collect-type\n";
return true;
}
break;
case OPT_help: {
printHelp(false);
return true;
}
case OPT_offset:
if (StringRef(InputArg->getValue()).getAsInteger(10, Offset)) {
llvm::errs() << "error: expected integer for 'offset'\n";
return true;
}
break;
case OPT_length:
if (StringRef(InputArg->getValue()).getAsInteger(10, Length)) {
llvm::errs() << "error: expected integer for 'length'\n";
return true;
}
break;
case OPT_pos: {
auto linecol = parseLineCol(InputArg->getValue());
Line = linecol.first;
Col = linecol.second;
break;
}
case OPT_end_pos: {
auto linecol = parseLineCol(InputArg->getValue());
EndLine = linecol.first;
EndCol = linecol.second;
break;
}
case OPT_using_swift_args: {
UsingSwiftArgs = true;
break;
}
case OPT_swift_version:
SwiftVersion = InputArg->getValue();
break;
case OPT_pass_version_as_string:
PassVersionAsString = true;
break;
case OPT_line:
if (StringRef(InputArg->getValue()).getAsInteger(10, Line)) {
llvm::errs() << "error: expected integer for 'line'\n";
return true;
}
Col = 1;
break;
case OPT_replace:
ReplaceText = InputArg->getValue();
break;
case OPT_module:
ModuleName = InputArg->getValue();
break;
case OPT_group_name:
ModuleGroupName = InputArg->getValue();
break;
case OPT_interested_usr:
InterestedUSR = InputArg->getValue();
break;
case OPT_header:
HeaderPath = InputArg->getValue();
break;
case OPT_text_input:
TextInputFile = InputArg->getValue();
break;
case OPT_usr:
USR = InputArg->getValue();
break;
case OPT_pass_as_sourcetext:
PassAsSourceText = true;
break;
case OPT_cache_path:
CachePath = InputArg->getValue();
break;
case OPT_req_opts:
for (auto item : InputArg->getValues())
RequestOptions.push_back(item);
break;
case OPT_check_interface_is_ascii:
CheckInterfaceIsASCII = true;
break;
case OPT_dont_print_request:
PrintRequest = false;
break;
case OPT_print_response_as_json:
PrintResponseAsJSON = true;
break;
case OPT_print_raw_response:
PrintRawResponse = true;
break;
case OPT_dont_print_response:
PrintResponse = false;
break;
case OPT_INPUT:
SourceFile = InputArg->getValue();
SourceText = llvm::None;
Inputs.push_back(InputArg->getValue());
break;
case OPT_rename_spec:
RenameSpecPath = InputArg->getValue();
break;
case OPT_json_request_path:
JsonRequestPath = InputArg->getValue();
break;
case OPT_simplified_demangling:
SimplifiedDemangling = true;
break;
case OPT_synthesized_extension:
SynthesizedExtensions = true;
break;
case OPT_async:
isAsyncRequest = true;
break;
case OPT_cursor_action:
CollectActionables = true;
break;
case OPT_swift_name:
SwiftName = InputArg->getValue();
break;
case OPT_objc_name:
ObjCName = InputArg->getValue();
break;
case OPT_objc_selector:
ObjCSelector = InputArg->getValue();
break;
case OPT_name:
Name = InputArg->getValue();
break;
case OPT_cancel_on_subsequent_request:
unsigned Cancel;
if (StringRef(InputArg->getValue()).getAsInteger(10, Cancel)) {
llvm::errs() << "error: expected integer for 'cancel-on-subsequent-request'\n";
return true;
}
CancelOnSubsequentRequest = Cancel;
break;
case OPT_time_request:
timeRequest = true;
break;
case OPT_repeat_request:
if (StringRef(InputArg->getValue()).getAsInteger(10, repeatRequest)) {
llvm::errs() << "error: expected integer for 'cancel-on-subsequent-request'\n";
return true;
} else if (repeatRequest < 1) {
llvm::errs() << "error: repeat-request must be >= 1\n";
return true;
}
break;
// SWIFT_ENABLE_TENSORFLOW
case OPT_in_memory_clang_module_cache:
#ifdef SWIFT_SOURCEKIT_USE_INPROC_LIBRARY
InMemoryClangModuleCache = true;
break;
#else
// The -in-memory-clang-module-cache option operates by making a function
// call to a function defined in the SourceKit InProc library
// (SourceKit::setGlobalInMemoryOutputFileSystem). So this option only
// works when sourcekitd-test is compiled using that library. It does not
// work when sourcekitd-test uses the XPC library.
llvm::errs() << "in-memory-clang-module-cache only supported when "
"SWIFT_SOURCEKIT_USE_INPROC_LIBRARY is set";
return true;
#endif
case OPT_vfs_files:
VFSName = VFSName.getValueOr("in-memory-vfs");
for (const char *vfsFile : InputArg->getValues()) {
StringRef name, target;
std::tie(name, target) = StringRef(vfsFile).split('=');
llvm::SmallString<64> nativeName;
llvm::sys::path::native(name, nativeName);
bool passAsSourceText = target.consume_front("@");
VFSFiles.try_emplace(nativeName.str(), VFSFile(target.str(), passAsSourceText));
}
break;
case OPT_vfs_name:
VFSName = InputArg->getValue();
break;
case OPT_suppress_config_request:
SuppressDefaultConfigRequest = true;
break;
case OPT_module_cache_path:
ModuleCachePath = InputArg->getValue();
break;
case OPT_shell:
ShellExecution = true;
break;
case OPT_UNKNOWN:
llvm::errs() << "error: unknown argument: "
<< InputArg->getAsString(ParsedArgs) << '\n'
<< "Use -h or -help for assistance" << '\n';
return true;
}
}
if (Request == SourceKitRequest::InterfaceGenOpen && isAsyncRequest) {
llvm::errs()
<< "error: cannot use -async with interface-gen-open request\n";
return true;
}
return false;
}
void TestOptions::printHelp(bool ShowHidden) const {
// Based off of swift/lib/Driver/Driver.cpp, at Driver::printHelp
//FIXME: should we use IncludedFlagsBitmask and ExcludedFlagsBitmask?
// Maybe not for modes such as Interactive, Batch, AutolinkExtract, etc,
// as in Driver.cpp. But could be useful for extra info, like HelpHidden.
TestOptTable Table;
Table.PrintHelp(llvm::outs(), "sourcekitd-test [options] <inputs>",
"SourceKit Testing Tool", ShowHidden);
}