xref: /llvm-project/clang/tools/clang-format/ClangFormat.cpp (revision 56ab56c85729976f29d5de2fd73912449cb6da7c)
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: .js .mjs .cjs .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 static cl::opt<bool> ListIgnored("list-ignored",
214                                  cl::desc("List ignored files."),
215                                  cl::cat(ClangFormatCategory), cl::Hidden);
216 
217 namespace clang {
218 namespace format {
219 
220 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
221                                  SourceManager &Sources, FileManager &Files,
222                                  llvm::vfs::InMemoryFileSystem *MemFS) {
223   MemFS->addFileNoOwn(FileName, 0, Source);
224   auto File = Files.getOptionalFileRef(FileName);
225   assert(File && "File not added to MemFS?");
226   return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
227 }
228 
229 // Parses <start line>:<end line> input to a pair of line numbers.
230 // Returns true on error.
231 static bool parseLineRange(StringRef Input, unsigned &FromLine,
232                            unsigned &ToLine) {
233   std::pair<StringRef, StringRef> LineRange = Input.split(':');
234   return LineRange.first.getAsInteger(0, FromLine) ||
235          LineRange.second.getAsInteger(0, ToLine);
236 }
237 
238 static bool fillRanges(MemoryBuffer *Code,
239                        std::vector<tooling::Range> &Ranges) {
240   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
241       new llvm::vfs::InMemoryFileSystem);
242   FileManager Files(FileSystemOptions(), InMemoryFileSystem);
243   DiagnosticsEngine Diagnostics(
244       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
245       new DiagnosticOptions);
246   SourceManager Sources(Diagnostics, Files);
247   FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
248                                  InMemoryFileSystem.get());
249   if (!LineRanges.empty()) {
250     if (!Offsets.empty() || !Lengths.empty()) {
251       errs() << "error: cannot use -lines with -offset/-length\n";
252       return true;
253     }
254 
255     for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
256       unsigned FromLine, ToLine;
257       if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
258         errs() << "error: invalid <start line>:<end line> pair\n";
259         return true;
260       }
261       if (FromLine < 1) {
262         errs() << "error: start line should be at least 1\n";
263         return true;
264       }
265       if (FromLine > ToLine) {
266         errs() << "error: start line should not exceed end line\n";
267         return true;
268       }
269       SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
270       SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
271       if (Start.isInvalid() || End.isInvalid())
272         return true;
273       unsigned Offset = Sources.getFileOffset(Start);
274       unsigned Length = Sources.getFileOffset(End) - Offset;
275       Ranges.push_back(tooling::Range(Offset, Length));
276     }
277     return false;
278   }
279 
280   if (Offsets.empty())
281     Offsets.push_back(0);
282   if (Offsets.size() != Lengths.size() &&
283       !(Offsets.size() == 1 && Lengths.empty())) {
284     errs() << "error: number of -offset and -length arguments must match.\n";
285     return true;
286   }
287   for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
288     if (Offsets[i] >= Code->getBufferSize()) {
289       errs() << "error: offset " << Offsets[i] << " is outside the file\n";
290       return true;
291     }
292     SourceLocation Start =
293         Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
294     SourceLocation End;
295     if (i < Lengths.size()) {
296       if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
297         errs() << "error: invalid length " << Lengths[i]
298                << ", offset + length (" << Offsets[i] + Lengths[i]
299                << ") is outside the file.\n";
300         return true;
301       }
302       End = Start.getLocWithOffset(Lengths[i]);
303     } else {
304       End = Sources.getLocForEndOfFile(ID);
305     }
306     unsigned Offset = Sources.getFileOffset(Start);
307     unsigned Length = Sources.getFileOffset(End) - Offset;
308     Ranges.push_back(tooling::Range(Offset, Length));
309   }
310   return false;
311 }
312 
313 static void outputReplacementXML(StringRef Text) {
314   // FIXME: When we sort includes, we need to make sure the stream is correct
315   // utf-8.
316   size_t From = 0;
317   size_t Index;
318   while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
319     outs() << Text.substr(From, Index - From);
320     switch (Text[Index]) {
321     case '\n':
322       outs() << "&#10;";
323       break;
324     case '\r':
325       outs() << "&#13;";
326       break;
327     case '<':
328       outs() << "&lt;";
329       break;
330     case '&':
331       outs() << "&amp;";
332       break;
333     default:
334       llvm_unreachable("Unexpected character encountered!");
335     }
336     From = Index + 1;
337   }
338   outs() << Text.substr(From);
339 }
340 
341 static void outputReplacementsXML(const Replacements &Replaces) {
342   for (const auto &R : Replaces) {
343     outs() << "<replacement "
344            << "offset='" << R.getOffset() << "' "
345            << "length='" << R.getLength() << "'>";
346     outputReplacementXML(R.getReplacementText());
347     outs() << "</replacement>\n";
348   }
349 }
350 
351 static bool
352 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
353                         const std::unique_ptr<llvm::MemoryBuffer> &Code) {
354   unsigned Errors = 0;
355   if (WarnFormat && !NoWarnFormat) {
356     SourceMgr Mgr;
357     const char *StartBuf = Code->getBufferStart();
358 
359     Mgr.AddNewSourceBuffer(
360         MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
361     for (const auto &R : Replaces) {
362       SMDiagnostic Diag = Mgr.GetMessage(
363           SMLoc::getFromPointer(StartBuf + R.getOffset()),
364           WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
365                            : SourceMgr::DiagKind::DK_Warning,
366           "code should be clang-formatted [-Wclang-format-violations]");
367 
368       Diag.print(nullptr, llvm::errs(), ShowColors && !NoShowColors);
369       if (ErrorLimit && ++Errors >= ErrorLimit)
370         break;
371     }
372   }
373   return WarningsAsErrors;
374 }
375 
376 static void outputXML(const Replacements &Replaces,
377                       const Replacements &FormatChanges,
378                       const FormattingAttemptStatus &Status,
379                       const cl::opt<unsigned> &Cursor,
380                       unsigned CursorPosition) {
381   outs() << "<?xml version='1.0'?>\n<replacements "
382             "xml:space='preserve' incomplete_format='"
383          << (Status.FormatComplete ? "false" : "true") << "'";
384   if (!Status.FormatComplete)
385     outs() << " line='" << Status.Line << "'";
386   outs() << ">\n";
387   if (Cursor.getNumOccurrences() != 0) {
388     outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
389            << "</cursor>\n";
390   }
391 
392   outputReplacementsXML(Replaces);
393   outs() << "</replacements>\n";
394 }
395 
396 class ClangFormatDiagConsumer : public DiagnosticConsumer {
397   virtual void anchor() {}
398 
399   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
400                         const Diagnostic &Info) override {
401 
402     SmallVector<char, 16> vec;
403     Info.FormatDiagnostic(vec);
404     errs() << "clang-format error:" << vec << "\n";
405   }
406 };
407 
408 // Returns true on error.
409 static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
410   const bool IsSTDIN = FileName == "-";
411   if (!OutputXML && Inplace && IsSTDIN) {
412     errs() << "error: cannot use -i when reading from stdin.\n";
413     return true;
414   }
415   // On Windows, overwriting a file with an open file mapping doesn't work,
416   // so read the whole file into memory when formatting in-place.
417   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
418       !OutputXML && Inplace
419           ? MemoryBuffer::getFileAsStream(FileName)
420           : MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
421   if (std::error_code EC = CodeOrErr.getError()) {
422     errs() << FileName << ": " << EC.message() << "\n";
423     return true;
424   }
425   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
426   if (Code->getBufferSize() == 0)
427     return false; // Empty files are formatted correctly.
428 
429   StringRef BufStr = Code->getBuffer();
430 
431   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
432 
433   if (InvalidBOM) {
434     errs() << "error: encoding with unsupported byte order mark \""
435            << InvalidBOM << "\" detected";
436     if (!IsSTDIN)
437       errs() << " in file '" << FileName << "'";
438     errs() << ".\n";
439     return true;
440   }
441 
442   std::vector<tooling::Range> Ranges;
443   if (fillRanges(Code.get(), Ranges))
444     return true;
445   StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
446   if (AssumedFileName.empty()) {
447     llvm::errs() << "error: empty filenames are not allowed\n";
448     return true;
449   }
450 
451   Expected<FormatStyle> FormatStyle =
452       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
453                nullptr, WNoErrorList.isSet(WNoError::Unknown));
454   if (!FormatStyle) {
455     llvm::errs() << toString(FormatStyle.takeError()) << "\n";
456     return true;
457   }
458 
459   StringRef QualifierAlignmentOrder = QualifierAlignment;
460 
461   FormatStyle->QualifierAlignment =
462       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
463           QualifierAlignmentOrder.lower())
464           .Case("right", FormatStyle::QAS_Right)
465           .Case("left", FormatStyle::QAS_Left)
466           .Default(FormatStyle->QualifierAlignment);
467 
468   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
469     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
470   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
471     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
472   } else if (QualifierAlignmentOrder.contains("type")) {
473     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
474     SmallVector<StringRef> Qualifiers;
475     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
476                                   /*KeepEmpty=*/false);
477     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
478   }
479 
480   if (SortIncludes.getNumOccurrences() != 0) {
481     if (SortIncludes)
482       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
483     else
484       FormatStyle->SortIncludes = FormatStyle::SI_Never;
485   }
486   unsigned CursorPosition = Cursor;
487   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
488                                        AssumedFileName, &CursorPosition);
489 
490   const bool IsJson = FormatStyle->isJson();
491 
492   // To format JSON insert a variable to trick the code into thinking its
493   // JavaScript.
494   if (IsJson && !FormatStyle->DisableFormat) {
495     auto Err = Replaces.add(tooling::Replacement(
496         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
497     if (Err)
498       llvm::errs() << "Bad Json variable insertion\n";
499   }
500 
501   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
502   if (!ChangedCode) {
503     llvm::errs() << toString(ChangedCode.takeError()) << "\n";
504     return true;
505   }
506   // Get new affected ranges after sorting `#includes`.
507   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
508   FormattingAttemptStatus Status;
509   Replacements FormatChanges =
510       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
511   Replaces = Replaces.merge(FormatChanges);
512   if (DryRun) {
513     return Replaces.size() > (IsJson ? 1u : 0u) &&
514            emitReplacementWarnings(Replaces, AssumedFileName, Code);
515   }
516   if (OutputXML) {
517     outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
518   } else {
519     IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
520         new llvm::vfs::InMemoryFileSystem);
521     FileManager Files(FileSystemOptions(), InMemoryFileSystem);
522 
523     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
524     ClangFormatDiagConsumer IgnoreDiagnostics;
525     DiagnosticsEngine Diagnostics(
526         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
527         &IgnoreDiagnostics, false);
528     SourceManager Sources(Diagnostics, Files);
529     FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
530                                    InMemoryFileSystem.get());
531     Rewriter Rewrite(Sources, LangOptions());
532     tooling::applyAllReplacements(Replaces, Rewrite);
533     if (Inplace) {
534       if (Rewrite.overwriteChangedFiles())
535         return true;
536     } else {
537       if (Cursor.getNumOccurrences() != 0) {
538         outs() << "{ \"Cursor\": "
539                << FormatChanges.getShiftedCodePosition(CursorPosition)
540                << ", \"IncompleteFormat\": "
541                << (Status.FormatComplete ? "false" : "true");
542         if (!Status.FormatComplete)
543           outs() << ", \"Line\": " << Status.Line;
544         outs() << " }\n";
545       }
546       Rewrite.getEditBuffer(ID).write(outs());
547     }
548   }
549   return ErrorOnIncompleteFormat && !Status.FormatComplete;
550 }
551 
552 } // namespace format
553 } // namespace clang
554 
555 static void PrintVersion(raw_ostream &OS) {
556   OS << clang::getClangToolFullVersion("clang-format") << '\n';
557 }
558 
559 // Dump the configuration.
560 static int dumpConfig() {
561   std::unique_ptr<llvm::MemoryBuffer> Code;
562   // We can't read the code to detect the language if there's no file name.
563   if (!FileNames.empty()) {
564     // Read in the code in case the filename alone isn't enough to detect the
565     // language.
566     ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
567         MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);
568     if (std::error_code EC = CodeOrErr.getError()) {
569       llvm::errs() << EC.message() << "\n";
570       return 1;
571     }
572     Code = std::move(CodeOrErr.get());
573   }
574   Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
575       Style,
576       FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
577       FallbackStyle, Code ? Code->getBuffer() : "");
578   if (!FormatStyle) {
579     llvm::errs() << toString(FormatStyle.takeError()) << "\n";
580     return 1;
581   }
582   std::string Config = clang::format::configurationAsText(*FormatStyle);
583   outs() << Config << "\n";
584   return 0;
585 }
586 
587 using String = SmallString<128>;
588 static String IgnoreDir;             // Directory of .clang-format-ignore file.
589 static String PrevDir;               // Directory of previous `FilePath`.
590 static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
591 
592 // Check whether `FilePath` is ignored according to the nearest
593 // .clang-format-ignore file based on the rules below:
594 // - A blank line is skipped.
595 // - Leading and trailing spaces of a line are trimmed.
596 // - A line starting with a hash (`#`) is a comment.
597 // - A non-comment line is a single pattern.
598 // - The slash (`/`) is used as the directory separator.
599 // - A pattern is relative to the directory of the .clang-format-ignore file (or
600 //   the root directory if the pattern starts with a slash).
601 // - A pattern is negated if it starts with a bang (`!`).
602 static bool isIgnored(StringRef FilePath) {
603   using namespace llvm::sys::fs;
604   if (!is_regular_file(FilePath))
605     return false;
606 
607   String Path;
608   String AbsPath{FilePath};
609 
610   using namespace llvm::sys::path;
611   make_absolute(AbsPath);
612   remove_dots(AbsPath, /*remove_dot_dot=*/true);
613 
614   if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
615     PrevDir = Dir;
616 
617     for (;;) {
618       Path = Dir;
619       append(Path, ".clang-format-ignore");
620       if (is_regular_file(Path))
621         break;
622       Dir = parent_path(Dir);
623       if (Dir.empty())
624         return false;
625     }
626 
627     IgnoreDir = convert_to_slash(Dir);
628 
629     std::ifstream IgnoreFile{Path.c_str()};
630     if (!IgnoreFile.good())
631       return false;
632 
633     Patterns.clear();
634 
635     for (std::string Line; std::getline(IgnoreFile, Line);) {
636       if (const auto Pattern{StringRef{Line}.trim()};
637           // Skip empty and comment lines.
638           !Pattern.empty() && Pattern[0] != '#') {
639         Patterns.push_back(Pattern);
640       }
641     }
642   }
643 
644   if (IgnoreDir.empty())
645     return false;
646 
647   const auto Pathname{convert_to_slash(AbsPath)};
648   for (const auto &Pat : Patterns) {
649     const bool IsNegated = Pat[0] == '!';
650     StringRef Pattern{Pat};
651     if (IsNegated)
652       Pattern = Pattern.drop_front();
653 
654     if (Pattern.empty())
655       continue;
656 
657     Pattern = Pattern.ltrim();
658 
659     // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
660     // This doesn't support patterns containing drive names (e.g. `C:`).
661     if (Pattern[0] != '/') {
662       Path = IgnoreDir;
663       append(Path, Style::posix, Pattern);
664       remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
665       Pattern = Path;
666     }
667 
668     if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
669       return true;
670   }
671 
672   return false;
673 }
674 
675 int main(int argc, const char **argv) {
676   InitLLVM X(argc, argv);
677 
678   cl::HideUnrelatedOptions(ClangFormatCategory);
679 
680   cl::SetVersionPrinter(PrintVersion);
681   cl::ParseCommandLineOptions(
682       argc, argv,
683       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
684       "code.\n\n"
685       "If no arguments are specified, it formats the code from standard input\n"
686       "and writes the result to the standard output.\n"
687       "If <file>s are given, it reformats the files. If -i is specified\n"
688       "together with <file>s, the files are edited in-place. Otherwise, the\n"
689       "result is written to the standard output.\n");
690 
691   if (Help) {
692     cl::PrintHelpMessage();
693     return 0;
694   }
695 
696   if (DumpConfig)
697     return dumpConfig();
698 
699   if (!Files.empty()) {
700     std::ifstream ExternalFileOfFiles{std::string(Files)};
701     std::string Line;
702     unsigned LineNo = 1;
703     while (std::getline(ExternalFileOfFiles, Line)) {
704       FileNames.push_back(Line);
705       LineNo++;
706     }
707     errs() << "Clang-formatting " << LineNo << " files\n";
708   }
709 
710   if (FileNames.empty()) {
711     if (isIgnored(AssumeFileName))
712       return 0;
713     return clang::format::format("-", FailOnIncompleteFormat);
714   }
715 
716   if (FileNames.size() > 1 &&
717       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
718     errs() << "error: -offset, -length and -lines can only be used for "
719               "single file.\n";
720     return 1;
721   }
722 
723   unsigned FileNo = 1;
724   bool Error = false;
725   for (const auto &FileName : FileNames) {
726     const bool Ignored = isIgnored(FileName);
727     if (ListIgnored) {
728       if (Ignored)
729         outs() << FileName << '\n';
730       continue;
731     }
732     if (Ignored)
733       continue;
734     if (Verbose) {
735       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
736              << FileName << "\n";
737     }
738     Error |= clang::format::format(FileName, FailOnIncompleteFormat);
739   }
740   return Error ? 1 : 0;
741 }
742