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