Skip to content

Commit

Permalink
[clang-tooling] Prevent llvm::fatal_error on invalid CLI option
Browse files Browse the repository at this point in the history
Fail gracefully instead. Prevent further misuse by enforcing the factory builder
instead of the constructor.

Differential Revision: https://reviews.llvm.org/D94420
  • Loading branch information
serge-sans-paille committed Jan 29, 2021
1 parent cba2552 commit d47ee52
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
8 changes: 7 additions & 1 deletion clang-tools-extra/clang-move/tool/ClangMove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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'?
Original file line number Diff line number Diff line change
@@ -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'?
16 changes: 14 additions & 2 deletions clang/docs/LibASTMatchersTutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down
26 changes: 7 additions & 19 deletions clang/include/clang/Tooling/CommonOptionsParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -86,16 +73,17 @@ class CommonOptionsParser {
/// 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.
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Tooling/CommonOptionsParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
8 changes: 7 additions & 1 deletion clang/tools/clang-check/ClangCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
9 changes: 7 additions & 2 deletions clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
7 changes: 6 additions & 1 deletion clang/tools/clang-refactor/ClangRefactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
8 changes: 7 additions & 1 deletion clang/tools/clang-rename/ClangRename.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit d47ee52

Please sign in to comment.