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 < 1) {
250         errs() << "error: start line should be at least 1\n";
251         return true;
252       }
253       if (FromLine > ToLine) {
254         errs() << "error: start line should not exceed end line\n";
255         return true;
256       }
257       SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
258       SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
259       if (Start.isInvalid() || End.isInvalid())
260         return true;
261       unsigned Offset = Sources.getFileOffset(Start);
262       unsigned Length = Sources.getFileOffset(End) - Offset;
263       Ranges.push_back(tooling::Range(Offset, Length));
264     }
265     return false;
266   }
267 
268   if (Offsets.empty())
269     Offsets.push_back(0);
270   if (Offsets.size() != Lengths.size() &&
271       !(Offsets.size() == 1 && Lengths.empty())) {
272     errs() << "error: number of -offset and -length arguments must match.\n";
273     return true;
274   }
275   for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
276     if (Offsets[i] >= Code->getBufferSize()) {
277       errs() << "error: offset " << Offsets[i] << " is outside the file\n";
278       return true;
279     }
280     SourceLocation Start =
281         Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
282     SourceLocation End;
283     if (i < Lengths.size()) {
284       if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
285         errs() << "error: invalid length " << Lengths[i]
286                << ", offset + length (" << Offsets[i] + Lengths[i]
287                << ") is outside the file.\n";
288         return true;
289       }
290       End = Start.getLocWithOffset(Lengths[i]);
291     } else {
292       End = Sources.getLocForEndOfFile(ID);
293     }
294     unsigned Offset = Sources.getFileOffset(Start);
295     unsigned Length = Sources.getFileOffset(End) - Offset;
296     Ranges.push_back(tooling::Range(Offset, Length));
297   }
298   return false;
299 }
300 
301 static void outputReplacementXML(StringRef Text) {
302   // FIXME: When we sort includes, we need to make sure the stream is correct
303   // utf-8.
304   size_t From = 0;
305   size_t Index;
306   while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
307     outs() << Text.substr(From, Index - From);
308     switch (Text[Index]) {
309     case '\n':
310       outs() << "&#10;";
311       break;
312     case '\r':
313       outs() << "&#13;";
314       break;
315     case '<':
316       outs() << "&lt;";
317       break;
318     case '&':
319       outs() << "&amp;";
320       break;
321     default:
322       llvm_unreachable("Unexpected character encountered!");
323     }
324     From = Index + 1;
325   }
326   outs() << Text.substr(From);
327 }
328 
329 static void outputReplacementsXML(const Replacements &Replaces) {
330   for (const auto &R : Replaces) {
331     outs() << "<replacement "
332            << "offset='" << R.getOffset() << "' "
333            << "length='" << R.getLength() << "'>";
334     outputReplacementXML(R.getReplacementText());
335     outs() << "</replacement>\n";
336   }
337 }
338 
339 static bool
340 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
341                         const std::unique_ptr<llvm::MemoryBuffer> &Code) {
342   if (Replaces.empty())
343     return false;
344 
345   unsigned Errors = 0;
346   if (WarnFormat && !NoWarnFormat) {
347     llvm::SourceMgr Mgr;
348     const char *StartBuf = Code->getBufferStart();
349 
350     Mgr.AddNewSourceBuffer(
351         MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
352     for (const auto &R : Replaces) {
353       SMDiagnostic Diag = Mgr.GetMessage(
354           SMLoc::getFromPointer(StartBuf + R.getOffset()),
355           WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
356                            : SourceMgr::DiagKind::DK_Warning,
357           "code should be clang-formatted [-Wclang-format-violations]");
358 
359       Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
360       if (ErrorLimit && ++Errors >= ErrorLimit)
361         break;
362     }
363   }
364   return WarningsAsErrors;
365 }
366 
367 static void outputXML(const Replacements &Replaces,
368                       const Replacements &FormatChanges,
369                       const FormattingAttemptStatus &Status,
370                       const cl::opt<unsigned> &Cursor,
371                       unsigned CursorPosition) {
372   outs() << "<?xml version='1.0'?>\n<replacements "
373             "xml:space='preserve' incomplete_format='"
374          << (Status.FormatComplete ? "false" : "true") << "'";
375   if (!Status.FormatComplete)
376     outs() << " line='" << Status.Line << "'";
377   outs() << ">\n";
378   if (Cursor.getNumOccurrences() != 0) {
379     outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
380            << "</cursor>\n";
381   }
382 
383   outputReplacementsXML(Replaces);
384   outs() << "</replacements>\n";
385 }
386 
387 class ClangFormatDiagConsumer : public DiagnosticConsumer {
388   virtual void anchor() {}
389 
390   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
391                         const Diagnostic &Info) override {
392 
393     SmallVector<char, 16> vec;
394     Info.FormatDiagnostic(vec);
395     errs() << "clang-format error:" << vec << "\n";
396   }
397 };
398 
399 // Returns true on error.
400 static bool format(StringRef FileName) {
401   if (!OutputXML && Inplace && FileName == "-") {
402     errs() << "error: cannot use -i when reading from stdin.\n";
403     return false;
404   }
405   // On Windows, overwriting a file with an open file mapping doesn't work,
406   // so read the whole file into memory when formatting in-place.
407   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
408       !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
409                             : MemoryBuffer::getFileOrSTDIN(FileName);
410   if (std::error_code EC = CodeOrErr.getError()) {
411     errs() << EC.message() << "\n";
412     return true;
413   }
414   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
415   if (Code->getBufferSize() == 0)
416     return false; // Empty files are formatted correctly.
417 
418   StringRef BufStr = Code->getBuffer();
419 
420   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
421 
422   if (InvalidBOM) {
423     errs() << "error: encoding with unsupported byte order mark \""
424            << InvalidBOM << "\" detected";
425     if (FileName != "-")
426       errs() << " in file '" << FileName << "'";
427     errs() << ".\n";
428     return true;
429   }
430 
431   std::vector<tooling::Range> Ranges;
432   if (fillRanges(Code.get(), Ranges))
433     return true;
434   StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
435   if (AssumedFileName.empty()) {
436     llvm::errs() << "error: empty filenames are not allowed\n";
437     return true;
438   }
439 
440   llvm::Expected<FormatStyle> FormatStyle =
441       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
442                nullptr, WNoErrorList.isSet(WNoError::Unknown));
443   if (!FormatStyle) {
444     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
445     return true;
446   }
447 
448   StringRef QualifierAlignmentOrder = QualifierAlignment;
449 
450   FormatStyle->QualifierAlignment =
451       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
452           QualifierAlignmentOrder.lower())
453           .Case("right", FormatStyle::QAS_Right)
454           .Case("left", FormatStyle::QAS_Left)
455           .Default(FormatStyle->QualifierAlignment);
456 
457   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
458     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
459   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
460     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
461   } else if (QualifierAlignmentOrder.contains("type")) {
462     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
463     SmallVector<StringRef> Qualifiers;
464     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
465                                   /*KeepEmpty=*/false);
466     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
467   }
468 
469   if (SortIncludes.getNumOccurrences() != 0) {
470     if (SortIncludes)
471       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
472     else
473       FormatStyle->SortIncludes = FormatStyle::SI_Never;
474   }
475   unsigned CursorPosition = Cursor;
476   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
477                                        AssumedFileName, &CursorPosition);
478 
479   // To format JSON insert a variable to trick the code into thinking its
480   // JavaScript.
481   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
482     auto Err = Replaces.add(tooling::Replacement(
483         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
484     if (Err)
485       llvm::errs() << "Bad Json variable insertion\n";
486   }
487 
488   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
489   if (!ChangedCode) {
490     llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
491     return true;
492   }
493   // Get new affected ranges after sorting `#includes`.
494   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
495   FormattingAttemptStatus Status;
496   Replacements FormatChanges =
497       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
498   Replaces = Replaces.merge(FormatChanges);
499   if (OutputXML || DryRun) {
500     if (DryRun)
501       return emitReplacementWarnings(Replaces, AssumedFileName, Code);
502     else
503       outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
504   } else {
505     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
506         new llvm::vfs::InMemoryFileSystem);
507     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
508 
509     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
510     ClangFormatDiagConsumer IgnoreDiagnostics;
511     DiagnosticsEngine Diagnostics(
512         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
513         &IgnoreDiagnostics, false);
514     SourceManager Sources(Diagnostics, Files);
515     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
516                                    InMemoryFileSystem.get());
517     Rewriter Rewrite(Sources, LangOptions());
518     tooling::applyAllReplacements(Replaces, Rewrite);
519     if (Inplace) {
520       if (Rewrite.overwriteChangedFiles())
521         return true;
522     } else {
523       if (Cursor.getNumOccurrences() != 0) {
524         outs() << "{ \"Cursor\": "
525                << FormatChanges.getShiftedCodePosition(CursorPosition)
526                << ", \"IncompleteFormat\": "
527                << (Status.FormatComplete ? "false" : "true");
528         if (!Status.FormatComplete)
529           outs() << ", \"Line\": " << Status.Line;
530         outs() << " }\n";
531       }
532       Rewrite.getEditBuffer(ID).write(outs());
533     }
534   }
535   return false;
536 }
537 
538 } // namespace format
539 } // namespace clang
540 
541 static void PrintVersion(raw_ostream &OS) {
542   OS << clang::getClangToolFullVersion("clang-format") << '\n';
543 }
544 
545 // Dump the configuration.
546 static int dumpConfig() {
547   StringRef FileName;
548   std::unique_ptr<llvm::MemoryBuffer> Code;
549   if (FileNames.empty()) {
550     // We can't read the code to detect the language if there's no
551     // file name, so leave Code empty here.
552     FileName = AssumeFileName;
553   } else {
554     // Read in the code in case the filename alone isn't enough to
555     // detect the language.
556     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
557         MemoryBuffer::getFileOrSTDIN(FileNames[0]);
558     if (std::error_code EC = CodeOrErr.getError()) {
559       llvm::errs() << EC.message() << "\n";
560       return 1;
561     }
562     FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
563     Code = std::move(CodeOrErr.get());
564   }
565   llvm::Expected<clang::format::FormatStyle> FormatStyle =
566       clang::format::getStyle(Style, FileName, FallbackStyle,
567                               Code ? Code->getBuffer() : "");
568   if (!FormatStyle) {
569     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
570     return 1;
571   }
572   std::string Config = clang::format::configurationAsText(*FormatStyle);
573   outs() << Config << "\n";
574   return 0;
575 }
576 
577 int main(int argc, const char **argv) {
578   llvm::InitLLVM X(argc, argv);
579 
580   cl::HideUnrelatedOptions(ClangFormatCategory);
581 
582   cl::SetVersionPrinter(PrintVersion);
583   cl::ParseCommandLineOptions(
584       argc, argv,
585       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
586       "code.\n\n"
587       "If no arguments are specified, it formats the code from standard input\n"
588       "and writes the result to the standard output.\n"
589       "If <file>s are given, it reformats the files. If -i is specified\n"
590       "together with <file>s, the files are edited in-place. Otherwise, the\n"
591       "result is written to the standard output.\n");
592 
593   if (Help) {
594     cl::PrintHelpMessage();
595     return 0;
596   }
597 
598   if (DumpConfig)
599     return dumpConfig();
600 
601   if (!Files.empty()) {
602     std::ifstream ExternalFileOfFiles{std::string(Files)};
603     std::string Line;
604     unsigned LineNo = 1;
605     while (std::getline(ExternalFileOfFiles, Line)) {
606       FileNames.push_back(Line);
607       LineNo++;
608     }
609     errs() << "Clang-formating " << LineNo << " files\n";
610   }
611 
612   bool Error = false;
613   if (FileNames.empty()) {
614     Error = clang::format::format("-");
615     return Error ? 1 : 0;
616   }
617   if (FileNames.size() != 1 &&
618       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
619     errs() << "error: -offset, -length and -lines can only be used for "
620               "single file.\n";
621     return 1;
622   }
623 
624   unsigned FileNo = 1;
625   for (const auto &FileName : FileNames) {
626     if (Verbose) {
627       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
628              << FileName << "\n";
629     }
630     Error |= clang::format::format(FileName);
631   }
632   return Error ? 1 : 0;
633 }
634