xref: /llvm-project/clang/tools/clang-format/ClangFormat.cpp (revision a29e9e32c50273abffc53e3700bbc23985f0a7af)
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: .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 ? MemoryBuffer::getFileAsStream(FileName)
417                             : MemoryBuffer::getFileOrSTDIN(FileName);
418   if (std::error_code EC = CodeOrErr.getError()) {
419     errs() << EC.message() << "\n";
420     return true;
421   }
422   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
423   if (Code->getBufferSize() == 0)
424     return false; // Empty files are formatted correctly.
425 
426   StringRef BufStr = Code->getBuffer();
427 
428   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
429 
430   if (InvalidBOM) {
431     errs() << "error: encoding with unsupported byte order mark \""
432            << InvalidBOM << "\" detected";
433     if (!IsSTDIN)
434       errs() << " in file '" << FileName << "'";
435     errs() << ".\n";
436     return true;
437   }
438 
439   std::vector<tooling::Range> Ranges;
440   if (fillRanges(Code.get(), Ranges))
441     return true;
442   StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
443   if (AssumedFileName.empty()) {
444     llvm::errs() << "error: empty filenames are not allowed\n";
445     return true;
446   }
447 
448   llvm::Expected<FormatStyle> FormatStyle =
449       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
450                nullptr, WNoErrorList.isSet(WNoError::Unknown));
451   if (!FormatStyle) {
452     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
453     return true;
454   }
455 
456   StringRef QualifierAlignmentOrder = QualifierAlignment;
457 
458   FormatStyle->QualifierAlignment =
459       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
460           QualifierAlignmentOrder.lower())
461           .Case("right", FormatStyle::QAS_Right)
462           .Case("left", FormatStyle::QAS_Left)
463           .Default(FormatStyle->QualifierAlignment);
464 
465   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
466     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
467   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
468     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
469   } else if (QualifierAlignmentOrder.contains("type")) {
470     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
471     SmallVector<StringRef> Qualifiers;
472     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
473                                   /*KeepEmpty=*/false);
474     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
475   }
476 
477   if (SortIncludes.getNumOccurrences() != 0) {
478     if (SortIncludes)
479       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
480     else
481       FormatStyle->SortIncludes = FormatStyle::SI_Never;
482   }
483   unsigned CursorPosition = Cursor;
484   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
485                                        AssumedFileName, &CursorPosition);
486 
487   // To format JSON insert a variable to trick the code into thinking its
488   // JavaScript.
489   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
490     auto Err = Replaces.add(tooling::Replacement(
491         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
492     if (Err)
493       llvm::errs() << "Bad Json variable insertion\n";
494   }
495 
496   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
497   if (!ChangedCode) {
498     llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
499     return true;
500   }
501   // Get new affected ranges after sorting `#includes`.
502   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
503   FormattingAttemptStatus Status;
504   Replacements FormatChanges =
505       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
506   Replaces = Replaces.merge(FormatChanges);
507   if (OutputXML || DryRun) {
508     if (DryRun)
509       return emitReplacementWarnings(Replaces, AssumedFileName, Code);
510     else
511       outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
512   } else {
513     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
514         new llvm::vfs::InMemoryFileSystem);
515     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
516 
517     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
518     ClangFormatDiagConsumer IgnoreDiagnostics;
519     DiagnosticsEngine Diagnostics(
520         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
521         &IgnoreDiagnostics, false);
522     SourceManager Sources(Diagnostics, Files);
523     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
524                                    InMemoryFileSystem.get());
525     Rewriter Rewrite(Sources, LangOptions());
526     tooling::applyAllReplacements(Replaces, Rewrite);
527     if (Inplace) {
528       if (Rewrite.overwriteChangedFiles())
529         return true;
530     } else {
531       if (Cursor.getNumOccurrences() != 0) {
532         outs() << "{ \"Cursor\": "
533                << FormatChanges.getShiftedCodePosition(CursorPosition)
534                << ", \"IncompleteFormat\": "
535                << (Status.FormatComplete ? "false" : "true");
536         if (!Status.FormatComplete)
537           outs() << ", \"Line\": " << Status.Line;
538         outs() << " }\n";
539       }
540       Rewrite.getEditBuffer(ID).write(outs());
541     }
542   }
543   return ErrorOnIncompleteFormat && !Status.FormatComplete;
544 }
545 
546 } // namespace format
547 } // namespace clang
548 
549 static void PrintVersion(raw_ostream &OS) {
550   OS << clang::getClangToolFullVersion("clang-format") << '\n';
551 }
552 
553 // Dump the configuration.
554 static int dumpConfig() {
555   std::unique_ptr<llvm::MemoryBuffer> Code;
556   // We can't read the code to detect the language if there's no file name.
557   if (!FileNames.empty()) {
558     // Read in the code in case the filename alone isn't enough to detect the
559     // language.
560     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
561         MemoryBuffer::getFileOrSTDIN(FileNames[0]);
562     if (std::error_code EC = CodeOrErr.getError()) {
563       llvm::errs() << EC.message() << "\n";
564       return 1;
565     }
566     Code = std::move(CodeOrErr.get());
567   }
568   llvm::Expected<clang::format::FormatStyle> FormatStyle =
569       clang::format::getStyle(Style,
570                               FileNames.empty() || FileNames[0] == "-"
571                                   ? AssumeFileName
572                                   : FileNames[0],
573                               FallbackStyle, Code ? Code->getBuffer() : "");
574   if (!FormatStyle) {
575     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
576     return 1;
577   }
578   std::string Config = clang::format::configurationAsText(*FormatStyle);
579   outs() << Config << "\n";
580   return 0;
581 }
582 
583 using String = SmallString<128>;
584 static String IgnoreDir;             // Directory of .clang-format-ignore file.
585 static String PrevDir;               // Directory of previous `FilePath`.
586 static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
587 
588 // Check whether `FilePath` is ignored according to the nearest
589 // .clang-format-ignore file based on the rules below:
590 // - A blank line is skipped.
591 // - Leading and trailing spaces of a line are trimmed.
592 // - A line starting with a hash (`#`) is a comment.
593 // - A non-comment line is a single pattern.
594 // - The slash (`/`) is used as the directory separator.
595 // - A pattern is relative to the directory of the .clang-format-ignore file (or
596 //   the root directory if the pattern starts with a slash).
597 // - A pattern is negated if it starts with a bang (`!`).
598 static bool isIgnored(StringRef FilePath) {
599   using namespace llvm::sys::fs;
600   if (!is_regular_file(FilePath))
601     return false;
602 
603   String Path;
604   String AbsPath{FilePath};
605 
606   using namespace llvm::sys::path;
607   make_absolute(AbsPath);
608   remove_dots(AbsPath, /*remove_dot_dot=*/true);
609 
610   if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
611     PrevDir = Dir;
612 
613     for (;;) {
614       Path = Dir;
615       append(Path, ".clang-format-ignore");
616       if (is_regular_file(Path))
617         break;
618       Dir = parent_path(Dir);
619       if (Dir.empty())
620         return false;
621     }
622 
623     IgnoreDir = convert_to_slash(Dir);
624 
625     std::ifstream IgnoreFile{Path.c_str()};
626     if (!IgnoreFile.good())
627       return false;
628 
629     Patterns.clear();
630 
631     for (std::string Line; std::getline(IgnoreFile, Line);) {
632       if (const auto Pattern{StringRef{Line}.trim()};
633           // Skip empty and comment lines.
634           !Pattern.empty() && Pattern[0] != '#') {
635         Patterns.push_back(Pattern);
636       }
637     }
638   }
639 
640   if (IgnoreDir.empty())
641     return false;
642 
643   const auto Pathname{convert_to_slash(AbsPath)};
644   for (const auto &Pat : Patterns) {
645     const bool IsNegated = Pat[0] == '!';
646     StringRef Pattern{Pat};
647     if (IsNegated)
648       Pattern = Pattern.drop_front();
649 
650     if (Pattern.empty())
651       continue;
652 
653     Pattern = Pattern.ltrim();
654 
655     // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
656     // This doesn't support patterns containing drive names (e.g. `C:`).
657     if (Pattern[0] != '/') {
658       Path = IgnoreDir;
659       append(Path, Style::posix, Pattern);
660       remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
661       Pattern = Path;
662     }
663 
664     if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
665       return true;
666   }
667 
668   return false;
669 }
670 
671 int main(int argc, const char **argv) {
672   llvm::InitLLVM X(argc, argv);
673 
674   cl::HideUnrelatedOptions(ClangFormatCategory);
675 
676   cl::SetVersionPrinter(PrintVersion);
677   cl::ParseCommandLineOptions(
678       argc, argv,
679       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
680       "code.\n\n"
681       "If no arguments are specified, it formats the code from standard input\n"
682       "and writes the result to the standard output.\n"
683       "If <file>s are given, it reformats the files. If -i is specified\n"
684       "together with <file>s, the files are edited in-place. Otherwise, the\n"
685       "result is written to the standard output.\n");
686 
687   if (Help) {
688     cl::PrintHelpMessage();
689     return 0;
690   }
691 
692   if (DumpConfig)
693     return dumpConfig();
694 
695   if (!Files.empty()) {
696     std::ifstream ExternalFileOfFiles{std::string(Files)};
697     std::string Line;
698     unsigned LineNo = 1;
699     while (std::getline(ExternalFileOfFiles, Line)) {
700       FileNames.push_back(Line);
701       LineNo++;
702     }
703     errs() << "Clang-formating " << LineNo << " files\n";
704   }
705 
706   if (FileNames.empty())
707     return clang::format::format("-", FailOnIncompleteFormat);
708 
709   if (FileNames.size() > 1 &&
710       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
711     errs() << "error: -offset, -length and -lines can only be used for "
712               "single file.\n";
713     return 1;
714   }
715 
716   unsigned FileNo = 1;
717   bool Error = false;
718   for (const auto &FileName : FileNames) {
719     if (isIgnored(FileName))
720       continue;
721     if (Verbose) {
722       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
723              << FileName << "\n";
724     }
725     Error |= clang::format::format(FileName, FailOnIncompleteFormat);
726   }
727   return Error ? 1 : 0;
728 }
729