[clang] [llvm] [clang] Migrate clang-rename to OptTable parsing (PR #89167)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 17 20:13:45 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Jordan Rupprecht (rupprecht)
<details>
<summary>Changes</summary>
Using OptTable to parse will allow including this tool in llvm-driver.
Because CommonOptionsParser is widely used and makes use of `cl::opt` flags, it needs to be refactored to handle both. The existing `CommonOptionsParser::create()` method should continue to work for downstream users. An additional overload allows a general function to be passed in, which can do arg parsing however it likes, as long as it returns the fields that CommonOptionsParser needs.
Many other simple `clang-*` tools can be similarly migrated after this.
---
Patch is 23.29 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/89167.diff
7 Files Affected:
- (modified) clang/include/clang/Tooling/CommonOptionsParser.h (+21-4)
- (added) clang/include/clang/Tooling/CommonOptionsParser.td (+14)
- (modified) clang/lib/Tooling/CommonOptionsParser.cpp (+75-52)
- (modified) clang/tools/clang-rename/CMakeLists.txt (+7)
- (modified) clang/tools/clang-rename/ClangRename.cpp (+136-46)
- (added) clang/tools/clang-rename/Opts.td (+32)
- (modified) utils/bazel/llvm-project-overlay/clang/BUILD.bazel (+19-2)
``````````diff
diff --git a/clang/include/clang/Tooling/CommonOptionsParser.h b/clang/include/clang/Tooling/CommonOptionsParser.h
index 3c0480af377943..da1fd299a04836 100644
--- a/clang/include/clang/Tooling/CommonOptionsParser.h
+++ b/clang/include/clang/Tooling/CommonOptionsParser.h
@@ -86,6 +86,24 @@ class CommonOptionsParser {
llvm::cl::NumOccurrencesFlag OccurrencesFlag = llvm::cl::OneOrMore,
const char *Overview = nullptr);
+ struct Args {
+ std::string BuildPath;
+ std::vector<std::string> SourcePaths;
+ std::vector<std::string> ArgsAfter;
+ std::vector<std::string> ArgsBefore;
+ };
+
+ using ArgParserCallback =
+ std::function<llvm::Expected<Args>(int &argc, const char **argv)>;
+
+ /// A factory method that is similar to the above factory method, except
+ /// this does not force use of cl::opt argument parsing. The function passed
+ /// in is expected to handle argument parsing, and must return values needed
+ /// by CommonOptionsParser.
+ static llvm::Expected<CommonOptionsParser>
+ create(int &argc, const char **argv, ArgParserCallback ArgsCallback,
+ llvm::cl::NumOccurrencesFlag OccurrencesFlag = llvm::cl::OneOrMore);
+
/// Returns a reference to the loaded compilations database.
CompilationDatabase &getCompilations() {
return *Compilations;
@@ -105,10 +123,9 @@ class CommonOptionsParser {
private:
CommonOptionsParser() = default;
- llvm::Error init(int &argc, const char **argv,
- llvm::cl::OptionCategory &Category,
- llvm::cl::NumOccurrencesFlag OccurrencesFlag,
- const char *Overview);
+ llvm::Error
+ init(int &argc, const char **argv, ArgParserCallback ArgsCallback,
+ llvm::cl::NumOccurrencesFlag OccurrencesFlag = llvm::cl::OneOrMore);
std::unique_ptr<CompilationDatabase> Compilations;
std::vector<std::string> SourcePathList;
diff --git a/clang/include/clang/Tooling/CommonOptionsParser.td b/clang/include/clang/Tooling/CommonOptionsParser.td
new file mode 100644
index 00000000000000..0e79136a42bcb2
--- /dev/null
+++ b/clang/include/clang/Tooling/CommonOptionsParser.td
@@ -0,0 +1,14 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Eq<string name, string help> {
+ def NAME#_EQ : Joined<["--", "-"], name#"=">, HelpText<help>;
+ def : Separate<["--", "-"], name>, Alias<!cast<Joined>(NAME#_EQ)>;
+}
+
+defm build_path : Eq<"p", "Build path.">;
+defm extra_arg
+ : Eq<"extra-arg",
+ "Additional argument to append to the compiler command line.">;
+defm extra_arg_before
+ : Eq<"extra-arg-before",
+ "Additional argument to prepend to the compiler command line.">;
diff --git a/clang/lib/Tooling/CommonOptionsParser.cpp b/clang/lib/Tooling/CommonOptionsParser.cpp
index 59ef47cc0166ef..341a8ab46c50e7 100644
--- a/clang/lib/Tooling/CommonOptionsParser.cpp
+++ b/clang/lib/Tooling/CommonOptionsParser.cpp
@@ -57,13 +57,12 @@ void ArgumentsAdjustingCompilations::appendArgumentsAdjuster(
Adjusters.push_back(std::move(Adjuster));
}
-std::vector<CompileCommand> ArgumentsAdjustingCompilations::getCompileCommands(
- StringRef FilePath) const {
+std::vector<CompileCommand>
+ArgumentsAdjustingCompilations::getCompileCommands(StringRef FilePath) const {
return adjustCommands(Compilations->getCompileCommands(FilePath));
}
-std::vector<std::string>
-ArgumentsAdjustingCompilations::getAllFiles() const {
+std::vector<std::string> ArgumentsAdjustingCompilations::getAllFiles() const {
return Compilations->getAllFiles();
}
@@ -80,58 +79,32 @@ std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
return Commands;
}
-llvm::Error CommonOptionsParser::init(
- int &argc, const char **argv, cl::OptionCategory &Category,
- llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
-
- static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
- cl::Optional, cl::cat(Category),
- cl::sub(cl::SubCommand::getAll()));
-
- static cl::list<std::string> SourcePaths(
- cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
- cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
-
- static cl::list<std::string> ArgsAfter(
- "extra-arg",
- cl::desc("Additional argument to append to the compiler command line"),
- cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
-
- static cl::list<std::string> ArgsBefore(
- "extra-arg-before",
- cl::desc("Additional argument to prepend to the compiler command line"),
- cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
-
- cl::ResetAllOptionOccurrences();
-
- cl::HideUnrelatedOptions(Category);
-
+llvm::Error
+CommonOptionsParser::init(int &argc, const char **argv,
+ ArgParserCallback ArgsCallback,
+ llvm::cl::NumOccurrencesFlag OccurrencesFlag) {
std::string ErrorMessage;
Compilations =
FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
if (!ErrorMessage.empty())
ErrorMessage.append("\n");
- llvm::raw_string_ostream OS(ErrorMessage);
- // Stop initializing if command-line option parsing failed.
- if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
- OS.flush();
- return llvm::make_error<llvm::StringError>(ErrorMessage,
- llvm::inconvertibleErrorCode());
- }
- cl::PrintOptionValues();
+ // Stop initializing if command-line option parsing failed.
+ auto Args = ArgsCallback(argc, argv);
+ if (!Args)
+ return Args.takeError();
- SourcePathList = SourcePaths;
+ SourcePathList = Args->SourcePaths;
if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) &&
SourcePathList.empty())
return llvm::Error::success();
if (!Compilations) {
- if (!BuildPath.empty()) {
- Compilations =
- CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
+ if (!Args->BuildPath.empty()) {
+ Compilations = CompilationDatabase::autoDetectFromDirectory(
+ Args->BuildPath, ErrorMessage);
} else {
- Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
- ErrorMessage);
+ Compilations = CompilationDatabase::autoDetectFromSource(
+ Args->SourcePaths[0], ErrorMessage);
}
if (!Compilations) {
llvm::errs() << "Error while trying to load a compilation database:\n"
@@ -141,24 +114,72 @@ llvm::Error CommonOptionsParser::init(
}
}
auto AdjustingCompilations =
- std::make_unique<ArgumentsAdjustingCompilations>(
- std::move(Compilations));
- Adjuster =
- getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
+ std::make_unique<ArgumentsAdjustingCompilations>(std::move(Compilations));
+ Adjuster = getInsertArgumentAdjuster(Args->ArgsBefore,
+ ArgumentInsertPosition::BEGIN);
Adjuster = combineAdjusters(
std::move(Adjuster),
- getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
+ getInsertArgumentAdjuster(Args->ArgsAfter, ArgumentInsertPosition::END));
AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
Compilations = std::move(AdjustingCompilations);
return llvm::Error::success();
}
+CommonOptionsParser::ArgParserCallback
+makeClOptParserCallback(llvm::cl::OptionCategory &Category,
+ llvm::cl::NumOccurrencesFlag OccurrencesFlag,
+ const char *Overview) {
+ return [&Category, OccurrencesFlag, Overview](
+ int &argc,
+ const char **argv) -> llvm::Expected<CommonOptionsParser::Args> {
+ static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
+ cl::Optional, cl::cat(Category),
+ cl::sub(cl::SubCommand::getAll()));
+
+ static cl::list<std::string> SourcePaths(
+ cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
+
+ static cl::list<std::string> ArgsAfter(
+ "extra-arg",
+ cl::desc("Additional argument to append to the compiler command line"),
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
+
+ static cl::list<std::string> ArgsBefore(
+ "extra-arg-before",
+ cl::desc("Additional argument to prepend to the compiler command line"),
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
+
+ cl::ResetAllOptionOccurrences();
+
+ cl::HideUnrelatedOptions(Category);
+
+ std::string ErrorMessage;
+ llvm::raw_string_ostream OS(ErrorMessage);
+ if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
+ OS.flush();
+ return llvm::make_error<llvm::StringError>(
+ ErrorMessage, llvm::inconvertibleErrorCode());
+ }
+ return CommonOptionsParser::Args{BuildPath, SourcePaths, ArgsAfter,
+ ArgsBefore};
+ };
+}
+
llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
int &argc, const char **argv, llvm::cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
+ return create(argc, argv,
+ makeClOptParserCallback(Category, OccurrencesFlag, Overview),
+ OccurrencesFlag);
+}
+
+llvm::Expected<CommonOptionsParser>
+CommonOptionsParser::create(int &argc, const char **argv,
+ ArgParserCallback ArgsCallback,
+ llvm::cl::NumOccurrencesFlag OccurrencesFlag) {
CommonOptionsParser Parser;
- llvm::Error Err =
- Parser.init(argc, argv, Category, OccurrencesFlag, Overview);
+ llvm::Error Err = Parser.init(argc, argv, ArgsCallback, OccurrencesFlag);
if (Err)
return std::move(Err);
return std::move(Parser);
@@ -167,7 +188,9 @@ llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
CommonOptionsParser::CommonOptionsParser(
int &argc, const char **argv, cl::OptionCategory &Category,
llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
- llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
+ llvm::Error Err = init(
+ argc, argv, makeClOptParserCallback(Category, OccurrencesFlag, Overview),
+ OccurrencesFlag);
if (Err) {
llvm::report_fatal_error(
Twine("CommonOptionsParser: failed to parse command-line arguments. ") +
diff --git a/clang/tools/clang-rename/CMakeLists.txt b/clang/tools/clang-rename/CMakeLists.txt
index f4c4e520520d9e..b67cc4ee0c84f8 100644
--- a/clang/tools/clang-rename/CMakeLists.txt
+++ b/clang/tools/clang-rename/CMakeLists.txt
@@ -3,8 +3,15 @@ set(LLVM_LINK_COMPONENTS
Support
)
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(ClangRenameOptsTableGen)
+
add_clang_tool(clang-rename
ClangRename.cpp
+
+ DEPENDS
+ ClangRenameOptsTableGen
)
clang_target_link_libraries(clang-rename
diff --git a/clang/tools/clang-rename/ClangRename.cpp b/clang/tools/clang-rename/ClangRename.cpp
index f2ac0c4360e0dc..99b530dd6d2c2d 100644
--- a/clang/tools/clang-rename/ClangRename.cpp
+++ b/clang/tools/clang-rename/ClangRename.cpp
@@ -28,13 +28,20 @@
#include "clang/Tooling/ReplacementsYaml.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <system_error>
+#include "Opts.inc"
+
using namespace llvm;
using namespace clang;
@@ -63,45 +70,123 @@ template <> struct MappingTraits<RenameAllInfo> {
} // end namespace yaml
} // end namespace llvm
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) \
+ static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
+ static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
+ NAME##_init, std::size(NAME##_init) - 1);
+#include "Opts.inc"
+#undef PREFIX
+
+using namespace llvm::opt;
+static constexpr opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+class ClangRenameOptTable : public opt::GenericOptTable {
+public:
+ ClangRenameOptTable() : GenericOptTable(InfoTable) {}
+};
+} // end anonymous namespace
+
static cl::OptionCategory ClangRenameOptions("clang-rename common options");
-static cl::list<unsigned> SymbolOffsets(
- "offset",
- cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
- cl::cat(ClangRenameOptions));
-static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
- cl::cat(ClangRenameOptions));
-static cl::list<std::string>
- QualifiedNames("qualified-name",
- cl::desc("The fully qualified name of the symbol."),
- cl::cat(ClangRenameOptions));
-
-static cl::list<std::string>
- NewNames("new-name", cl::desc("The new name to change the symbol to."),
- cl::cat(ClangRenameOptions));
-static cl::opt<bool> PrintName(
- "pn",
- cl::desc("Print the found symbol's name prior to renaming to stderr."),
- cl::cat(ClangRenameOptions));
-static cl::opt<bool> PrintLocations(
- "pl", cl::desc("Print the locations affected by renaming to stderr."),
- cl::cat(ClangRenameOptions));
-static cl::opt<std::string>
- ExportFixes("export-fixes",
- cl::desc("YAML file to store suggested fixes in."),
- cl::value_desc("filename"), cl::cat(ClangRenameOptions));
-static cl::opt<std::string>
- Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
- cl::Optional, cl::cat(ClangRenameOptions));
-static cl::opt<bool> Force("force",
- cl::desc("Ignore nonexistent qualified names."),
- cl::cat(ClangRenameOptions));
+static std::vector<unsigned> SymbolOffsets;
+static bool Inplace;
+static std::vector<std::string> QualifiedNames;
+static std::vector<std::string> NewNames;
+static bool PrintName;
+static bool PrintLocations;
+static std::string ExportFixes;
+static std::string Input;
+static bool Force;
+
+static tooling::CommonOptionsParser::Args ParseArgs(int argc,
+ const char **argv) {
+ ClangRenameOptTable Tbl;
+ llvm::StringRef ToolName = argv[0];
+ llvm::BumpPtrAllocator A;
+ llvm::StringSaver Saver{A};
+ llvm::opt::InputArgList Args = Tbl.parseArgs(
+ argc, const_cast<char **>(argv), OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ WithColor::error() << Msg << "\n";
+ std::exit(1);
+ });
+
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(llvm::outs(), "clang-rename [options]", "clang-rename");
+ std::exit(0);
+ }
+ if (Args.hasArg(OPT_version)) {
+ llvm::outs() << ToolName << '\n';
+ llvm::cl::PrintVersionMessage();
+ std::exit(0);
+ }
+
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_offset_EQ)) {
+ StringRef S{A->getValue()};
+ unsigned Value;
+ if (!llvm::to_integer(S, Value, 0)) {
+ WithColor::error() << ToolName << ": for the --offset option: '" << S
+ << "' value invalid for uint argument!\n";
+ std::exit(1);
+ }
+ SymbolOffsets.emplace_back(Value);
+ }
+
+ Inplace = Args.hasArg(OPT_inplace);
+
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_qualified_name_EQ))
+ QualifiedNames.emplace_back(A->getValue());
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_new_name_EQ))
+ NewNames.emplace_back(A->getValue());
+
+ PrintName = Args.hasArg(OPT_print_name);
+ PrintLocations = Args.hasArg(OPT_print_locations);
+
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_export_fixes_EQ))
+ ExportFixes = A->getValue();
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_input_EQ))
+ Input = A->getValue();
+
+ Force = Args.hasArg(OPT_force);
+
+ tooling::CommonOptionsParser::Args args;
+ if (const llvm::opt::Arg *A = Args.getLastArg(OPT_build_path_EQ))
+ args.BuildPath = A->getValue();
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_extra_arg_EQ))
+ args.ArgsAfter.emplace_back(A->getValue());
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_extra_arg_before_EQ))
+ args.ArgsBefore.emplace_back(A->getValue());
+ for (const llvm::opt::Arg *A : Args.filtered(OPT_INPUT))
+ args.SourcePaths.emplace_back(A->getValue());
+ if (args.SourcePaths.empty()) {
+ WithColor::error() << ToolName
+ << ": must set at least one source path (-p).\n";
+ std::exit(1);
+ }
+ return args;
+}
int main(int argc, const char **argv) {
- auto ExpectedParser =
- tooling::CommonOptionsParser::create(argc, argv, ClangRenameOptions);
+ auto callback = [&](int &argc, const char **argv)
+ -> llvm::Expected<tooling::CommonOptionsParser::Args> {
+ return ParseArgs(argc, argv);
+ };
+
+ auto ExpectedParser = tooling::CommonOptionsParser::create(
+ argc, const_cast<const char **>(argv), callback);
if (!ExpectedParser) {
- llvm::errs() << ExpectedParser.takeError();
+ WithColor::error() << ExpectedParser.takeError();
return 1;
}
tooling::CommonOptionsParser &OP = ExpectedParser.get();
@@ -111,8 +196,8 @@ int main(int argc, const char **argv) {
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
llvm::MemoryBuffer::getFile(Input);
if (!Buffer) {
- errs() << "clang-rename: failed to read " << Input << ": "
- << Buffer.getError().message() << "\n";
+ WithColor::error() << "clang-rename: failed to read " << Input << ": "
+ << Buffer.getError().message() << "\n";
return 1;
}
@@ -130,13 +215,14 @@ int main(int argc, const char **argv) {
// Check the arguments for correctness.
if (NewNames.empty()) {
- errs() << "clang-rename: -new-name must be specified.\n\n";
+ WithColor::error() << "clang-rename: -new-name must be specified.\n\n";
return 1;
}
if (SymbolOffsets.empty() == QualifiedNames.empty()) {
- errs() << "clang-rename: -offset and -qualified-name can't be present at "
- "the same time.\n";
+ WithColor::error()
+ << "clang-rename: -offset and -qualified-name can't be present at "
+ "the same time.\n";
return 1;
}
@@ -148,16 +234,19 @@ int main(int argc, const char **argv) {
for (const auto &NewName : NewNames) {
auto NewNameTokKind = Table.get(NewName).getTokenID();
if (!tok::isAnyIdentifier(NewNameTokKind)) {
- errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
+ WithColor::error()
+ << "ERROR: new name is not a valid identifier in C++17.\n\n";
return 1;
}
}
if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
- errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
- << ") + number of qualified names (" << QualifiedNames.size()
- << ") must be equal to number of new names(" << NewNames.size()
- << ").\n\n";
+ WithColor::error() << "clang-rename: number of symbol offsets("
+ << SymbolOffsets.size()
+ << ") + number of qualified names ("
+ << QualifiedNames.size()
+ << ") must be equal to number of new names("
+ << NewNames.size() << ").\n\n";
cl::PrintHelpMessage();
return 1;
}
@@ -196,7 +285,8 @@ int main(int argc, const char **argv) {
std::error_code EC;
llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None);
if (EC) {
- llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+ WithColor::error() << "E...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/89167
More information about the llvm-commits
mailing list