[clang-tooling] Prevent llvm::fatal_error on invalid CLI option
authorserge-sans-paille <sguelton@redhat.com>
Mon, 11 Jan 2021 15:19:35 +0000 (16:19 +0100)
committerserge-sans-paille <sguelton@redhat.com>
Fri, 29 Jan 2021 09:15:06 +0000 (10:15 +0100)
Fail gracefully instead. Prevent further misuse by enforcing the factory builder
instead of the constructor.

Differential Revision: https://reviews.llvm.org/D94420

14 files changed:
clang-tools-extra/clang-change-namespace/tool/ClangChangeNamespace.cpp
clang-tools-extra/clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
clang-tools-extra/clang-include-fixer/tool/ClangIncludeFixer.cpp
clang-tools-extra/clang-move/tool/ClangMove.cpp
clang-tools-extra/clang-reorder-fields/tool/ClangReorderFields.cpp
clang-tools-extra/test/clang-query/invalid-command-line.cpp
clang-tools-extra/test/clang-tidy/infrastructure/invalid-command-line.cpp
clang/docs/LibASTMatchersTutorial.rst
clang/include/clang/Tooling/CommonOptionsParser.h
clang/lib/Tooling/CommonOptionsParser.cpp
clang/tools/clang-check/ClangCheck.cpp
clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp
clang/tools/clang-refactor/ClangRefactor.cpp
clang/tools/clang-rename/ClangRename.cpp

index babc207..5f30cbf 100644 (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 =
index 8508721..b2d0efe 100644 (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());
 
index 50a0c49..3a11a22 100644 (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());
 
index 7e16dc2..d4bd9a3 100644 (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 "
index 5150dc4..3753067 100644 (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);
index 901aad8..e3e8af1 100644 (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'?
index be84a08..c06b09d 100644 (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'?
index 22285c5..f70173e 100644 (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());
 
index a5bfeee..0f072c2 100644 (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.
index 5d881aa..6301544 100644 (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());
   }
 
index 6565aa2..a049364 100644 (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());
 
index b91bf79..8aba130 100644 (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());
index 8b44c7f..0c82fd7 100644 (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";
index 6dcd33a..141ba37 100644 (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.