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