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 <[email protected]> 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", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
