[clang] [llvm] Convert clang-format to Opt and add multicall support (PR #201730)
David Zbarsky via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 4 19:16:24 PDT 2026
https://github.com/dzbarsky updated https://github.com/llvm/llvm-project/pull/201730
>From cfcbe11ee07f58a5498f1bf0af9f649570cebbee Mon Sep 17 00:00:00 2001
From: David Zbarsky <dzbarsky at gmail.com>
Date: Thu, 4 Jun 2026 21:47:06 -0400
Subject: [PATCH] Convert clang-format to Opt and add multicall support
---
clang/docs/ClangFormat.rst | 172 +++----
clang/tools/clang-format/CMakeLists.txt | 13 +-
clang/tools/clang-format/ClangFormat.cpp | 432 +++++++++---------
clang/tools/clang-format/Opts.td | 154 +++++++
.../llvm-project-overlay/clang/BUILD.bazel | 22 +-
.../llvm-project-overlay/llvm/driver.bzl | 1 +
6 files changed, 498 insertions(+), 296 deletions(-)
create mode 100644 clang/tools/clang-format/Opts.td
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index 26490f9c15bb8..e7fac3e2eaad6 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -29,92 +29,92 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
USAGE: clang-format [options] [@<file>] [<file> ...]
OPTIONS:
-
- Clang-format options:
-
- --Werror - If set, changes formatting warnings to errors
- --Wno-error=<value> - If set, don't error out on the specified warning type.
- =unknown - If set, unknown format options are only warned about.
- This can be used to enable formatting, even if the
- configuration contains unknown (newer) options.
- Use with caution, as this might lead to dramatically
- differing format depending on an option being
- supported or not.
- --assume-filename=<string> - Set filename used to determine the language and to find
- .clang-format file.
- Only used when reading from stdin.
- If this is not passed, the .clang-format file is searched
- relative to the current working directory when reading stdin.
- Unrecognized filenames are treated as C++.
- supported:
- CSharp: .cs
- Java: .java
- JavaScript: .js .mjs .cjs .ts
- JSON: .json .ipynb
- Objective-C: .m .mm
- Proto: .proto .protodevel
- TableGen: .td
- TextProto: .txtpb .textpb .pb.txt .textproto .asciipb
- Verilog: .sv .svh .v .vh
- --cursor=<uint> - The position of the cursor when invoking
- clang-format from an editor integration
- --dry-run - If set, do not actually make the formatting changes
- --dump-config - Dump configuration options to stdout and exit.
- Can be used with -style option.
- --fail-on-incomplete-format - If set, fail with exit code 1 on incomplete format.
- --fallback-style=<string> - The name of the predefined style used as a
- fallback in case clang-format is invoked with
- -style=file, but can not find the .clang-format
- file to use. Defaults to 'LLVM'.
- Use -fallback-style=none to skip formatting.
- --ferror-limit=<uint> - Set the maximum number of clang-format errors to emit
- before stopping (0 = no limit).
- Used only with --dry-run or -n
- --files=<filename> - A file containing a list of files to process, one per line.
- -i - Inplace edit <file>s, if specified.
- --length=<uint> - Format a range of this length (in bytes).
- Multiple ranges can be formatted by specifying
- several -offset and -length pairs.
- When only a single -offset is specified without
- -length, clang-format will format up to the end
- of the file.
- Can only be used with one input file.
- --lines=<string> - <start line>:<end line> - format a range of
- lines (both 1-based).
- Multiple ranges can be formatted by specifying
- several -lines arguments.
- Can't be used with -offset and -length.
- Can only be used with one input file.
- -n - Alias for --dry-run
- --offset=<uint> - Format a range starting at this byte offset.
- Multiple ranges can be formatted by specifying
- several -offset and -length pairs.
- Can only be used with one input file.
- --output-replacements-xml - Output replacements as XML.
- --qualifier-alignment=<string> - If set, overrides the qualifier alignment style
- determined by the QualifierAlignment style flag
- --sort-includes - If set, overrides the include sorting behavior
- determined by the SortIncludes style flag
- --style=<string> - Set coding style. <string> can be:
- 1. A preset: LLVM, GNU, Google, Chromium, Microsoft,
- Mozilla, WebKit.
- 2. 'file' to load style configuration from a
- .clang-format file in one of the parent directories
- of the source file (for stdin, see --assume-filename).
- If no .clang-format file is found, falls back to
- --fallback-style.
- --style=file is the default.
- 3. 'file:<format_file_path>' to explicitly specify
- the configuration file.
- 4. "{key: value, ...}" to set specific parameters, e.g.:
- --style="{BasedOnStyle: llvm, IndentWidth: 8}"
- --verbose - If set, shows the list of processed files
-
- Generic Options:
-
- --help - Display available options (--help-hidden for more)
- --help-list - Display list of available options (--help-list-hidden for more)
- --version - Display the version of this program
+ -assume-filename=<string>
+ Set filename used to determine the language and to find
+ .clang-format file.
+ Only used when reading from stdin.
+ If this is not passed, the .clang-format file is searched
+ relative to the current working directory when reading stdin.
+ Unrecognized filenames are treated as C++.
+ supported:
+ CSharp: .cs
+ Java: .java
+ JavaScript: .js .mjs .cjs .ts
+ JSON: .json .ipynb
+ Objective-C: .m .mm
+ Proto: .proto .protodevel
+ TableGen: .td
+ TextProto: .txtpb .textpb .pb.txt .textproto .asciipb
+ Verilog: .sv .svh .v .vh
+ -cursor=<uint> The position of the cursor when invoking
+ clang-format from an editor integration
+ -dry-run If set, do not actually make the formatting changes
+ -dump-config Dump configuration options to stdout and exit.
+ Can be used with -style option.
+ -fail-on-incomplete-format
+ If set, fail with exit code 1 on incomplete format.
+ -fallback-style=<string>
+ The name of the predefined style used as a
+ fallback in case clang-format is invoked with
+ -style=file, but can not find the .clang-format
+ file to use. Defaults to 'LLVM'.
+ Use -fallback-style=none to skip formatting.
+ -ferror-limit=<uint> Set the maximum number of clang-format errors to emit
+ before stopping (0 = no limit).
+ Used only with --dry-run or -n
+ -files=<filename> A file containing a list of files to process, one per line.
+ -help-hidden Display all available options, including hidden options
+ -help Display available options
+ -i Inplace edit <file>s, if specified.
+ -length=<uint> Format a range of this length (in bytes).
+ Multiple ranges can be formatted by specifying
+ several -offset and -length pairs.
+ When only a single -offset is specified without
+ -length, clang-format will format up to the end
+ of the file.
+ Can only be used with one input file.
+ -lines=<start line>:<end line>
+ Format a range of lines (both 1-based).
+ Multiple ranges can be formatted by specifying
+ several -lines arguments.
+ Can't be used with -offset and -length.
+ Can only be used with one input file.
+ -n Alias for --dry-run
+ -offset=<uint> Format a range starting at this byte offset.
+ Multiple ranges can be formatted by specifying
+ several -offset and -length pairs.
+ Can only be used with one input file.
+ -output-replacements-xml
+ Output replacements as XML.
+ -qualifier-alignment=<string>
+ If set, overrides the qualifier alignment style
+ determined by the QualifierAlignment style flag
+ -sort-includes If set, overrides the include sorting behavior
+ determined by the SortIncludes style flag
+ -style=<string> Set coding style. <string> can be:
+ 1. A preset: LLVM, GNU, Google, Chromium, Microsoft,
+ Mozilla, WebKit.
+ 2. 'file' to load style configuration from a
+ .clang-format file in one of the parent directories
+ of the source file (for stdin, see --assume-filename).
+ If no .clang-format file is found, falls back to
+ --fallback-style.
+ --style=file is the default.
+ 3. 'file:<format_file_path>' to explicitly specify
+ the configuration file.
+ 4. "{key: value, ...}" to set specific parameters, e.g.:
+ --style="{BasedOnStyle: llvm, IndentWidth: 8}"
+ -verbose If set, shows the list of processed files
+ -version Display the version of this program
+ -Werror If set, changes formatting warnings to errors
+ -Wno-error=<warning> If set, don't error out on the specified warning type.
+ Supported warnings:
+ unknown - unknown format options are only warned about.
+ This can be used to enable formatting, even if the
+ configuration contains unknown (newer) options.
+ Use with caution, as this might lead to dramatically
+ differing format depending on an option being
+ supported or not.
.. END_FORMAT_HELP
diff --git a/clang/tools/clang-format/CMakeLists.txt b/clang/tools/clang-format/CMakeLists.txt
index 1c61a3c8fb803..5a1e53d3aa99b 100644
--- a/clang/tools/clang-format/CMakeLists.txt
+++ b/clang/tools/clang-format/CMakeLists.txt
@@ -1,7 +1,18 @@
-set(LLVM_LINK_COMPONENTS support)
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(ClangFormatOptsTableGen)
add_clang_tool(clang-format
ClangFormat.cpp
+
+ DEPENDS
+ ClangFormatOptsTableGen
+ GENERATE_DRIVER
)
set(CLANG_FORMAT_LIB_DEPS
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index 37d0eb83414f4..f4bf01fbc4e39 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -21,198 +21,80 @@
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/Support/CommandLine.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Allocator.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/InitLLVM.h"
-#include "llvm/Support/Process.h"
+#include "llvm/Support/LLVMDriver.h"
+#include "llvm/Support/StringSaver.h"
#include <fstream>
+#include <optional>
+#include <string>
+#include <vector>
using namespace llvm;
using clang::tooling::Replacements;
-static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
-
-// Mark all our options with this category, everything else (except for -version
-// and -help) will be hidden.
-static cl::OptionCategory ClangFormatCategory("Clang-format options");
-
-static cl::list<unsigned>
- Offsets("offset",
- cl::desc("Format a range starting at this byte offset.\n"
- "Multiple ranges can be formatted by specifying\n"
- "several -offset and -length pairs.\n"
- "Can only be used with one input file."),
- cl::cat(ClangFormatCategory));
-static cl::list<unsigned>
- Lengths("length",
- cl::desc("Format a range of this length (in bytes).\n"
- "Multiple ranges can be formatted by specifying\n"
- "several -offset and -length pairs.\n"
- "When only a single -offset is specified without\n"
- "-length, clang-format will format up to the end\n"
- "of the file.\n"
- "Can only be used with one input file."),
- cl::cat(ClangFormatCategory));
-static cl::list<std::string>
- LineRanges("lines",
- cl::desc("<start line>:<end line> - format a range of\n"
- "lines (both 1-based).\n"
- "Multiple ranges can be formatted by specifying\n"
- "several -lines arguments.\n"
- "Can't be used with -offset and -length.\n"
- "Can only be used with one input file."),
- cl::cat(ClangFormatCategory));
-static cl::opt<std::string>
- Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
- cl::init(clang::format::DefaultFormatStyle),
- cl::cat(ClangFormatCategory));
-static cl::opt<std::string>
- FallbackStyle("fallback-style",
- cl::desc("The name of the predefined style used as a\n"
- "fallback in case clang-format is invoked with\n"
- "-style=file, but can not find the .clang-format\n"
- "file to use. Defaults to 'LLVM'.\n"
- "Use -fallback-style=none to skip formatting."),
- cl::init(clang::format::DefaultFallbackStyle),
- cl::cat(ClangFormatCategory));
-
-static cl::opt<std::string> AssumeFileName(
- "assume-filename",
- cl::desc("Set filename used to determine the language and to find\n"
- ".clang-format file.\n"
- "Only used when reading from stdin.\n"
- "If this is not passed, the .clang-format file is searched\n"
- "relative to the current working directory when reading stdin.\n"
- "Unrecognized filenames are treated as C++.\n"
- "supported:\n"
- " CSharp: .cs\n"
- " Java: .java\n"
- " JavaScript: .js .mjs .cjs .ts\n"
- " JSON: .json .ipynb\n"
- " Objective-C: .m .mm\n"
- " Proto: .proto .protodevel\n"
- " TableGen: .td\n"
- " TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"
- " Verilog: .sv .svh .v .vh"),
- cl::init("<stdin>"), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> Inplace("i",
- cl::desc("Inplace edit <file>s, if specified."),
- cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> OutputXML("output-replacements-xml",
- cl::desc("Output replacements as XML."),
- cl::cat(ClangFormatCategory));
-static cl::opt<bool>
- DumpConfig("dump-config",
- cl::desc("Dump configuration options to stdout and exit.\n"
- "Can be used with -style option."),
- cl::cat(ClangFormatCategory));
-static cl::opt<unsigned>
- Cursor("cursor",
- cl::desc("The position of the cursor when invoking\n"
- "clang-format from an editor integration"),
- cl::init(0), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool>
- SortIncludes("sort-includes",
- cl::desc("If set, overrides the include sorting behavior\n"
- "determined by the SortIncludes style flag"),
- cl::cat(ClangFormatCategory));
-
-static cl::opt<std::string> QualifierAlignment(
- "qualifier-alignment",
- cl::desc("If set, overrides the qualifier alignment style\n"
- "determined by the QualifierAlignment style flag"),
- cl::init(""), cl::cat(ClangFormatCategory));
-
-static cl::opt<std::string> Files(
- "files",
- cl::desc("A file containing a list of files to process, one per line."),
- cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool>
- Verbose("verbose", cl::desc("If set, shows the list of processed files"),
- cl::cat(ClangFormatCategory));
-
-// Use --dry-run to match other LLVM tools when you mean do it but don't
-// actually do it
-static cl::opt<bool>
- DryRun("dry-run",
- cl::desc("If set, do not actually make the formatting changes"),
- cl::cat(ClangFormatCategory));
-
-// Use -n as a common command as an alias for --dry-run. (git and make use -n)
-static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
- cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
- cl::NotHidden);
-
-// Emulate being able to turn on/off the warning.
-static cl::opt<bool>
- WarnFormat("Wclang-format-violations",
- cl::desc("Warnings about individual formatting changes needed. "
- "Used only with --dry-run or -n"),
- cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
-
-static cl::opt<bool>
- NoWarnFormat("Wno-clang-format-violations",
- cl::desc("Do not warn about individual formatting changes "
- "needed. Used only with --dry-run or -n"),
- cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
-
-static cl::opt<unsigned> ErrorLimit(
- "ferror-limit",
- cl::desc("Set the maximum number of clang-format errors to emit\n"
- "before stopping (0 = no limit).\n"
- "Used only with --dry-run or -n"),
- cl::init(0), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool>
- WarningsAsErrors("Werror",
- cl::desc("If set, changes formatting warnings to errors"),
- cl::cat(ClangFormatCategory));
-
namespace {
-enum class WNoError { Unknown };
-}
+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
+
+using namespace llvm::opt;
+static constexpr opt::OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "Opts.inc"
+#undef OPTION
+};
-static cl::bits<WNoError> WNoErrorList(
- "Wno-error",
- cl::desc("If set, don't error out on the specified warning type."),
- cl::values(
- clEnumValN(WNoError::Unknown, "unknown",
- "If set, unknown format options are only warned about.\n"
- "This can be used to enable formatting, even if the\n"
- "configuration contains unknown (newer) options.\n"
- "Use with caution, as this might lead to dramatically\n"
- "differing format depending on an option being\n"
- "supported or not.")),
- cl::cat(ClangFormatCategory));
-
-static cl::opt<bool>
- ShowColors("fcolor-diagnostics",
- cl::desc("If set, and on a color-capable terminal controls "
- "whether or not to print diagnostics in color"),
- cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
-
-static cl::opt<bool>
- NoShowColors("fno-color-diagnostics",
- cl::desc("If set, and on a color-capable terminal controls "
- "whether or not to print diagnostics in color"),
- cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
-
-static cl::list<std::string> FileNames(cl::Positional,
- cl::desc("[@<file>] [<file> ...]"),
- cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> FailOnIncompleteFormat(
- "fail-on-incomplete-format",
- cl::desc("If set, fail with exit code 1 on incomplete format."),
- cl::init(false), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> ListIgnored("list-ignored",
- cl::desc("List ignored files."),
- cl::cat(ClangFormatCategory), cl::Hidden);
+class ClangFormatOptTable : public opt::GenericOptTable {
+public:
+ ClangFormatOptTable()
+ : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
+ setDashDashParsing(true);
+ }
+};
+
+static std::vector<unsigned> Offsets;
+static std::vector<unsigned> Lengths;
+static std::vector<std::string> LineRanges;
+static std::string Style;
+static std::string FallbackStyle;
+static std::string AssumeFileName;
+static bool Inplace;
+static bool OutputXML;
+static bool DumpConfig;
+static unsigned Cursor;
+static bool CursorSpecified;
+static bool SortIncludes;
+static bool SortIncludesSpecified;
+static std::string QualifierAlignment;
+static std::string Files;
+static bool Verbose;
+static bool DryRun;
+static bool WarnFormat;
+static bool NoWarnFormat;
+static unsigned ErrorLimit;
+static bool WarningsAsErrors;
+static bool WNoErrorUnknown;
+static bool ShowColors;
+static bool NoShowColors;
+static std::vector<std::string> FileNames;
+static bool FailOnIncompleteFormat;
+static bool ListIgnored;
+} // namespace
namespace clang {
namespace format {
@@ -370,7 +252,6 @@ static bool emitReplacementWarnings(const Replacements &Replaces,
static void outputXML(const Replacements &Replaces,
const Replacements &FormatChanges,
const FormattingAttemptStatus &Status,
- const cl::opt<unsigned> &Cursor,
unsigned CursorPosition) {
outs() << "<?xml version='1.0'?>\n<replacements "
"xml:space='preserve' incomplete_format='"
@@ -378,7 +259,7 @@ static void outputXML(const Replacements &Replaces,
if (!Status.FormatComplete)
outs() << " line='" << Status.Line << "'";
outs() << ">\n";
- if (Cursor.getNumOccurrences() != 0) {
+ if (CursorSpecified) {
outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
<< "</cursor>\n";
}
@@ -444,7 +325,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
Expected<FormatStyle> FormatStyle =
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
- nullptr, WNoErrorList.isSet(WNoError::Unknown));
+ nullptr, WNoErrorUnknown);
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return true;
@@ -471,7 +352,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
}
- if (SortIncludes.getNumOccurrences() != 0) {
+ if (SortIncludesSpecified) {
FormatStyle->SortIncludes = {};
if (SortIncludes)
FormatStyle->SortIncludes.Enabled = true;
@@ -507,7 +388,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
emitReplacementWarnings(Replaces, AssumedFileName, std::move(Code));
}
if (OutputXML) {
- outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
+ outputXML(Replaces, FormatChanges, Status, CursorPosition);
} else {
auto InMemoryFileSystem =
makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
@@ -526,7 +407,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
if (Rewrite.overwriteChangedFiles())
return true;
} else {
- if (Cursor.getNumOccurrences() != 0) {
+ if (CursorSpecified) {
outs() << "{ \"Cursor\": "
<< FormatChanges.getShiftedCodePosition(CursorPosition)
<< ", \"IncompleteFormat\": "
@@ -548,6 +429,138 @@ static void PrintVersion(raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-format") << '\n';
}
+static bool parseUnsignedArgs(const opt::InputArgList &Args, OptSpecifier ID,
+ std::vector<unsigned> &Values) {
+ for (const opt::Arg *A : Args.filtered(ID)) {
+ unsigned Value;
+ if (!StringRef(A->getValue()).getAsInteger(0, Value)) {
+ Values.push_back(Value);
+ continue;
+ }
+ errs() << "clang-format: invalid value '" << A->getValue()
+ << "' for option '" << A->getSpelling() << "'\n";
+ return false;
+ }
+ return true;
+}
+
+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-format: invalid value '" << A->getValue() << "' for option '"
+ << A->getSpelling() << "'\n";
+ return false;
+}
+
+static bool parseBoolArg(const opt::InputArgList &Args, unsigned FlagID,
+ unsigned ValueID, bool Default, bool &Value,
+ bool *Specified = nullptr) {
+ const opt::Arg *A = Args.getLastArg(FlagID, ValueID);
+ if (Specified)
+ *Specified = A != nullptr;
+ if (!A) {
+ Value = Default;
+ return true;
+ }
+ return parseBoolArg(A, ValueID, Value);
+}
+
+static bool parseArgs(int argc, char **argv, StringSaver &Saver,
+ ClangFormatOptTable &Tbl, opt::InputArgList &Args) {
+ bool HasError = false;
+ Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ errs() << "clang-format: " << Msg << '\n';
+ HasError = true;
+ });
+ if (HasError)
+ return false;
+
+ Offsets.clear();
+ Lengths.clear();
+ LineRanges = Args.getAllArgValues(OPT_lines_EQ);
+ Style = Args.getLastArgValue(OPT_style_EQ, clang::format::DefaultFormatStyle);
+ FallbackStyle = Args.getLastArgValue(OPT_fallback_style_EQ,
+ clang::format::DefaultFallbackStyle);
+ AssumeFileName = Args.getLastArgValue(OPT_assume_filename_EQ, "<stdin>");
+ Cursor = 0;
+ CursorSpecified = Args.hasArg(OPT_cursor_EQ);
+ QualifierAlignment = Args.getLastArgValue(OPT_qualifier_alignment_EQ);
+ Files = Args.getLastArgValue(OPT_files_EQ);
+ ErrorLimit = 0;
+ WNoErrorUnknown = false;
+ FileNames = Args.getAllArgValues(OPT_INPUT);
+
+ if (!parseBoolArg(Args, OPT_i, OPT_i_EQ, false, Inplace) ||
+ !parseBoolArg(Args, OPT_output_replacements_xml,
+ OPT_output_replacements_xml_EQ, false, OutputXML) ||
+ !parseBoolArg(Args, OPT_dump_config, OPT_dump_config_EQ, false,
+ DumpConfig) ||
+ !parseBoolArg(Args, OPT_sort_includes, OPT_sort_includes_EQ, false,
+ SortIncludes, &SortIncludesSpecified) ||
+ !parseBoolArg(Args, OPT_verbose, OPT_verbose_EQ, false, Verbose) ||
+ !parseBoolArg(Args, OPT_dry_run, OPT_dry_run_EQ, false, DryRun) ||
+ !parseBoolArg(Args, OPT_Wclang_format_violations,
+ OPT_Wclang_format_violations_EQ, true, WarnFormat) ||
+ !parseBoolArg(Args, OPT_Wno_clang_format_violations,
+ OPT_Wno_clang_format_violations_EQ, false, NoWarnFormat) ||
+ !parseBoolArg(Args, OPT_Werror, OPT_Werror_EQ, false, WarningsAsErrors) ||
+ !parseBoolArg(Args, OPT_fcolor_diagnostics, OPT_fcolor_diagnostics_EQ,
+ true, ShowColors) ||
+ !parseBoolArg(Args, OPT_fno_color_diagnostics,
+ OPT_fno_color_diagnostics_EQ, false, NoShowColors) ||
+ !parseBoolArg(Args, OPT_fail_on_incomplete_format,
+ OPT_fail_on_incomplete_format_EQ, false,
+ FailOnIncompleteFormat) ||
+ !parseBoolArg(Args, OPT_list_ignored, OPT_list_ignored_EQ, false,
+ ListIgnored)) {
+ return false;
+ }
+
+ if (!parseUnsignedArgs(Args, OPT_offset_EQ, Offsets) ||
+ !parseUnsignedArgs(Args, OPT_length_EQ, Lengths)) {
+ return false;
+ }
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_cursor_EQ)) {
+ if (StringRef(A->getValue()).getAsInteger(0, Cursor)) {
+ errs() << "clang-format: invalid value '" << A->getValue()
+ << "' for option '" << A->getSpelling() << "'\n";
+ return false;
+ }
+ }
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_ferror_limit_EQ)) {
+ if (StringRef(A->getValue()).getAsInteger(0, ErrorLimit)) {
+ errs() << "clang-format: invalid value '" << A->getValue()
+ << "' for option '" << A->getSpelling() << "'\n";
+ return false;
+ }
+ }
+
+ for (StringRef Warning : Args.getAllArgValues(OPT_Wno_error_EQ)) {
+ if (Warning == "unknown") {
+ WNoErrorUnknown = true;
+ continue;
+ }
+ errs() << "clang-format: invalid value '" << Warning
+ << "' for option '-Wno-error'\n";
+ return false;
+ }
+ return true;
+}
+
// Dump the configuration.
static int dumpConfig() {
std::unique_ptr<llvm::MemoryBuffer> Code;
@@ -664,24 +677,31 @@ static bool isIgnored(StringRef FilePath) {
return false;
}
-int main(int argc, const char **argv) {
- InitLLVM X(argc, argv);
-
- cl::HideUnrelatedOptions(ClangFormatCategory);
+int clang_format_main(int argc, char **argv, const llvm::ToolContext &) {
+ BumpPtrAllocator Alloc;
+ StringSaver Saver(Alloc);
+ ClangFormatOptTable Tbl;
+ opt::InputArgList Args;
+ if (!parseArgs(argc, argv, Saver, Tbl, Args))
+ return 1;
- cl::SetVersionPrinter(PrintVersion);
- cl::ParseCommandLineOptions(
- argc, argv,
- "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
- "code.\n\n"
- "If no arguments are specified, it formats the code from standard input\n"
- "and writes the result to the standard output.\n"
- "If <file>s are given, it reformats the files. If -i is specified\n"
- "together with <file>s, the files are edited in-place. Otherwise, the\n"
- "result is written to the standard output.\n");
+ if (Args.hasArg(OPT_help, OPT_help_hidden)) {
+ Tbl.printHelp(
+ outs(), "clang-format [options] [@<file>] [<file> ...]",
+ "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
+ "code.\n\n"
+ "If no arguments are specified, it formats the code from standard "
+ "input\n"
+ "and writes the result to the standard output.\n"
+ "If <file>s are given, it reformats the files. If -i is specified\n"
+ "together with <file>s, the files are edited in-place. Otherwise, the\n"
+ "result is written to the standard output.",
+ Args.hasArg(OPT_help_hidden));
+ return 0;
+ }
- if (Help) {
- cl::PrintHelpMessage();
+ if (Args.hasArg(OPT_version)) {
+ PrintVersion(outs());
return 0;
}
diff --git a/clang/tools/clang-format/Opts.td b/clang/tools/clang-format/Opts.td
new file mode 100644
index 0000000000000..48f01ece0b51b
--- /dev/null
+++ b/clang/tools/clang-format/Opts.td
@@ -0,0 +1,154 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name, string help>
+ : Flag<["-", "--"], name>, HelpText<help>;
+
+multiclass B<string name, string help> {
+ def NAME : F<name, help>;
+ def NAME #_EQ : Joined<["-", "--"], name #"=">,
+ MetaVarName<"<true|false>">,
+ Flags<[HelpHidden]>;
+}
+
+multiclass BH<string name, string help> {
+ def NAME : F<name, help>, Flags<[HelpHidden]>;
+ def NAME #_EQ : Joined<["-", "--"], name #"=">,
+ MetaVarName<"<true|false>">,
+ Flags<[HelpHidden]>;
+}
+
+multiclass Eq<string name, string metavar, string help> {
+ def NAME #_EQ : Joined<["-", "--"], name #"=">,
+ HelpText<help>, MetaVarName<metavar>;
+ def : Separate<["-", "--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def help : F<"help", "Display available options">;
+def help_hidden : F<"help-hidden",
+ "Display all available options, including hidden options">;
+def version : F<"version", "Display the version of this program">;
+def h : F<"h", "Alias for --help">, Alias<help>, Flags<[HelpHidden]>;
+
+defm offset : Eq<"offset", "<uint>",
+ "Format a range starting at this byte offset.\n"
+ "Multiple ranges can be formatted by specifying\n"
+ "several -offset and -length pairs.\n"
+ "Can only be used with one input file.">;
+defm length : Eq<"length", "<uint>",
+ "Format a range of this length (in bytes).\n"
+ "Multiple ranges can be formatted by specifying\n"
+ "several -offset and -length pairs.\n"
+ "When only a single -offset is specified without\n"
+ "-length, clang-format will format up to the end\n"
+ "of the file.\n"
+ "Can only be used with one input file.">;
+defm lines : Eq<"lines", "<start line>:<end line>",
+ "Format a range of lines (both 1-based).\n"
+ "Multiple ranges can be formatted by specifying\n"
+ "several -lines arguments.\n"
+ "Can't be used with -offset and -length.\n"
+ "Can only be used with one input file.">;
+defm style : Eq<"style", "<string>",
+ "Set coding style. <string> can be:\n"
+ "1. A preset: LLVM, GNU, Google, Chromium, Microsoft,\n"
+ " Mozilla, WebKit.\n"
+ "2. 'file' to load style configuration from a\n"
+ " .clang-format file in one of the parent directories\n"
+ " of the source file (for stdin, see --assume-filename).\n"
+ " If no .clang-format file is found, falls back to\n"
+ " --fallback-style.\n"
+ " --style=file is the default.\n"
+ "3. 'file:<format_file_path>' to explicitly specify\n"
+ " the configuration file.\n"
+ "4. \"{key: value, ...}\" to set specific parameters, e.g.:\n"
+ " --style=\"{BasedOnStyle: llvm, IndentWidth: 8}\"">;
+defm fallback_style : Eq<"fallback-style", "<string>",
+ "The name of the predefined style used as a\n"
+ "fallback in case clang-format is invoked with\n"
+ "-style=file, but can not find the .clang-format\n"
+ "file to use. Defaults to 'LLVM'.\n"
+ "Use -fallback-style=none to skip formatting.">;
+defm assume_filename : Eq<"assume-filename", "<string>",
+ "Set filename used to determine the language and to "
+ "find\n"
+ ".clang-format file.\n"
+ "Only used when reading from stdin.\n"
+ "If this is not passed, the .clang-format file is "
+ "searched\n"
+ "relative to the current working directory when "
+ "reading stdin.\n"
+ "Unrecognized filenames are treated as C++.\n"
+ "supported:\n"
+ " CSharp: .cs\n"
+ " Java: .java\n"
+ " JavaScript: .js .mjs .cjs .ts\n"
+ " JSON: .json .ipynb\n"
+ " Objective-C: .m .mm\n"
+ " Proto: .proto .protodevel\n"
+ " TableGen: .td\n"
+ " TextProto: .txtpb .textpb .pb.txt .textproto "
+ ".asciipb\n"
+ " Verilog: .sv .svh .v .vh">;
+
+defm i : B<"i", "Inplace edit <file>s, if specified.">;
+defm output_replacements_xml
+ : B<"output-replacements-xml", "Output replacements as XML.">;
+defm dump_config : B<"dump-config",
+ "Dump configuration options to stdout and exit.\n"
+ "Can be used with -style option.">;
+defm cursor : Eq<"cursor", "<uint>",
+ "The position of the cursor when invoking\n"
+ "clang-format from an editor integration">;
+defm sort_includes
+ : B<"sort-includes",
+ "If set, overrides the include sorting behavior\n"
+ "determined by the SortIncludes style flag">;
+defm qualifier_alignment
+ : Eq<"qualifier-alignment", "<string>",
+ "If set, overrides the qualifier alignment style\n"
+ "determined by the QualifierAlignment style flag">;
+defm files : Eq<"files", "<filename>",
+ "A file containing a list of files to process, one per line.">;
+defm verbose
+ : B<"verbose", "If set, shows the list of processed files">;
+defm dry_run
+ : B<"dry-run", "If set, do not actually make the formatting changes">;
+def n : F<"n", "Alias for --dry-run">, Alias<dry_run>;
+
+defm Wclang_format_violations
+ : BH<"Wclang-format-violations",
+ "Warnings about individual formatting changes needed. Used only with "
+ "--dry-run or -n">;
+defm Wno_clang_format_violations
+ : BH<"Wno-clang-format-violations",
+ "Do not warn about individual formatting changes needed. Used only "
+ "with --dry-run or -n">;
+defm ferror_limit
+ : Eq<"ferror-limit", "<uint>",
+ "Set the maximum number of clang-format errors to emit\n"
+ "before stopping (0 = no limit).\n"
+ "Used only with --dry-run or -n">;
+defm Werror
+ : B<"Werror", "If set, changes formatting warnings to errors">;
+defm Wno_error
+ : Eq<"Wno-error", "<warning>",
+ "If set, don't error out on the specified warning type.\n"
+ "Supported warnings:\n"
+ " unknown - unknown format options are only warned about.\n"
+ "This can be used to enable formatting, even if the\n"
+ "configuration contains unknown (newer) options.\n"
+ "Use with caution, as this might lead to dramatically\n"
+ "differing format depending on an option being\n"
+ "supported or not.">;
+defm fcolor_diagnostics
+ : BH<"fcolor-diagnostics",
+ "If set, and on a color-capable terminal controls whether or not to "
+ "print diagnostics in color">;
+defm fno_color_diagnostics
+ : BH<"fno-color-diagnostics",
+ "If set, and on a color-capable terminal controls whether or not to "
+ "print diagnostics in color">;
+defm fail_on_incomplete_format
+ : B<"fail-on-incomplete-format",
+ "If set, fail with exit code 1 on incomplete format.">;
+defm list_ignored : BH<"list-ignored", "List ignored files.">;
diff --git a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
index 819b6b9dcfbfd..a57334ae7557c 100644
--- a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
@@ -2402,22 +2402,38 @@ cc_binary(
],
)
-cc_binary(
- name = "clang-format",
+gentbl_cc_library(
+ name = "ClangFormatOptsTableGen",
+ strip_include_prefix = "tools/clang-format",
+ tbl_outs = {"tools/clang-format/Opts.inc": ["-gen-opt-parser-defs"]},
+ tblgen = "//llvm:llvm-tblgen",
+ td_file = "tools/clang-format/Opts.td",
+ deps = ["//llvm:OptParserTdFiles"],
+)
+
+cc_library(
+ name = "clang-format-lib",
srcs = [
"lib/Format/MatchFilePath.h",
"tools/clang-format/ClangFormat.cpp",
],
- stamp = 0,
deps = [
+ ":ClangFormatOptsTableGen",
":basic",
":format",
":frontend",
":rewrite",
+ "//llvm:Option",
"//llvm:Support",
],
)
+llvm_driver_cc_binary(
+ name = "clang-format",
+ stamp = 0,
+ deps = [":clang-format-lib"],
+)
+
cc_binary(
name = "clang-diff",
srcs = glob(["tools/clang-diff/*.cpp"]),
diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
index 478e0205aeed5..6b44d1b67f98e 100644
--- a/utils/bazel/llvm-project-overlay/llvm/driver.bzl
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -10,6 +10,7 @@ load("@rules_cc//cc:defs.bzl", "CcInfo", "cc_binary")
# Mapping from every tool to the cc_library that implements the tool's entrypoint.
_TOOLS = {
+ "clang-format": "//clang:clang-format-lib",
"clang-scan-deps": "//clang:clang-scan-deps-lib",
"clang": "//clang:clang-driver",
"dsymutil": "//llvm:dsymutil-lib",
More information about the cfe-commits
mailing list