[clang-tools-extra] [llvm] Port clang-tidy to Opts.td and multicall (PR #201743)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 4 21:11:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-clang-tidy
Author: David Zbarsky (dzbarsky)
<details>
<summary>Changes</summary>
---
Patch is 69.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/201743.diff
9 Files Affected:
- (modified) clang-tools-extra/clang-tidy/tool/CMakeLists.txt (+8)
- (modified) clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp (+309-313)
- (modified) clang-tools-extra/clang-tidy/tool/ClangTidyMain.h (+1-1)
- (modified) clang-tools-extra/clang-tidy/tool/ClangTidyToolMain.cpp (+2-1)
- (added) clang-tools-extra/clang-tidy/tool/Opts.td (+245)
- (modified) clang-tools-extra/docs/clang-tidy/index.rst (+152-179)
- (modified) clang-tools-extra/test/clang-tidy/infrastructure/invalid-command-line.cpp (+1-2)
- (modified) utils/bazel/llvm-project-overlay/clang-tools-extra/clang-tidy/BUILD.bazel (+19-3)
- (modified) utils/bazel/llvm-project-overlay/llvm/driver.bzl (+1)
``````````diff
diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt
index 0d4501d1eac06..da007f49c934a 100644
--- a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt
@@ -3,10 +3,15 @@ set(LLVM_LINK_COMPONENTS
AllTargetsDescs
AllTargetsInfos
FrontendOpenMP
+ Option
TargetParser
support
)
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(ClangTidyOptsTableGen)
+
# Needed by LLVM's CMake checks because this file defines multiple targets.
set(LLVM_OPTIONAL_SOURCES ClangTidyMain.cpp ClangTidyToolMain.cpp)
@@ -20,6 +25,7 @@ add_clang_library(clangTidyMain STATIC
DEPENDS
omp_gen
ClangDriverOptions
+ ClangTidyOptsTableGen
)
clang_target_link_libraries(clangTidyMain
@@ -42,6 +48,8 @@ add_clang_tool(clang-tidy
DEPENDS
clang-resource-headers
${support_plugins}
+
+ GENERATE_DRIVER
)
clang_target_link_libraries(clang-tidy
PRIVATE
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 949a88f0fd50d..89ae0f497abeb 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -18,13 +18,22 @@
#include "../ClangTidy.h"
#include "../ClangTidyForceLinker.h" // IWYU pragma: keep
#include "../GlobList.h"
+#include "clang/Basic/Version.h"
#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Allocator.h"
#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/InitLLVM.h"
+#define DONT_GET_PLUGIN_LOADER_OPTION
#include "llvm/Support/PluginLoader.h" // IWYU pragma: keep
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/WithColor.h"
#include "llvm/TargetParser/Host.h"
@@ -33,17 +42,62 @@
using namespace clang::tooling;
using namespace llvm;
-static cl::desc desc(StringRef Description) { return {Description.ltrim()}; }
-
-static cl::OptionCategory ClangTidyCategory("clang-tidy options");
-
-static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
-static cl::extrahelp ClangTidyParameterFileHelp(R"(
+namespace {
+using namespace llvm::opt;
+
+enum ID {
+ OPT_INVALID = 0,
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define OPTTABLE_STR_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "Opts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+static constexpr opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
+
+class ClangTidyOptTable : public opt::GenericOptTable {
+public:
+ ClangTidyOptTable()
+ : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
+};
+
+static constexpr llvm::StringLiteral CommonHelp = R"(
+-p <build-path> is used to read a compile command database.
+
+ For example, it can be a CMake build directory in which a file named
+ compile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+ CMake option to get this output). When no build path is specified,
+ a search for compile_commands.json will be attempted through all
+ parent paths of the first input file. See:
+ https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an
+ example of setting up Clang Tooling on a source tree.
+
+<source0> ... specify the paths of source files. These paths are
+ looked up in the compile command database. If the path of a file is
+ absolute, it needs to point into CMake's source tree. If the path is
+ relative, the current working directory needs to be in the CMake
+ source tree and the file must be in a subdirectory of the current
+ working directory. "./" prefixes in the relative files will be
+ automatically removed, but the rest of a relative path must be a
+ suffix of a path in the compile command database.
+)";
+static constexpr llvm::StringLiteral ClangTidyParameterFileHelp = R"(
Parameters files:
A large number of options or source files can be passed as parameter files
by use '@parameter-file' in the command line.
-)");
-static cl::extrahelp ClangTidyHelp(R"(
+)";
+static constexpr llvm::StringLiteral ClangTidyHelp = R"(
Configuration files:
clang-tidy attempts to read configuration for each source file from a
.clang-tidy file located in the closest parent directory of the source
@@ -102,270 +156,221 @@ Configuration files:
some-check.SomeOption: 'some value'
...
-)");
+)";
const char DefaultChecks[] = // Enable these checks by default:
"clang-diagnostic-*"; // * compiler diagnostics
-static cl::opt<std::string> Checks("checks", desc(R"(
-Comma-separated list of globs with optional '-'
-prefix. Globs are processed in order of
-appearance in the list. Globs without '-'
-prefix add checks with matching names to the
-set, globs with the '-' prefix remove checks
-with matching names from the set of enabled
-checks. This option's value is appended to the
-value of the 'Checks' option in .clang-tidy
-file, if any.
-)"),
- cl::init(""), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", desc(R"(
-Upgrades warnings to errors. Same format as
-'-checks'.
-This option's value is appended to the value of
-the 'WarningsAsErrors' option in .clang-tidy
-file, if any.
-)"),
- cl::init(""),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> HeaderFilter("header-filter", desc(R"(
-Regular expression matching the names of the
-headers to output diagnostics from. The default
-value is '.*', i.e. diagnostics from all non-system
-headers are displayed by default. Diagnostics
-from the main file of each translation unit are
-always displayed.
-Can be used together with -line-filter.
-This option overrides the 'HeaderFilterRegex'
-option in .clang-tidy file, if any.
-)"),
- cl::init(".*"),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> ExcludeHeaderFilter("exclude-header-filter",
- desc(R"(
-Regular expression matching the names of the
-headers to exclude diagnostics from. Diagnostics
-from the main file of each translation unit are
-always displayed.
-Must be used together with --header-filter.
-Can be used together with -line-filter.
-This option overrides the 'ExcludeHeaderFilterRegex'
-option in .clang-tidy file, if any.
-)"),
- cl::init(""),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> SystemHeaders("system-headers", desc(R"(
-Display the errors from system headers.
-This option overrides the 'SystemHeaders' option
-in .clang-tidy file, if any.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> LineFilter("line-filter", desc(R"(
-List of files and line ranges to output diagnostics from.
-The range is inclusive on both ends. Can be used together
-with -header-filter. The format of the list is a JSON
-array of objects. For example:
-
- [
- {"name":"file1.cpp","lines":[[1,3],[5,7]]},
- {"name":"file2.h"}
- ]
-
-This will output diagnostics from 'file1.cpp' only for
-the line ranges [1,3] and [5,7], as well as all from the
-entire 'file2.h'.
-)"),
- cl::init(""),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> Fix("fix", desc(R"(
-Apply suggested fixes. Without -fix-errors
-clang-tidy will bail out if any compilation
-errors were found.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> FixErrors("fix-errors", desc(R"(
-Apply suggested fixes even if compilation
-errors were found. If compiler errors have
-attached fix-its, clang-tidy will apply them as
-well.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> FixNotes("fix-notes", desc(R"(
-If a warning has no fix, but a single fix can
-be found through an associated diagnostic note,
-apply the fix.
-Specifying this flag will implicitly enable the
-'--fix' flag.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> FormatStyle("format-style", desc(R"(
-Style for formatting code around applied fixes:
- - 'none' (default) turns off formatting
- - 'file' (literally 'file', not a placeholder)
- uses .clang-format file in the closest parent
- directory
- - '{ <json> }' specifies options inline, e.g.
- -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
- - 'llvm', 'google', 'webkit', 'mozilla'
-See clang-format documentation for the up-to-date
-information about formatting styles and options.
-This option overrides the 'FormatStyle` option in
-.clang-tidy file, if any.
-)"),
- cl::init("none"),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> ListChecks("list-checks", desc(R"(
-List all enabled checks and exit. Use with
--checks=* to list all available checks.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> ExplainConfig("explain-config", desc(R"(
-For each enabled check explains, where it is
-enabled, i.e. in clang-tidy binary, command
-line or a specific configuration file.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> Config("config", desc(R"(
-Specifies a configuration in YAML/JSON format:
- -config="{Checks: '*',
- CheckOptions: {x: y}}"
-When the value is empty, clang-tidy will
-attempt to find a file named .clang-tidy for
-each source file in its parent directories.
-)"),
- cl::init(""), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> ConfigFile("config-file", desc(R"(
-Specify the path of .clang-tidy or custom config file:
- e.g. --config-file=/some/path/myTidyConfigFile
-This option internally works exactly the same way as
- --config option after reading specified config file.
-Use either --config-file or --config, not both.
-)"),
- cl::init(""),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> DumpConfig("dump-config", desc(R"(
-Dumps configuration in the YAML format to
-stdout. This option can be used along with a
-file name (and '--' if the file is outside of a
-project with configured compilation database).
-The configuration used for this file will be
-printed.
-Use along with -checks=* to include
-configuration of all checks.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> EnableCheckProfile("enable-check-profile", desc(R"(
-Enable per-check timing profiles, and print a
-report to stderr.
-)"),
- cl::init(false),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> StoreCheckProfile("store-check-profile", desc(R"(
-By default reports are printed in tabulated
-format to stderr. When this option is passed,
-these per-TU profiles are instead stored as JSON.
-)"),
- cl::value_desc("prefix"),
- cl::cat(ClangTidyCategory));
-
-/// This option allows enabling the experimental alpha checkers from the static
-/// analyzer. This option is set to false and not visible in help, because it is
-/// highly not recommended for users.
-static cl::opt<bool>
- AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers",
- cl::init(false), cl::Hidden,
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> EnableModuleHeadersParsing("enable-module-headers-parsing",
- desc(R"(
-Enables preprocessor-level module header parsing
-for C++20 and above, empowering specific checks
-to detect macro definitions within modules. This
-feature may cause performance and parsing issues
-and is therefore considered experimental.
-)"),
- cl::init(false),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> ExportFixes("export-fixes", desc(R"(
-YAML file to store suggested fixes in. The
-stored fixes can be applied to the input source
-code with clang-apply-replacements.
-)"),
- cl::value_desc("filename"),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> Quiet("quiet", desc(R"(
-Run clang-tidy in quiet mode. This suppresses
-printing statistics about ignored warnings and
-warnings treated as errors if the respective
-options are specified.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<std::string> VfsOverlay("vfsoverlay", desc(R"(
-Overlay the virtual filesystem described by file
-over the real file system.
-)"),
- cl::value_desc("filename"),
- cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> UseColor("use-color", desc(R"(
-Use colors in diagnostics. If not set, colors
-will be used if the terminal connected to
-standard output supports colors.
-This option overrides the 'UseColor' option in
-.clang-tidy file, if any.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> VerifyConfig("verify-config", desc(R"(
-Check the config files to ensure each check and
-option is recognized without running any checks.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> AllowNoChecks("allow-no-checks", desc(R"(
-Allow empty enabled checks. This suppresses
-the "no checks enabled" error when disabling
-all of the checks.
-)"),
- cl::init(false), cl::cat(ClangTidyCategory));
-
-static cl::opt<bool> ExperimentalCustomChecks("experimental-custom-checks",
- desc(R"(
-Enable experimental clang-query based
-custom checks.
-see https://clang.llvm.org/extra/clang-tidy/QueryBasedCustomChecks.html.
-)"),
- cl::init(false),
- cl::cat(ClangTidyCategory));
-
-static cl::list<std::string> RemovedArgs("removed-arg", desc(R"(
-List of arguments to remove from the command
-line sent to the compiler. Please note that
-removing arguments might change the semantic
-of the analyzed code, possibly leading to
-compiler errors, false positives or
-false negatives. This option is applied
-before --extra-arg and --extra-arg-before)"),
- cl::cat(ClangTidyCategory));
+static std::string Checks;
+static bool ChecksSpecified;
+static std::string WarningsAsErrors;
+static bool WarningsAsErrorsSpecified;
+static std::string HeaderFilter;
+static bool HeaderFilterSpecified;
+static std::string ExcludeHeaderFilter;
+static bool ExcludeHeaderFilterSpecified;
+static bool SystemHeaders;
+static bool SystemHeadersSpecified;
+static std::string LineFilter;
+static bool Fix;
+static bool FixErrors;
+static bool FixNotes;
+static std::string FormatStyle;
+static bool FormatStyleSpecified;
+static bool ListChecks;
+static bool ExplainConfig;
+static std::string Config;
+static bool ConfigSpecified;
+static std::string ConfigFile;
+static bool ConfigFileSpecified;
+static bool DumpConfig;
+static bool EnableCheckProfile;
+static std::string StoreCheckProfile;
+static bool AllowEnablingAnalyzerAlphaCheckers;
+static bool EnableModuleHeadersParsing;
+static std::string ExportFixes;
+static bool Quiet;
+static std::string VfsOverlay;
+static bool UseColor;
+static bool UseColorSpecified;
+static bool VerifyConfig;
+static bool AllowNoChecks;
+static bool ExperimentalCustomChecks;
+static std::vector<std::string> RemovedArgs;
+static bool RemovedArgsSpecified;
+
+static void printHelp(bool ShowHidden = false) {
+ ClangTidyOptTable Tbl;
+ Tbl.printHelp(outs(),
+ "clang-tidy [options] <source0> [... <sourceN>] "
+ "[-- <compiler arguments>]",
+ "clang-tidy", ShowHidden);
+ outs() << CommonHelp << ClangTidyParameterFileHelp << ClangTidyHelp;
+}
+
+static bool parseBoolArg(const opt::Arg *A, unsigned ValueID, bool &Value) {
+ if (!A->getOption().matches(ValueID)) {
+ Value = true;
+ return true;
+ }
+ std::optional<bool> Parsed = StringSwitch<std::optional<bool>>(A->getValue())
+ .CaseLower("true", true)
+ .Case("1", true)
+ .CaseLower("false", false)
+ .Case("0", false)
+ .Default(std::nullopt);
+ if (Parsed) {
+ Value = *Parsed;
+ return true;
+ }
+ errs() << "clang-tidy: invalid value '" << A->getValue() << "' for option '"
+ << A->getSpelling() << "'\n";
+ return false;
+}
+
+static bool parseBoolArg(const opt::InputArgList &Args, unsigned FlagID,
+ unsigned ValueID, bool &Value,
+ bool *Specified = nullptr) {
+ const opt::Arg *A = Args.getLastArg(FlagID, ValueID);
+ if (Specified)
+ *Specified = A != nullptr;
+ Value = false;
+ return !A || parseBoolArg(A, ValueID, Value);
+}
+
+static bool parseCommandLine(int argc, char **argv, BumpPtrAllocator &Allocator,
+ StringSaver &Saver, ClangTidyOptTable &Tbl,
+ opt::InputArgList &Args,
+ std::unique_ptr<CompilationDatabase> &Compilations,
+ std::vector<std::string> &SourcePaths) {
+ SmallVector<const char *> ExpandedArgs(argv, argv + argc);
+ cl::TokenizerCallback Tokenizer =
+ Triple(sys::getProcessTriple()).isOSWindows()
+ ? cl::TokenizeWindowsCommandLine
+ : cl::TokenizeGNUCommandLine;
+ cl::ExpansionContext ECtx(Allocator, Tokenizer);
+ if (Error Err = ECtx.expandResponseFiles(ExpandedArgs)) {
+ WithColor::error() << toString(std::move(Err)) << "\n";
+ return false;
+ }
+
+ int ToolArgc = static_cast<int>(ExpandedArgs.size());
+ std::string FixedDatabaseError;
+ Compilations = FixedCompilationDatabase::loadFromCommandLine(
+ ToolArgc, ExpandedArgs.data(), FixedDatabaseError);
+
+ SmallVector<char *> ToolArgv;
+ ToolArgv.reserve(ToolArgc);
+ for (const char *Arg : ArrayRef(ExpandedArgs).take_front(ToolArgc))
+ ToolArgv.push_back(const_cast<char *>(Arg));
+
+ bool HasError = false;
+ Args = Tbl.parseArgs(ToolArgc, ToolArgv.data(), OPT_UNKNOWN, Saver,
+ [&](StringRef Message) {
+ errs() << "clang-tidy: " << Message << '\n';
+ HasError = true;
+ });
+ if (HasError) {
+ if (!FixedDatabaseError.empty())
+ errs() << FixedDatabaseError;
+ return false;
+ }
+
+ Checks = Args.getLastArgValue(OPT_checks_EQ);
+ ChecksSpecified = Args.hasArg(OPT_checks_EQ);
+ WarningsAsErrors = Args.getLastArgValue(OPT_warnings_as_errors_EQ);
+ WarningsAsErrorsSpecified = Args.hasArg(OPT_warnings_as_errors_EQ);
+ HeaderFilter = Args.getLastArgValue(OPT_header_filter_EQ, ".*");
+ HeaderFilterSpecified = Args.hasArg(OPT_header_filter_EQ);
+ ExcludeHeaderFilter = Args.getLastArgValue(OPT_exclude_header_filter_EQ);
+ ExcludeHeaderFilter...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/201743
More information about the cfe-commits
mailing list