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