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>
104     SortIncludes("sort-includes",
105                  cl::desc("If set, overrides the include sorting behavior\n"
106                           "determined by the SortIncludes style flag"),
107                  cl::cat(ClangFormatCategory));
108 
109 static cl::opt<std::string> QualifierAlignment(
110     "qualifier-alignment",
111     cl::desc("If set, overrides the qualifier alignment style\n"
112              "determined by the QualifierAlignment style flag"),
113     cl::init(""), cl::cat(ClangFormatCategory));
114 
115 static cl::opt<std::string>
116     Files("files", cl::desc("Provide a list of files to run clang-format"),
117           cl::init(""), cl::cat(ClangFormatCategory));
118 
119 static cl::opt<bool>
120     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
121             cl::cat(ClangFormatCategory));
122 
123 // Use --dry-run to match other LLVM tools when you mean do it but don't
124 // actually do it
125 static cl::opt<bool>
126     DryRun("dry-run",
127            cl::desc("If set, do not actually make the formatting changes"),
128            cl::cat(ClangFormatCategory));
129 
130 // Use -n as a common command as an alias for --dry-run. (git and make use -n)
131 static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
132                              cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
133                              cl::NotHidden);
134 
135 // Emulate being able to turn on/off the warning.
136 static cl::opt<bool>
137     WarnFormat("Wclang-format-violations",
138                cl::desc("Warnings about individual formatting changes needed. "
139                         "Used only with --dry-run or -n"),
140                cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
141 
142 static cl::opt<bool>
143     NoWarnFormat("Wno-clang-format-violations",
144                  cl::desc("Do not warn about individual formatting changes "
145                           "needed. Used only with --dry-run or -n"),
146                  cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
147 
148 static cl::opt<unsigned> ErrorLimit(
149     "ferror-limit",
150     cl::desc("Set the maximum number of clang-format errors to emit\n"
151              "before stopping (0 = no limit).\n"
152              "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 
366   outputReplacementsXML(Replaces);
367   outs() << "</replacements>\n";
368 }
369 
370 class ClangFormatDiagConsumer : public DiagnosticConsumer {
371   virtual void anchor() {}
372 
373   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
374                         const Diagnostic &Info) override {
375 
376     SmallVector<char, 16> vec;
377     Info.FormatDiagnostic(vec);
378     errs() << "clang-format error:" << vec << "\n";
379   }
380 };
381 
382 // Returns true on error.
383 static bool format(StringRef FileName) {
384   if (!OutputXML && Inplace && FileName == "-") {
385     errs() << "error: cannot use -i when reading from stdin.\n";
386     return false;
387   }
388   // On Windows, overwriting a file with an open file mapping doesn't work,
389   // so read the whole file into memory when formatting in-place.
390   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
391       !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
392                             : MemoryBuffer::getFileOrSTDIN(FileName);
393   if (std::error_code EC = CodeOrErr.getError()) {
394     errs() << EC.message() << "\n";
395     return true;
396   }
397   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
398   if (Code->getBufferSize() == 0)
399     return false; // Empty files are formatted correctly.
400 
401   StringRef BufStr = Code->getBuffer();
402 
403   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
404 
405   if (InvalidBOM) {
406     errs() << "error: encoding with unsupported byte order mark \""
407            << InvalidBOM << "\" detected";
408     if (FileName != "-")
409       errs() << " in file '" << FileName << "'";
410     errs() << ".\n";
411     return true;
412   }
413 
414   std::vector<tooling::Range> Ranges;
415   if (fillRanges(Code.get(), Ranges))
416     return true;
417   StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
418   if (AssumedFileName.empty()) {
419     llvm::errs() << "error: empty filenames are not allowed\n";
420     return true;
421   }
422 
423   llvm::Expected<FormatStyle> FormatStyle =
424       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
425                nullptr, WNoErrorList.isSet(WNoError::Unknown));
426   if (!FormatStyle) {
427     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
428     return true;
429   }
430 
431   StringRef QualifierAlignmentOrder = QualifierAlignment;
432 
433   FormatStyle->QualifierAlignment =
434       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
435           QualifierAlignmentOrder.lower())
436           .Case("right", FormatStyle::QAS_Right)
437           .Case("left", FormatStyle::QAS_Left)
438           .Default(FormatStyle->QualifierAlignment);
439 
440   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
441     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
442   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
443     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
444   } else if (QualifierAlignmentOrder.contains("type")) {
445     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
446     SmallVector<StringRef> Qualifiers;
447     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
448                                   /*KeepEmpty=*/false);
449     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
450   }
451 
452   if (SortIncludes.getNumOccurrences() != 0) {
453     if (SortIncludes)
454       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
455     else
456       FormatStyle->SortIncludes = FormatStyle::SI_Never;
457   }
458   unsigned CursorPosition = Cursor;
459   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
460                                        AssumedFileName, &CursorPosition);
461 
462   // To format JSON insert a variable to trick the code into thinking its
463   // JavaScript.
464   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
465     auto Err = Replaces.add(tooling::Replacement(
466         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
467     if (Err)
468       llvm::errs() << "Bad Json variable insertion\n";
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   } else {
488     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
489         new llvm::vfs::InMemoryFileSystem);
490     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
491 
492     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
493     ClangFormatDiagConsumer IgnoreDiagnostics;
494     DiagnosticsEngine Diagnostics(
495         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
496         &IgnoreDiagnostics, false);
497     SourceManager Sources(Diagnostics, Files);
498     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
499                                    InMemoryFileSystem.get());
500     Rewriter Rewrite(Sources, LangOptions());
501     tooling::applyAllReplacements(Replaces, Rewrite);
502     if (Inplace) {
503       if (Rewrite.overwriteChangedFiles())
504         return true;
505     } else {
506       if (Cursor.getNumOccurrences() != 0) {
507         outs() << "{ \"Cursor\": "
508                << FormatChanges.getShiftedCodePosition(CursorPosition)
509                << ", \"IncompleteFormat\": "
510                << (Status.FormatComplete ? "false" : "true");
511         if (!Status.FormatComplete)
512           outs() << ", \"Line\": " << Status.Line;
513         outs() << " }\n";
514       }
515       Rewrite.getEditBuffer(ID).write(outs());
516     }
517   }
518   return false;
519 }
520 
521 } // namespace format
522 } // namespace clang
523 
524 static void PrintVersion(raw_ostream &OS) {
525   OS << clang::getClangToolFullVersion("clang-format") << '\n';
526 }
527 
528 // Dump the configuration.
529 static int dumpConfig() {
530   StringRef FileName;
531   std::unique_ptr<llvm::MemoryBuffer> Code;
532   if (FileNames.empty()) {
533     // We can't read the code to detect the language if there's no
534     // file name, so leave Code empty here.
535     FileName = AssumeFileName;
536   } else {
537     // Read in the code in case the filename alone isn't enough to
538     // detect the language.
539     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
540         MemoryBuffer::getFileOrSTDIN(FileNames[0]);
541     if (std::error_code EC = CodeOrErr.getError()) {
542       llvm::errs() << EC.message() << "\n";
543       return 1;
544     }
545     FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
546     Code = std::move(CodeOrErr.get());
547   }
548   llvm::Expected<clang::format::FormatStyle> FormatStyle =
549       clang::format::getStyle(Style, FileName, FallbackStyle,
550                               Code ? Code->getBuffer() : "");
551   if (!FormatStyle) {
552     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
553     return 1;
554   }
555   std::string Config = clang::format::configurationAsText(*FormatStyle);
556   outs() << Config << "\n";
557   return 0;
558 }
559 
560 int main(int argc, const char **argv) {
561   llvm::InitLLVM X(argc, argv);
562 
563   cl::HideUnrelatedOptions(ClangFormatCategory);
564 
565   cl::SetVersionPrinter(PrintVersion);
566   cl::ParseCommandLineOptions(
567       argc, argv,
568       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
569       "code.\n\n"
570       "If no arguments are specified, it formats the code from standard input\n"
571       "and writes the result to the standard output.\n"
572       "If <file>s are given, it reformats the files. If -i is specified\n"
573       "together with <file>s, the files are edited in-place. Otherwise, the\n"
574       "result is written to the standard output.\n");
575 
576   if (Help) {
577     cl::PrintHelpMessage();
578     return 0;
579   }
580 
581   if (DumpConfig)
582     return dumpConfig();
583 
584   if (!Files.empty()) {
585     std::ifstream ExternalFileOfFiles{std::string(Files)};
586     std::string Line;
587     unsigned LineNo = 1;
588     while (std::getline(ExternalFileOfFiles, Line)) {
589       FileNames.push_back(Line);
590       LineNo++;
591     }
592     errs() << "Clang-formating " << LineNo << " files\n";
593   }
594 
595   bool Error = false;
596   if (FileNames.empty()) {
597     Error = clang::format::format("-");
598     return Error ? 1 : 0;
599   }
600   if (FileNames.size() != 1 &&
601       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
602     errs() << "error: -offset, -length and -lines can only be used for "
603               "single file.\n";
604     return 1;
605   }
606 
607   unsigned FileNo = 1;
608   for (const auto &FileName : FileNames) {
609     if (Verbose) {
610       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
611              << FileName << "\n";
612     }
613     Error |= clang::format::format(FileName);
614   }
615   return Error ? 1 : 0;
616 }
617