xref: /llvm-project/clang/tools/clang-format/ClangFormat.cpp (revision e22ce615fe31a78857a8574c12a32bddc6da465e)
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 "../../lib/Format/MatchFilePath.h"
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/DiagnosticOptions.h"
18 #include "clang/Basic/FileManager.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Basic/Version.h"
21 #include "clang/Format/Format.h"
22 #include "clang/Rewrite/Core/Rewriter.h"
23 #include "llvm/ADT/StringSwitch.h"
24 #include "llvm/Support/CommandLine.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/InitLLVM.h"
27 #include "llvm/Support/Process.h"
28 #include <fstream>
29 
30 using namespace llvm;
31 using clang::tooling::Replacements;
32 
33 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
34 
35 // Mark all our options with this category, everything else (except for -version
36 // and -help) will be hidden.
37 static cl::OptionCategory ClangFormatCategory("Clang-format options");
38 
39 static cl::list<unsigned>
40     Offsets("offset",
41             cl::desc("Format a range starting at this byte offset.\n"
42                      "Multiple ranges can be formatted by specifying\n"
43                      "several -offset and -length pairs.\n"
44                      "Can only be used with one input file."),
45             cl::cat(ClangFormatCategory));
46 static cl::list<unsigned>
47     Lengths("length",
48             cl::desc("Format a range of this length (in bytes).\n"
49                      "Multiple ranges can be formatted by specifying\n"
50                      "several -offset and -length pairs.\n"
51                      "When only a single -offset is specified without\n"
52                      "-length, clang-format will format up to the end\n"
53                      "of the file.\n"
54                      "Can only be used with one input file."),
55             cl::cat(ClangFormatCategory));
56 static cl::list<std::string>
57     LineRanges("lines",
58                cl::desc("<start line>:<end line> - format a range of\n"
59                         "lines (both 1-based).\n"
60                         "Multiple ranges can be formatted by specifying\n"
61                         "several -lines arguments.\n"
62                         "Can't be used with -offset and -length.\n"
63                         "Can only be used with one input file."),
64                cl::cat(ClangFormatCategory));
65 static cl::opt<std::string>
66     Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
67           cl::init(clang::format::DefaultFormatStyle),
68           cl::cat(ClangFormatCategory));
69 static cl::opt<std::string>
70     FallbackStyle("fallback-style",
71                   cl::desc("The name of the predefined style used as a\n"
72                            "fallback in case clang-format is invoked with\n"
73                            "-style=file, but can not find the .clang-format\n"
74                            "file to use. Defaults to 'LLVM'.\n"
75                            "Use -fallback-style=none to skip formatting."),
76                   cl::init(clang::format::DefaultFallbackStyle),
77                   cl::cat(ClangFormatCategory));
78 
79 static cl::opt<std::string> AssumeFileName(
80     "assume-filename",
81     cl::desc("Set filename used to determine the language and to find\n"
82              ".clang-format file.\n"
83              "Only used when reading from stdin.\n"
84              "If this is not passed, the .clang-format file is searched\n"
85              "relative to the current working directory when reading stdin.\n"
86              "Unrecognized filenames are treated as C++.\n"
87              "supported:\n"
88              "  CSharp: .cs\n"
89              "  Java: .java\n"
90              "  JavaScript: .mjs .js .ts\n"
91              "  Json: .json\n"
92              "  Objective-C: .m .mm\n"
93              "  Proto: .proto .protodevel\n"
94              "  TableGen: .td\n"
95              "  TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"
96              "  Verilog: .sv .svh .v .vh"),
97     cl::init("<stdin>"), cl::cat(ClangFormatCategory));
98 
99 static cl::opt<bool> Inplace("i",
100                              cl::desc("Inplace edit <file>s, if specified."),
101                              cl::cat(ClangFormatCategory));
102 
103 static cl::opt<bool> OutputXML("output-replacements-xml",
104                                cl::desc("Output replacements as XML."),
105                                cl::cat(ClangFormatCategory));
106 static cl::opt<bool>
107     DumpConfig("dump-config",
108                cl::desc("Dump configuration options to stdout and exit.\n"
109                         "Can be used with -style option."),
110                cl::cat(ClangFormatCategory));
111 static cl::opt<unsigned>
112     Cursor("cursor",
113            cl::desc("The position of the cursor when invoking\n"
114                     "clang-format from an editor integration"),
115            cl::init(0), cl::cat(ClangFormatCategory));
116 
117 static cl::opt<bool>
118     SortIncludes("sort-includes",
119                  cl::desc("If set, overrides the include sorting behavior\n"
120                           "determined by the SortIncludes style flag"),
121                  cl::cat(ClangFormatCategory));
122 
123 static cl::opt<std::string> QualifierAlignment(
124     "qualifier-alignment",
125     cl::desc("If set, overrides the qualifier alignment style\n"
126              "determined by the QualifierAlignment style flag"),
127     cl::init(""), cl::cat(ClangFormatCategory));
128 
129 static cl::opt<std::string> Files(
130     "files",
131     cl::desc("A file containing a list of files to process, one per line."),
132     cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
133 
134 static cl::opt<bool>
135     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
136             cl::cat(ClangFormatCategory));
137 
138 // Use --dry-run to match other LLVM tools when you mean do it but don't
139 // actually do it
140 static cl::opt<bool>
141     DryRun("dry-run",
142            cl::desc("If set, do not actually make the formatting changes"),
143            cl::cat(ClangFormatCategory));
144 
145 // Use -n as a common command as an alias for --dry-run. (git and make use -n)
146 static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
147                              cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
148                              cl::NotHidden);
149 
150 // Emulate being able to turn on/off the warning.
151 static cl::opt<bool>
152     WarnFormat("Wclang-format-violations",
153                cl::desc("Warnings about individual formatting changes needed. "
154                         "Used only with --dry-run or -n"),
155                cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
156 
157 static cl::opt<bool>
158     NoWarnFormat("Wno-clang-format-violations",
159                  cl::desc("Do not warn about individual formatting changes "
160                           "needed. Used only with --dry-run or -n"),
161                  cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
162 
163 static cl::opt<unsigned> ErrorLimit(
164     "ferror-limit",
165     cl::desc("Set the maximum number of clang-format errors to emit\n"
166              "before stopping (0 = no limit).\n"
167              "Used only with --dry-run or -n"),
168     cl::init(0), cl::cat(ClangFormatCategory));
169 
170 static cl::opt<bool>
171     WarningsAsErrors("Werror",
172                      cl::desc("If set, changes formatting warnings to errors"),
173                      cl::cat(ClangFormatCategory));
174 
175 namespace {
176 enum class WNoError { Unknown };
177 }
178 
179 static cl::bits<WNoError> WNoErrorList(
180     "Wno-error",
181     cl::desc("If set don't error out on the specified warning type."),
182     cl::values(
183         clEnumValN(WNoError::Unknown, "unknown",
184                    "If set, unknown format options are only warned about.\n"
185                    "This can be used to enable formatting, even if the\n"
186                    "configuration contains unknown (newer) options.\n"
187                    "Use with caution, as this might lead to dramatically\n"
188                    "differing format depending on an option being\n"
189                    "supported or not.")),
190     cl::cat(ClangFormatCategory));
191 
192 static cl::opt<bool>
193     ShowColors("fcolor-diagnostics",
194                cl::desc("If set, and on a color-capable terminal controls "
195                         "whether or not to print diagnostics in color"),
196                cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
197 
198 static cl::opt<bool>
199     NoShowColors("fno-color-diagnostics",
200                  cl::desc("If set, and on a color-capable terminal controls "
201                           "whether or not to print diagnostics in color"),
202                  cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
203 
204 static cl::list<std::string> FileNames(cl::Positional,
205                                        cl::desc("[@<file>] [<file> ...]"),
206                                        cl::cat(ClangFormatCategory));
207 
208 static cl::opt<bool> FailOnIncompleteFormat(
209     "fail-on-incomplete-format",
210     cl::desc("If set, fail with exit code 1 on incomplete format."),
211     cl::init(false), cl::cat(ClangFormatCategory));
212 
213 namespace clang {
214 namespace format {
215 
216 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
217                                  SourceManager &Sources, FileManager &Files,
218                                  llvm::vfs::InMemoryFileSystem *MemFS) {
219   MemFS->addFileNoOwn(FileName, 0, Source);
220   auto File = Files.getOptionalFileRef(FileName);
221   assert(File && "File not added to MemFS?");
222   return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
223 }
224 
225 // Parses <start line>:<end line> input to a pair of line numbers.
226 // Returns true on error.
227 static bool parseLineRange(StringRef Input, unsigned &FromLine,
228                            unsigned &ToLine) {
229   std::pair<StringRef, StringRef> LineRange = Input.split(':');
230   return LineRange.first.getAsInteger(0, FromLine) ||
231          LineRange.second.getAsInteger(0, ToLine);
232 }
233 
234 static bool fillRanges(MemoryBuffer *Code,
235                        std::vector<tooling::Range> &Ranges) {
236   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
237       new llvm::vfs::InMemoryFileSystem);
238   FileManager Files(FileSystemOptions(), InMemoryFileSystem);
239   DiagnosticsEngine Diagnostics(
240       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
241       new DiagnosticOptions);
242   SourceManager Sources(Diagnostics, Files);
243   FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
244                                  InMemoryFileSystem.get());
245   if (!LineRanges.empty()) {
246     if (!Offsets.empty() || !Lengths.empty()) {
247       errs() << "error: cannot use -lines with -offset/-length\n";
248       return true;
249     }
250 
251     for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
252       unsigned FromLine, ToLine;
253       if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
254         errs() << "error: invalid <start line>:<end line> pair\n";
255         return true;
256       }
257       if (FromLine < 1) {
258         errs() << "error: start line should be at least 1\n";
259         return true;
260       }
261       if (FromLine > ToLine) {
262         errs() << "error: start line should not exceed end line\n";
263         return true;
264       }
265       SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
266       SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
267       if (Start.isInvalid() || End.isInvalid())
268         return true;
269       unsigned Offset = Sources.getFileOffset(Start);
270       unsigned Length = Sources.getFileOffset(End) - Offset;
271       Ranges.push_back(tooling::Range(Offset, Length));
272     }
273     return false;
274   }
275 
276   if (Offsets.empty())
277     Offsets.push_back(0);
278   if (Offsets.size() != Lengths.size() &&
279       !(Offsets.size() == 1 && Lengths.empty())) {
280     errs() << "error: number of -offset and -length arguments must match.\n";
281     return true;
282   }
283   for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
284     if (Offsets[i] >= Code->getBufferSize()) {
285       errs() << "error: offset " << Offsets[i] << " is outside the file\n";
286       return true;
287     }
288     SourceLocation Start =
289         Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
290     SourceLocation End;
291     if (i < Lengths.size()) {
292       if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
293         errs() << "error: invalid length " << Lengths[i]
294                << ", offset + length (" << Offsets[i] + Lengths[i]
295                << ") is outside the file.\n";
296         return true;
297       }
298       End = Start.getLocWithOffset(Lengths[i]);
299     } else {
300       End = Sources.getLocForEndOfFile(ID);
301     }
302     unsigned Offset = Sources.getFileOffset(Start);
303     unsigned Length = Sources.getFileOffset(End) - Offset;
304     Ranges.push_back(tooling::Range(Offset, Length));
305   }
306   return false;
307 }
308 
309 static void outputReplacementXML(StringRef Text) {
310   // FIXME: When we sort includes, we need to make sure the stream is correct
311   // utf-8.
312   size_t From = 0;
313   size_t Index;
314   while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
315     outs() << Text.substr(From, Index - From);
316     switch (Text[Index]) {
317     case '\n':
318       outs() << "&#10;";
319       break;
320     case '\r':
321       outs() << "&#13;";
322       break;
323     case '<':
324       outs() << "&lt;";
325       break;
326     case '&':
327       outs() << "&amp;";
328       break;
329     default:
330       llvm_unreachable("Unexpected character encountered!");
331     }
332     From = Index + 1;
333   }
334   outs() << Text.substr(From);
335 }
336 
337 static void outputReplacementsXML(const Replacements &Replaces) {
338   for (const auto &R : Replaces) {
339     outs() << "<replacement " << "offset='" << R.getOffset() << "' "
340            << "length='" << R.getLength() << "'>";
341     outputReplacementXML(R.getReplacementText());
342     outs() << "</replacement>\n";
343   }
344 }
345 
346 static bool
347 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
348                         const std::unique_ptr<llvm::MemoryBuffer> &Code) {
349   if (Replaces.empty())
350     return false;
351 
352   unsigned Errors = 0;
353   if (WarnFormat && !NoWarnFormat) {
354     llvm::SourceMgr Mgr;
355     const char *StartBuf = Code->getBufferStart();
356 
357     Mgr.AddNewSourceBuffer(
358         MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
359     for (const auto &R : Replaces) {
360       SMDiagnostic Diag = Mgr.GetMessage(
361           SMLoc::getFromPointer(StartBuf + R.getOffset()),
362           WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
363                            : SourceMgr::DiagKind::DK_Warning,
364           "code should be clang-formatted [-Wclang-format-violations]");
365 
366       Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
367       if (ErrorLimit && ++Errors >= ErrorLimit)
368         break;
369     }
370   }
371   return WarningsAsErrors;
372 }
373 
374 static void outputXML(const Replacements &Replaces,
375                       const Replacements &FormatChanges,
376                       const FormattingAttemptStatus &Status,
377                       const cl::opt<unsigned> &Cursor,
378                       unsigned CursorPosition) {
379   outs() << "<?xml version='1.0'?>\n<replacements "
380             "xml:space='preserve' incomplete_format='"
381          << (Status.FormatComplete ? "false" : "true") << "'";
382   if (!Status.FormatComplete)
383     outs() << " line='" << Status.Line << "'";
384   outs() << ">\n";
385   if (Cursor.getNumOccurrences() != 0) {
386     outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
387            << "</cursor>\n";
388   }
389 
390   outputReplacementsXML(Replaces);
391   outs() << "</replacements>\n";
392 }
393 
394 class ClangFormatDiagConsumer : public DiagnosticConsumer {
395   virtual void anchor() {}
396 
397   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
398                         const Diagnostic &Info) override {
399 
400     SmallVector<char, 16> vec;
401     Info.FormatDiagnostic(vec);
402     errs() << "clang-format error:" << vec << "\n";
403   }
404 };
405 
406 // Returns true on error.
407 static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
408   const bool IsSTDIN = FileName == "-";
409   if (!OutputXML && Inplace && IsSTDIN) {
410     errs() << "error: cannot use -i when reading from stdin.\n";
411     return false;
412   }
413   // On Windows, overwriting a file with an open file mapping doesn't work,
414   // so read the whole file into memory when formatting in-place.
415   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
416       !OutputXML && Inplace
417           ? MemoryBuffer::getFileAsStream(FileName)
418           : MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
419   if (std::error_code EC = CodeOrErr.getError()) {
420     errs() << EC.message() << "\n";
421     return true;
422   }
423   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
424   if (Code->getBufferSize() == 0)
425     return false; // Empty files are formatted correctly.
426 
427   StringRef BufStr = Code->getBuffer();
428 
429   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
430 
431   if (InvalidBOM) {
432     errs() << "error: encoding with unsupported byte order mark \""
433            << InvalidBOM << "\" detected";
434     if (!IsSTDIN)
435       errs() << " in file '" << FileName << "'";
436     errs() << ".\n";
437     return true;
438   }
439 
440   std::vector<tooling::Range> Ranges;
441   if (fillRanges(Code.get(), Ranges))
442     return true;
443   StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
444   if (AssumedFileName.empty()) {
445     llvm::errs() << "error: empty filenames are not allowed\n";
446     return true;
447   }
448 
449   llvm::Expected<FormatStyle> FormatStyle =
450       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
451                nullptr, WNoErrorList.isSet(WNoError::Unknown));
452   if (!FormatStyle) {
453     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
454     return true;
455   }
456 
457   StringRef QualifierAlignmentOrder = QualifierAlignment;
458 
459   FormatStyle->QualifierAlignment =
460       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
461           QualifierAlignmentOrder.lower())
462           .Case("right", FormatStyle::QAS_Right)
463           .Case("left", FormatStyle::QAS_Left)
464           .Default(FormatStyle->QualifierAlignment);
465 
466   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
467     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
468   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
469     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
470   } else if (QualifierAlignmentOrder.contains("type")) {
471     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
472     SmallVector<StringRef> Qualifiers;
473     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
474                                   /*KeepEmpty=*/false);
475     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
476   }
477 
478   if (SortIncludes.getNumOccurrences() != 0) {
479     if (SortIncludes)
480       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
481     else
482       FormatStyle->SortIncludes = FormatStyle::SI_Never;
483   }
484   unsigned CursorPosition = Cursor;
485   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
486                                        AssumedFileName, &CursorPosition);
487 
488   // To format JSON insert a variable to trick the code into thinking its
489   // JavaScript.
490   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
491     auto Err = Replaces.add(tooling::Replacement(
492         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
493     if (Err)
494       llvm::errs() << "Bad Json variable insertion\n";
495   }
496 
497   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
498   if (!ChangedCode) {
499     llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
500     return true;
501   }
502   // Get new affected ranges after sorting `#includes`.
503   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
504   FormattingAttemptStatus Status;
505   Replacements FormatChanges =
506       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
507   Replaces = Replaces.merge(FormatChanges);
508   if (OutputXML || DryRun) {
509     if (DryRun)
510       return emitReplacementWarnings(Replaces, AssumedFileName, Code);
511     else
512       outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
513   } else {
514     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
515         new llvm::vfs::InMemoryFileSystem);
516     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
517 
518     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
519     ClangFormatDiagConsumer IgnoreDiagnostics;
520     DiagnosticsEngine Diagnostics(
521         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
522         &IgnoreDiagnostics, false);
523     SourceManager Sources(Diagnostics, Files);
524     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
525                                    InMemoryFileSystem.get());
526     Rewriter Rewrite(Sources, LangOptions());
527     tooling::applyAllReplacements(Replaces, Rewrite);
528     if (Inplace) {
529       if (Rewrite.overwriteChangedFiles())
530         return true;
531     } else {
532       if (Cursor.getNumOccurrences() != 0) {
533         outs() << "{ \"Cursor\": "
534                << FormatChanges.getShiftedCodePosition(CursorPosition)
535                << ", \"IncompleteFormat\": "
536                << (Status.FormatComplete ? "false" : "true");
537         if (!Status.FormatComplete)
538           outs() << ", \"Line\": " << Status.Line;
539         outs() << " }\n";
540       }
541       Rewrite.getEditBuffer(ID).write(outs());
542     }
543   }
544   return ErrorOnIncompleteFormat && !Status.FormatComplete;
545 }
546 
547 } // namespace format
548 } // namespace clang
549 
550 static void PrintVersion(raw_ostream &OS) {
551   OS << clang::getClangToolFullVersion("clang-format") << '\n';
552 }
553 
554 // Dump the configuration.
555 static int dumpConfig() {
556   std::unique_ptr<llvm::MemoryBuffer> Code;
557   // We can't read the code to detect the language if there's no file name.
558   if (!FileNames.empty()) {
559     // Read in the code in case the filename alone isn't enough to detect the
560     // language.
561     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
562         MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);
563     if (std::error_code EC = CodeOrErr.getError()) {
564       llvm::errs() << EC.message() << "\n";
565       return 1;
566     }
567     Code = std::move(CodeOrErr.get());
568   }
569   llvm::Expected<clang::format::FormatStyle> FormatStyle =
570       clang::format::getStyle(Style,
571                               FileNames.empty() || FileNames[0] == "-"
572                                   ? AssumeFileName
573                                   : FileNames[0],
574                               FallbackStyle, Code ? Code->getBuffer() : "");
575   if (!FormatStyle) {
576     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
577     return 1;
578   }
579   std::string Config = clang::format::configurationAsText(*FormatStyle);
580   outs() << Config << "\n";
581   return 0;
582 }
583 
584 using String = SmallString<128>;
585 static String IgnoreDir;             // Directory of .clang-format-ignore file.
586 static String PrevDir;               // Directory of previous `FilePath`.
587 static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
588 
589 // Check whether `FilePath` is ignored according to the nearest
590 // .clang-format-ignore file based on the rules below:
591 // - A blank line is skipped.
592 // - Leading and trailing spaces of a line are trimmed.
593 // - A line starting with a hash (`#`) is a comment.
594 // - A non-comment line is a single pattern.
595 // - The slash (`/`) is used as the directory separator.
596 // - A pattern is relative to the directory of the .clang-format-ignore file (or
597 //   the root directory if the pattern starts with a slash).
598 // - A pattern is negated if it starts with a bang (`!`).
599 static bool isIgnored(StringRef FilePath) {
600   using namespace llvm::sys::fs;
601   if (!is_regular_file(FilePath))
602     return false;
603 
604   String Path;
605   String AbsPath{FilePath};
606 
607   using namespace llvm::sys::path;
608   make_absolute(AbsPath);
609   remove_dots(AbsPath, /*remove_dot_dot=*/true);
610 
611   if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
612     PrevDir = Dir;
613 
614     for (;;) {
615       Path = Dir;
616       append(Path, ".clang-format-ignore");
617       if (is_regular_file(Path))
618         break;
619       Dir = parent_path(Dir);
620       if (Dir.empty())
621         return false;
622     }
623 
624     IgnoreDir = convert_to_slash(Dir);
625 
626     std::ifstream IgnoreFile{Path.c_str()};
627     if (!IgnoreFile.good())
628       return false;
629 
630     Patterns.clear();
631 
632     for (std::string Line; std::getline(IgnoreFile, Line);) {
633       if (const auto Pattern{StringRef{Line}.trim()};
634           // Skip empty and comment lines.
635           !Pattern.empty() && Pattern[0] != '#') {
636         Patterns.push_back(Pattern);
637       }
638     }
639   }
640 
641   if (IgnoreDir.empty())
642     return false;
643 
644   const auto Pathname{convert_to_slash(AbsPath)};
645   for (const auto &Pat : Patterns) {
646     const bool IsNegated = Pat[0] == '!';
647     StringRef Pattern{Pat};
648     if (IsNegated)
649       Pattern = Pattern.drop_front();
650 
651     if (Pattern.empty())
652       continue;
653 
654     Pattern = Pattern.ltrim();
655 
656     // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
657     // This doesn't support patterns containing drive names (e.g. `C:`).
658     if (Pattern[0] != '/') {
659       Path = IgnoreDir;
660       append(Path, Style::posix, Pattern);
661       remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
662       Pattern = Path;
663     }
664 
665     if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
666       return true;
667   }
668 
669   return false;
670 }
671 
672 int main(int argc, const char **argv) {
673   llvm::InitLLVM X(argc, argv);
674 
675   cl::HideUnrelatedOptions(ClangFormatCategory);
676 
677   cl::SetVersionPrinter(PrintVersion);
678   cl::ParseCommandLineOptions(
679       argc, argv,
680       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
681       "code.\n\n"
682       "If no arguments are specified, it formats the code from standard input\n"
683       "and writes the result to the standard output.\n"
684       "If <file>s are given, it reformats the files. If -i is specified\n"
685       "together with <file>s, the files are edited in-place. Otherwise, the\n"
686       "result is written to the standard output.\n");
687 
688   if (Help) {
689     cl::PrintHelpMessage();
690     return 0;
691   }
692 
693   if (DumpConfig)
694     return dumpConfig();
695 
696   if (!Files.empty()) {
697     std::ifstream ExternalFileOfFiles{std::string(Files)};
698     std::string Line;
699     unsigned LineNo = 1;
700     while (std::getline(ExternalFileOfFiles, Line)) {
701       FileNames.push_back(Line);
702       LineNo++;
703     }
704     errs() << "Clang-formating " << LineNo << " files\n";
705   }
706 
707   if (FileNames.empty())
708     return clang::format::format("-", FailOnIncompleteFormat);
709 
710   if (FileNames.size() > 1 &&
711       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
712     errs() << "error: -offset, -length and -lines can only be used for "
713               "single file.\n";
714     return 1;
715   }
716 
717   unsigned FileNo = 1;
718   bool Error = false;
719   for (const auto &FileName : FileNames) {
720     if (isIgnored(FileName))
721       continue;
722     if (Verbose) {
723       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
724              << FileName << "\n";
725     }
726     Error |= clang::format::format(FileName, FailOnIncompleteFormat);
727   }
728   return Error ? 1 : 0;
729 }
730