xref: /llvm-project/clang/tools/clang-format/ClangFormat.cpp (revision 2c94f75f00af16b6b4c2336e5cbfd8187d8bf33c)
1 //===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements a clang-format tool that automatically formats
11 /// (fragments of) C++ code.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "clang/Basic/Diagnostic.h"
16 #include "clang/Basic/DiagnosticOptions.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Basic/Version.h"
20 #include "clang/Format/Format.h"
21 #include "clang/Rewrite/Core/Rewriter.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/InitLLVM.h"
26 #include "llvm/Support/Process.h"
27 #include <fstream>
28 
29 using namespace llvm;
30 using clang::tooling::Replacements;
31 
32 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
33 
34 // Mark all our options with this category, everything else (except for -version
35 // and -help) will be hidden.
36 static cl::OptionCategory ClangFormatCategory("Clang-format options");
37 
38 static cl::list<unsigned>
39     Offsets("offset",
40             cl::desc("Format a range starting at this byte offset.\n"
41                      "Multiple ranges can be formatted by specifying\n"
42                      "several -offset and -length pairs.\n"
43                      "Can only be used with one input file."),
44             cl::cat(ClangFormatCategory));
45 static cl::list<unsigned>
46     Lengths("length",
47             cl::desc("Format a range of this length (in bytes).\n"
48                      "Multiple ranges can be formatted by specifying\n"
49                      "several -offset and -length pairs.\n"
50                      "When only a single -offset is specified without\n"
51                      "-length, clang-format will format up to the end\n"
52                      "of the file.\n"
53                      "Can only be used with one input file."),
54             cl::cat(ClangFormatCategory));
55 static cl::list<std::string>
56     LineRanges("lines",
57                cl::desc("<start line>:<end line> - format a range of\n"
58                         "lines (both 1-based).\n"
59                         "Multiple ranges can be formatted by specifying\n"
60                         "several -lines arguments.\n"
61                         "Can't be used with -offset and -length.\n"
62                         "Can only be used with one input file."),
63                cl::cat(ClangFormatCategory));
64 static cl::opt<std::string>
65     Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
66           cl::init(clang::format::DefaultFormatStyle),
67           cl::cat(ClangFormatCategory));
68 static cl::opt<std::string>
69     FallbackStyle("fallback-style",
70                   cl::desc("The name of the predefined style used as a\n"
71                            "fallback in case clang-format is invoked with\n"
72                            "-style=file, but can not find the .clang-format\n"
73                            "file to use. Defaults to 'LLVM'.\n"
74                            "Use -fallback-style=none to skip formatting."),
75                   cl::init(clang::format::DefaultFallbackStyle),
76                   cl::cat(ClangFormatCategory));
77 
78 static cl::opt<std::string> AssumeFileName(
79     "assume-filename",
80     cl::desc("Set filename used to determine the language and to find\n"
81              ".clang-format file.\n"
82              "Only used when reading from stdin.\n"
83              "If this is not passed, the .clang-format file is searched\n"
84              "relative to the current working directory when reading stdin.\n"
85              "Unrecognized filenames are treated as C++.\n"
86              "supported:\n"
87              "  CSharp: .cs\n"
88              "  Java: .java\n"
89              "  JavaScript: .mjs .js .ts\n"
90              "  Json: .json\n"
91              "  Objective-C: .m .mm\n"
92              "  Proto: .proto .protodevel\n"
93              "  TableGen: .td\n"
94              "  TextProto: .textpb .pb.txt .textproto .asciipb\n"
95              "  Verilog: .sv .svh .v .vh"),
96     cl::init("<stdin>"), cl::cat(ClangFormatCategory));
97 
98 static cl::opt<bool> Inplace("i",
99                              cl::desc("Inplace edit <file>s, if specified."),
100                              cl::cat(ClangFormatCategory));
101 
102 static cl::opt<bool> OutputXML("output-replacements-xml",
103                                cl::desc("Output replacements as XML."),
104                                cl::cat(ClangFormatCategory));
105 static cl::opt<bool>
106     DumpConfig("dump-config",
107                cl::desc("Dump configuration options to stdout and exit.\n"
108                         "Can be used with -style option."),
109                cl::cat(ClangFormatCategory));
110 static cl::opt<unsigned>
111     Cursor("cursor",
112            cl::desc("The position of the cursor when invoking\n"
113                     "clang-format from an editor integration"),
114            cl::init(0), cl::cat(ClangFormatCategory));
115 
116 static cl::opt<bool>
117     SortIncludes("sort-includes",
118                  cl::desc("If set, overrides the include sorting behavior\n"
119                           "determined by the SortIncludes style flag"),
120                  cl::cat(ClangFormatCategory));
121 
122 static cl::opt<std::string> QualifierAlignment(
123     "qualifier-alignment",
124     cl::desc("If set, overrides the qualifier alignment style\n"
125              "determined by the QualifierAlignment style flag"),
126     cl::init(""), cl::cat(ClangFormatCategory));
127 
128 static cl::opt<std::string> Files(
129     "files",
130     cl::desc("A file containing a list of files to process, one per line."),
131     cl::value_desc("filename"),
132     cl::init(""), cl::cat(ClangFormatCategory));
133 
134 static cl::opt<bool>
135     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
136             cl::cat(ClangFormatCategory));
137 
138 // Use --dry-run to match other LLVM tools when you mean do it but don't
139 // actually do it
140 static cl::opt<bool>
141     DryRun("dry-run",
142            cl::desc("If set, do not actually make the formatting changes"),
143            cl::cat(ClangFormatCategory));
144 
145 // Use -n as a common command as an alias for --dry-run. (git and make use -n)
146 static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
147                              cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
148                              cl::NotHidden);
149 
150 // Emulate being able to turn on/off the warning.
151 static cl::opt<bool>
152     WarnFormat("Wclang-format-violations",
153                cl::desc("Warnings about individual formatting changes needed. "
154                         "Used only with --dry-run or -n"),
155                cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
156 
157 static cl::opt<bool>
158     NoWarnFormat("Wno-clang-format-violations",
159                  cl::desc("Do not warn about individual formatting changes "
160                           "needed. Used only with --dry-run or -n"),
161                  cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
162 
163 static cl::opt<unsigned> ErrorLimit(
164     "ferror-limit",
165     cl::desc("Set the maximum number of clang-format errors to emit\n"
166              "before stopping (0 = no limit).\n"
167              "Used only with --dry-run or -n"),
168     cl::init(0), cl::cat(ClangFormatCategory));
169 
170 static cl::opt<bool>
171     WarningsAsErrors("Werror",
172                      cl::desc("If set, changes formatting warnings to errors"),
173                      cl::cat(ClangFormatCategory));
174 
175 namespace {
176 enum class WNoError { Unknown };
177 }
178 
179 static cl::bits<WNoError> WNoErrorList(
180     "Wno-error",
181     cl::desc("If set don't error out on the specified warning type."),
182     cl::values(
183         clEnumValN(WNoError::Unknown, "unknown",
184                    "If set, unknown format options are only warned about.\n"
185                    "This can be used to enable formatting, even if the\n"
186                    "configuration contains unknown (newer) options.\n"
187                    "Use with caution, as this might lead to dramatically\n"
188                    "differing format depending on an option being\n"
189                    "supported or not.")),
190     cl::cat(ClangFormatCategory));
191 
192 static cl::opt<bool>
193     ShowColors("fcolor-diagnostics",
194                cl::desc("If set, and on a color-capable terminal controls "
195                         "whether or not to print diagnostics in color"),
196                cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
197 
198 static cl::opt<bool>
199     NoShowColors("fno-color-diagnostics",
200                  cl::desc("If set, and on a color-capable terminal controls "
201                           "whether or not to print diagnostics in color"),
202                  cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
203 
204 static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
205                                        cl::cat(ClangFormatCategory));
206 
207 namespace clang {
208 namespace format {
209 
210 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
211                                  SourceManager &Sources, FileManager &Files,
212                                  llvm::vfs::InMemoryFileSystem *MemFS) {
213   MemFS->addFileNoOwn(FileName, 0, Source);
214   auto File = Files.getOptionalFileRef(FileName);
215   assert(File && "File not added to MemFS?");
216   return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
217 }
218 
219 // Parses <start line>:<end line> input to a pair of line numbers.
220 // Returns true on error.
221 static bool parseLineRange(StringRef Input, unsigned &FromLine,
222                            unsigned &ToLine) {
223   std::pair<StringRef, StringRef> LineRange = Input.split(':');
224   return LineRange.first.getAsInteger(0, FromLine) ||
225          LineRange.second.getAsInteger(0, ToLine);
226 }
227 
228 static bool fillRanges(MemoryBuffer *Code,
229                        std::vector<tooling::Range> &Ranges) {
230   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
231       new llvm::vfs::InMemoryFileSystem);
232   FileManager Files(FileSystemOptions(), InMemoryFileSystem);
233   DiagnosticsEngine Diagnostics(
234       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
235       new DiagnosticOptions);
236   SourceManager Sources(Diagnostics, Files);
237   FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
238                                  InMemoryFileSystem.get());
239   if (!LineRanges.empty()) {
240     if (!Offsets.empty() || !Lengths.empty()) {
241       errs() << "error: cannot use -lines with -offset/-length\n";
242       return true;
243     }
244 
245     for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
246       unsigned FromLine, ToLine;
247       if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
248         errs() << "error: invalid <start line>:<end line> pair\n";
249         return true;
250       }
251       if (FromLine < 1) {
252         errs() << "error: start line should be at least 1\n";
253         return true;
254       }
255       if (FromLine > ToLine) {
256         errs() << "error: start line should not exceed end line\n";
257         return true;
258       }
259       SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
260       SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
261       if (Start.isInvalid() || End.isInvalid())
262         return true;
263       unsigned Offset = Sources.getFileOffset(Start);
264       unsigned Length = Sources.getFileOffset(End) - Offset;
265       Ranges.push_back(tooling::Range(Offset, Length));
266     }
267     return false;
268   }
269 
270   if (Offsets.empty())
271     Offsets.push_back(0);
272   if (Offsets.size() != Lengths.size() &&
273       !(Offsets.size() == 1 && Lengths.empty())) {
274     errs() << "error: number of -offset and -length arguments must match.\n";
275     return true;
276   }
277   for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
278     if (Offsets[i] >= Code->getBufferSize()) {
279       errs() << "error: offset " << Offsets[i] << " is outside the file\n";
280       return true;
281     }
282     SourceLocation Start =
283         Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
284     SourceLocation End;
285     if (i < Lengths.size()) {
286       if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
287         errs() << "error: invalid length " << Lengths[i]
288                << ", offset + length (" << Offsets[i] + Lengths[i]
289                << ") is outside the file.\n";
290         return true;
291       }
292       End = Start.getLocWithOffset(Lengths[i]);
293     } else {
294       End = Sources.getLocForEndOfFile(ID);
295     }
296     unsigned Offset = Sources.getFileOffset(Start);
297     unsigned Length = Sources.getFileOffset(End) - Offset;
298     Ranges.push_back(tooling::Range(Offset, Length));
299   }
300   return false;
301 }
302 
303 static void outputReplacementXML(StringRef Text) {
304   // FIXME: When we sort includes, we need to make sure the stream is correct
305   // utf-8.
306   size_t From = 0;
307   size_t Index;
308   while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
309     outs() << Text.substr(From, Index - From);
310     switch (Text[Index]) {
311     case '\n':
312       outs() << "&#10;";
313       break;
314     case '\r':
315       outs() << "&#13;";
316       break;
317     case '<':
318       outs() << "&lt;";
319       break;
320     case '&':
321       outs() << "&amp;";
322       break;
323     default:
324       llvm_unreachable("Unexpected character encountered!");
325     }
326     From = Index + 1;
327   }
328   outs() << Text.substr(From);
329 }
330 
331 static void outputReplacementsXML(const Replacements &Replaces) {
332   for (const auto &R : Replaces) {
333     outs() << "<replacement "
334            << "offset='" << R.getOffset() << "' "
335            << "length='" << R.getLength() << "'>";
336     outputReplacementXML(R.getReplacementText());
337     outs() << "</replacement>\n";
338   }
339 }
340 
341 static bool
342 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
343                         const std::unique_ptr<llvm::MemoryBuffer> &Code) {
344   if (Replaces.empty())
345     return false;
346 
347   unsigned Errors = 0;
348   if (WarnFormat && !NoWarnFormat) {
349     llvm::SourceMgr Mgr;
350     const char *StartBuf = Code->getBufferStart();
351 
352     Mgr.AddNewSourceBuffer(
353         MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
354     for (const auto &R : Replaces) {
355       SMDiagnostic Diag = Mgr.GetMessage(
356           SMLoc::getFromPointer(StartBuf + R.getOffset()),
357           WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
358                            : SourceMgr::DiagKind::DK_Warning,
359           "code should be clang-formatted [-Wclang-format-violations]");
360 
361       Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
362       if (ErrorLimit && ++Errors >= ErrorLimit)
363         break;
364     }
365   }
366   return WarningsAsErrors;
367 }
368 
369 static void outputXML(const Replacements &Replaces,
370                       const Replacements &FormatChanges,
371                       const FormattingAttemptStatus &Status,
372                       const cl::opt<unsigned> &Cursor,
373                       unsigned CursorPosition) {
374   outs() << "<?xml version='1.0'?>\n<replacements "
375             "xml:space='preserve' incomplete_format='"
376          << (Status.FormatComplete ? "false" : "true") << "'";
377   if (!Status.FormatComplete)
378     outs() << " line='" << Status.Line << "'";
379   outs() << ">\n";
380   if (Cursor.getNumOccurrences() != 0) {
381     outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
382            << "</cursor>\n";
383   }
384 
385   outputReplacementsXML(Replaces);
386   outs() << "</replacements>\n";
387 }
388 
389 class ClangFormatDiagConsumer : public DiagnosticConsumer {
390   virtual void anchor() {}
391 
392   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
393                         const Diagnostic &Info) override {
394 
395     SmallVector<char, 16> vec;
396     Info.FormatDiagnostic(vec);
397     errs() << "clang-format error:" << vec << "\n";
398   }
399 };
400 
401 // Returns true on error.
402 static bool format(StringRef FileName) {
403   if (!OutputXML && Inplace && FileName == "-") {
404     errs() << "error: cannot use -i when reading from stdin.\n";
405     return false;
406   }
407   // On Windows, overwriting a file with an open file mapping doesn't work,
408   // so read the whole file into memory when formatting in-place.
409   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
410       !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
411                             : MemoryBuffer::getFileOrSTDIN(FileName);
412   if (std::error_code EC = CodeOrErr.getError()) {
413     errs() << EC.message() << "\n";
414     return true;
415   }
416   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
417   if (Code->getBufferSize() == 0)
418     return false; // Empty files are formatted correctly.
419 
420   StringRef BufStr = Code->getBuffer();
421 
422   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
423 
424   if (InvalidBOM) {
425     errs() << "error: encoding with unsupported byte order mark \""
426            << InvalidBOM << "\" detected";
427     if (FileName != "-")
428       errs() << " in file '" << FileName << "'";
429     errs() << ".\n";
430     return true;
431   }
432 
433   std::vector<tooling::Range> Ranges;
434   if (fillRanges(Code.get(), Ranges))
435     return true;
436   StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
437   if (AssumedFileName.empty()) {
438     llvm::errs() << "error: empty filenames are not allowed\n";
439     return true;
440   }
441 
442   llvm::Expected<FormatStyle> FormatStyle =
443       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
444                nullptr, WNoErrorList.isSet(WNoError::Unknown));
445   if (!FormatStyle) {
446     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
447     return true;
448   }
449 
450   StringRef QualifierAlignmentOrder = QualifierAlignment;
451 
452   FormatStyle->QualifierAlignment =
453       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
454           QualifierAlignmentOrder.lower())
455           .Case("right", FormatStyle::QAS_Right)
456           .Case("left", FormatStyle::QAS_Left)
457           .Default(FormatStyle->QualifierAlignment);
458 
459   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
460     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
461   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
462     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
463   } else if (QualifierAlignmentOrder.contains("type")) {
464     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
465     SmallVector<StringRef> Qualifiers;
466     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
467                                   /*KeepEmpty=*/false);
468     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
469   }
470 
471   if (SortIncludes.getNumOccurrences() != 0) {
472     if (SortIncludes)
473       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
474     else
475       FormatStyle->SortIncludes = FormatStyle::SI_Never;
476   }
477   unsigned CursorPosition = Cursor;
478   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
479                                        AssumedFileName, &CursorPosition);
480 
481   // To format JSON insert a variable to trick the code into thinking its
482   // JavaScript.
483   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
484     auto Err = Replaces.add(tooling::Replacement(
485         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
486     if (Err)
487       llvm::errs() << "Bad Json variable insertion\n";
488   }
489 
490   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
491   if (!ChangedCode) {
492     llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
493     return true;
494   }
495   // Get new affected ranges after sorting `#includes`.
496   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
497   FormattingAttemptStatus Status;
498   Replacements FormatChanges =
499       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
500   Replaces = Replaces.merge(FormatChanges);
501   if (OutputXML || DryRun) {
502     if (DryRun)
503       return emitReplacementWarnings(Replaces, AssumedFileName, Code);
504     else
505       outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
506   } else {
507     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
508         new llvm::vfs::InMemoryFileSystem);
509     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
510 
511     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
512     ClangFormatDiagConsumer IgnoreDiagnostics;
513     DiagnosticsEngine Diagnostics(
514         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
515         &IgnoreDiagnostics, false);
516     SourceManager Sources(Diagnostics, Files);
517     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
518                                    InMemoryFileSystem.get());
519     Rewriter Rewrite(Sources, LangOptions());
520     tooling::applyAllReplacements(Replaces, Rewrite);
521     if (Inplace) {
522       if (Rewrite.overwriteChangedFiles())
523         return true;
524     } else {
525       if (Cursor.getNumOccurrences() != 0) {
526         outs() << "{ \"Cursor\": "
527                << FormatChanges.getShiftedCodePosition(CursorPosition)
528                << ", \"IncompleteFormat\": "
529                << (Status.FormatComplete ? "false" : "true");
530         if (!Status.FormatComplete)
531           outs() << ", \"Line\": " << Status.Line;
532         outs() << " }\n";
533       }
534       Rewrite.getEditBuffer(ID).write(outs());
535     }
536   }
537   return false;
538 }
539 
540 } // namespace format
541 } // namespace clang
542 
543 static void PrintVersion(raw_ostream &OS) {
544   OS << clang::getClangToolFullVersion("clang-format") << '\n';
545 }
546 
547 // Dump the configuration.
548 static int dumpConfig() {
549   StringRef FileName;
550   std::unique_ptr<llvm::MemoryBuffer> Code;
551   if (FileNames.empty()) {
552     // We can't read the code to detect the language if there's no
553     // file name, so leave Code empty here.
554     FileName = AssumeFileName;
555   } else {
556     // Read in the code in case the filename alone isn't enough to
557     // detect the language.
558     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
559         MemoryBuffer::getFileOrSTDIN(FileNames[0]);
560     if (std::error_code EC = CodeOrErr.getError()) {
561       llvm::errs() << EC.message() << "\n";
562       return 1;
563     }
564     FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
565     Code = std::move(CodeOrErr.get());
566   }
567   llvm::Expected<clang::format::FormatStyle> FormatStyle =
568       clang::format::getStyle(Style, FileName, FallbackStyle,
569                               Code ? Code->getBuffer() : "");
570   if (!FormatStyle) {
571     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
572     return 1;
573   }
574   std::string Config = clang::format::configurationAsText(*FormatStyle);
575   outs() << Config << "\n";
576   return 0;
577 }
578 
579 int main(int argc, const char **argv) {
580   llvm::InitLLVM X(argc, argv);
581 
582   cl::HideUnrelatedOptions(ClangFormatCategory);
583 
584   cl::SetVersionPrinter(PrintVersion);
585   cl::ParseCommandLineOptions(
586       argc, argv,
587       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
588       "code.\n\n"
589       "If no arguments are specified, it formats the code from standard input\n"
590       "and writes the result to the standard output.\n"
591       "If <file>s are given, it reformats the files. If -i is specified\n"
592       "together with <file>s, the files are edited in-place. Otherwise, the\n"
593       "result is written to the standard output.\n");
594 
595   if (Help) {
596     cl::PrintHelpMessage();
597     return 0;
598   }
599 
600   if (DumpConfig)
601     return dumpConfig();
602 
603   if (!Files.empty()) {
604     std::ifstream ExternalFileOfFiles{std::string(Files)};
605     std::string Line;
606     unsigned LineNo = 1;
607     while (std::getline(ExternalFileOfFiles, Line)) {
608       FileNames.push_back(Line);
609       LineNo++;
610     }
611     errs() << "Clang-formating " << LineNo << " files\n";
612   }
613 
614   bool Error = false;
615   if (FileNames.empty()) {
616     Error = clang::format::format("-");
617     return Error ? 1 : 0;
618   }
619   if (FileNames.size() != 1 &&
620       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
621     errs() << "error: -offset, -length and -lines can only be used for "
622               "single file.\n";
623     return 1;
624   }
625 
626   unsigned FileNo = 1;
627   for (const auto &FileName : FileNames) {
628     if (Verbose) {
629       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
630              << FileName << "\n";
631     }
632     Error |= clang::format::format(FileName);
633   }
634   return Error ? 1 : 0;
635 }
636