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