[clang-tooling] Prevent llvm::fatal_error on invalid CLI option

Fail gracefully instead. Prevent further misuse by enforcing the factory builder
instead of the constructor.

Differential Revision: https://reviews.llvm.org/D94420
This commit is contained in:
serge-sans-paille 2021-01-11 16:19:35 +01:00
parent cba2552bfe
commit d47ee525f9
14 changed files with 88 additions and 37 deletions

View File

@ -99,8 +99,13 @@ llvm::ErrorOr<std::vector<std::string>> GetAllowedSymbolPatterns() {
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
tooling::CommonOptionsParser OptionsParser(argc, argv,
ChangeNamespaceCategory);
auto ExpectedParser =
tooling::CommonOptionsParser::create(argc, argv, ChangeNamespaceCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
const auto &Files = OptionsParser.getSourcePathList();
tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
llvm::ErrorOr<std::vector<std::string>> AllowedPatterns =

View File

@ -128,7 +128,14 @@ bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
} // namespace find_all_symbols
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, FindAllSymbolsCategory);
auto ExpectedParser =
CommonOptionsParser::create(argc, argv, FindAllSymbolsCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser &OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());

View File

@ -263,7 +263,13 @@ void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
}
int includeFixerMain(int argc, const char **argv) {
tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
auto ExpectedParser =
tooling::CommonOptionsParser::create(argc, argv, IncludeFixerCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &options = ExpectedParser.get();
tooling::ClangTool tool(options.getCompilations(),
options.getSourcePathList());

View File

@ -95,7 +95,13 @@ cl::opt<bool> DumpDecls(
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
auto ExpectedParser =
tooling::CommonOptionsParser::create(argc, argv, ClangMoveCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &OptionsParser = ExpectedParser.get();
if (OldDependOnNew && NewDependOnOld) {
llvm::errs() << "Provide either --old_depend_on_new or "

View File

@ -50,8 +50,14 @@ static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
int main(int argc, const char **argv) {
tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
Usage);
auto ExpectedParser = tooling::CommonOptionsParser::create(
argc, argv, ClangReorderFieldsCategory, cl::OneOrMore, Usage);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &OP = ExpectedParser.get();
auto Files = OP.getSourcePathList();
tooling::RefactoringTool Tool(OP.getCompilations(), Files);

View File

@ -1,4 +1,4 @@
// RUN: not clang-query --invalid-arg 2>&1 | FileCheck %s
// CHECK: error: [CommonOptionsParser]: clang-query{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-query{{(\.exe)?}} --help'
// CHECK: error: clang-query{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-query{{(\.exe)?}} --help'
// CHECK-NEXT: clang-query{{(\.exe)?}}: Did you mean '--extra-arg'?

View File

@ -1,4 +1,4 @@
// RUN: not clang-tidy --invalid-arg 2>&1 | FileCheck %s
// CHECK: error: [CommonOptionsParser]: clang-tidy{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-tidy{{(\.exe)?}} --help'
// CHECK: error: clang-tidy{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-tidy{{(\.exe)?}} --help'
// CHECK-NEXT: clang-tidy{{(\.exe)?}}: Did you mean '--extra-arg'?

View File

@ -141,7 +141,13 @@ documentation <LibTooling.html>`_.
static cl::extrahelp MoreHelp("\nMore help text...\n");
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
if (!ExpectedParser) {
// Fail gracefully for unsupported options.
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser& OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
return Tool.run(newFrontendActionFactory<clang::SyntaxOnlyAction>().get());
@ -282,7 +288,13 @@ And change ``main()`` to:
.. code-block:: c++
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
if (!ExpectedParser) {
// Fail gracefully for unsupported options.
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser& OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());

View File

@ -63,21 +63,8 @@ namespace tooling {
/// }
/// \endcode
class CommonOptionsParser {
public:
/// Parses command-line, initializes a compilation database.
///
/// This constructor can change argc and argv contents, e.g. consume
/// command-line options used for creating FixedCompilationDatabase.
///
/// All options not belonging to \p Category become hidden.
///
/// This constructor exits program in case of error.
CommonOptionsParser(int &argc, const char **argv,
llvm::cl::OptionCategory &Category,
const char *Overview = nullptr)
: CommonOptionsParser(argc, argv, Category, llvm::cl::OneOrMore,
Overview) {}
protected:
/// Parses command-line, initializes a compilation database.
///
/// This constructor can change argc and argv contents, e.g. consume
@ -86,16 +73,17 @@ public:
/// All options not belonging to \p Category become hidden.
///
/// It also allows calls to set the required number of positional parameters.
CommonOptionsParser(int &argc, const char **argv,
llvm::cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag,
const char *Overview = nullptr);
CommonOptionsParser(
int &argc, const char **argv, llvm::cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag = llvm::cl::OneOrMore,
const char *Overview = nullptr);
public:
/// A factory method that is similar to the above constructor, except
/// this returns an error instead exiting the program on error.
static llvm::Expected<CommonOptionsParser>
create(int &argc, const char **argv, llvm::cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag,
llvm::cl::NumOccurrencesFlag OccurrencesFlag = llvm::cl::OneOrMore,
const char *Overview = nullptr);
/// Returns a reference to the loaded compilations database.

View File

@ -115,8 +115,7 @@ llvm::Error CommonOptionsParser::init(
// Stop initializing if command-line option parsing failed.
if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
OS.flush();
return llvm::make_error<llvm::StringError>("[CommonOptionsParser]: " +
ErrorMessage,
return llvm::make_error<llvm::StringError>(ErrorMessage,
llvm::inconvertibleErrorCode());
}

View File

@ -160,7 +160,13 @@ int main(int argc, const char **argv) {
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
CommonOptionsParser OptionsParser(argc, argv, ClangCheckCategory);
auto ExpectedParser =
CommonOptionsParser::create(argc, argv, ClangCheckCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser &OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());

View File

@ -119,8 +119,13 @@ int main(int argc, const char **argv) {
const char *Overview = "\nThis tool collects the USR name and location "
"of external definitions in the source files "
"(excluding headers).\n";
CommonOptionsParser OptionsParser(argc, argv, ClangExtDefMapGenCategory,
cl::ZeroOrMore, Overview);
auto ExpectedParser = CommonOptionsParser::create(
argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser &OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());

View File

@ -612,9 +612,14 @@ int main(int argc, const char **argv) {
ClangRefactorTool RefactorTool;
CommonOptionsParser Options(
auto ExpectedParser = CommonOptionsParser::create(
argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
"Clang-based refactoring tool for C, C++ and Objective-C");
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser &Options = ExpectedParser.get();
if (auto Err = RefactorTool.Init()) {
llvm::errs() << llvm::toString(std::move(Err)) << "\n";

View File

@ -98,7 +98,13 @@ static cl::opt<bool> Force("force",
cl::cat(ClangRenameOptions));
int main(int argc, const char **argv) {
tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
auto ExpectedParser =
tooling::CommonOptionsParser::create(argc, argv, ClangRenameOptions);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &OP = ExpectedParser.get();
if (!Input.empty()) {
// Populate QualifiedNames and NewNames from a YAML file.