xref: /llvm-project/clang/tools/clang-format/ClangFormat.cpp (revision 9fa17fea3c2edb5903250b0da8f67861b81527b3)
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 "
340            << "offset='" << R.getOffset() << "' "
341            << "length='" << R.getLength() << "'>";
342     outputReplacementXML(R.getReplacementText());
343     outs() << "</replacement>\n";
344   }
345 }
346 
347 static bool
348 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
349                         const std::unique_ptr<llvm::MemoryBuffer> &Code) {
350   if (Replaces.empty())
351     return false;
352 
353   unsigned Errors = 0;
354   if (WarnFormat && !NoWarnFormat) {
355     SourceMgr Mgr;
356     const char *StartBuf = Code->getBufferStart();
357 
358     Mgr.AddNewSourceBuffer(
359         MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
360     for (const auto &R : Replaces) {
361       SMDiagnostic Diag = Mgr.GetMessage(
362           SMLoc::getFromPointer(StartBuf + R.getOffset()),
363           WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
364                            : SourceMgr::DiagKind::DK_Warning,
365           "code should be clang-formatted [-Wclang-format-violations]");
366 
367       Diag.print(nullptr, llvm::errs(), ShowColors && !NoShowColors);
368       if (ErrorLimit && ++Errors >= ErrorLimit)
369         break;
370     }
371   }
372   return WarningsAsErrors;
373 }
374 
375 static void outputXML(const Replacements &Replaces,
376                       const Replacements &FormatChanges,
377                       const FormattingAttemptStatus &Status,
378                       const cl::opt<unsigned> &Cursor,
379                       unsigned CursorPosition) {
380   outs() << "<?xml version='1.0'?>\n<replacements "
381             "xml:space='preserve' incomplete_format='"
382          << (Status.FormatComplete ? "false" : "true") << "'";
383   if (!Status.FormatComplete)
384     outs() << " line='" << Status.Line << "'";
385   outs() << ">\n";
386   if (Cursor.getNumOccurrences() != 0) {
387     outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
388            << "</cursor>\n";
389   }
390 
391   outputReplacementsXML(Replaces);
392   outs() << "</replacements>\n";
393 }
394 
395 class ClangFormatDiagConsumer : public DiagnosticConsumer {
396   virtual void anchor() {}
397 
398   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
399                         const Diagnostic &Info) override {
400 
401     SmallVector<char, 16> vec;
402     Info.FormatDiagnostic(vec);
403     errs() << "clang-format error:" << vec << "\n";
404   }
405 };
406 
407 // Returns true on error.
408 static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
409   const bool IsSTDIN = FileName == "-";
410   if (!OutputXML && Inplace && IsSTDIN) {
411     errs() << "error: cannot use -i when reading from stdin.\n";
412     return false;
413   }
414   // On Windows, overwriting a file with an open file mapping doesn't work,
415   // so read the whole file into memory when formatting in-place.
416   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
417       !OutputXML && Inplace
418           ? MemoryBuffer::getFileAsStream(FileName)
419           : MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
420   if (std::error_code EC = CodeOrErr.getError()) {
421     errs() << EC.message() << "\n";
422     return true;
423   }
424   std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
425   if (Code->getBufferSize() == 0)
426     return false; // Empty files are formatted correctly.
427 
428   StringRef BufStr = Code->getBuffer();
429 
430   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
431 
432   if (InvalidBOM) {
433     errs() << "error: encoding with unsupported byte order mark \""
434            << InvalidBOM << "\" detected";
435     if (!IsSTDIN)
436       errs() << " in file '" << FileName << "'";
437     errs() << ".\n";
438     return true;
439   }
440 
441   std::vector<tooling::Range> Ranges;
442   if (fillRanges(Code.get(), Ranges))
443     return true;
444   StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
445   if (AssumedFileName.empty()) {
446     llvm::errs() << "error: empty filenames are not allowed\n";
447     return true;
448   }
449 
450   Expected<FormatStyle> FormatStyle =
451       getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
452                nullptr, WNoErrorList.isSet(WNoError::Unknown));
453   if (!FormatStyle) {
454     llvm::errs() << toString(FormatStyle.takeError()) << "\n";
455     return true;
456   }
457 
458   StringRef QualifierAlignmentOrder = QualifierAlignment;
459 
460   FormatStyle->QualifierAlignment =
461       StringSwitch<FormatStyle::QualifierAlignmentStyle>(
462           QualifierAlignmentOrder.lower())
463           .Case("right", FormatStyle::QAS_Right)
464           .Case("left", FormatStyle::QAS_Left)
465           .Default(FormatStyle->QualifierAlignment);
466 
467   if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
468     FormatStyle->QualifierOrder = {"const", "volatile", "type"};
469   } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
470     FormatStyle->QualifierOrder = {"type", "const", "volatile"};
471   } else if (QualifierAlignmentOrder.contains("type")) {
472     FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
473     SmallVector<StringRef> Qualifiers;
474     QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
475                                   /*KeepEmpty=*/false);
476     FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
477   }
478 
479   if (SortIncludes.getNumOccurrences() != 0) {
480     if (SortIncludes)
481       FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
482     else
483       FormatStyle->SortIncludes = FormatStyle::SI_Never;
484   }
485   unsigned CursorPosition = Cursor;
486   Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
487                                        AssumedFileName, &CursorPosition);
488 
489   // To format JSON insert a variable to trick the code into thinking its
490   // JavaScript.
491   if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
492     auto Err = Replaces.add(tooling::Replacement(
493         tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
494     if (Err)
495       llvm::errs() << "Bad Json variable insertion\n";
496   }
497 
498   auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
499   if (!ChangedCode) {
500     llvm::errs() << toString(ChangedCode.takeError()) << "\n";
501     return true;
502   }
503   // Get new affected ranges after sorting `#includes`.
504   Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
505   FormattingAttemptStatus Status;
506   Replacements FormatChanges =
507       reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
508   Replaces = Replaces.merge(FormatChanges);
509   if (OutputXML || DryRun) {
510     if (DryRun)
511       return emitReplacementWarnings(Replaces, AssumedFileName, Code);
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   Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
570       Style,
571       FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
572       FallbackStyle, Code ? Code->getBuffer() : "");
573   if (!FormatStyle) {
574     llvm::errs() << toString(FormatStyle.takeError()) << "\n";
575     return 1;
576   }
577   std::string Config = clang::format::configurationAsText(*FormatStyle);
578   outs() << Config << "\n";
579   return 0;
580 }
581 
582 using String = SmallString<128>;
583 static String IgnoreDir;             // Directory of .clang-format-ignore file.
584 static String PrevDir;               // Directory of previous `FilePath`.
585 static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
586 
587 // Check whether `FilePath` is ignored according to the nearest
588 // .clang-format-ignore file based on the rules below:
589 // - A blank line is skipped.
590 // - Leading and trailing spaces of a line are trimmed.
591 // - A line starting with a hash (`#`) is a comment.
592 // - A non-comment line is a single pattern.
593 // - The slash (`/`) is used as the directory separator.
594 // - A pattern is relative to the directory of the .clang-format-ignore file (or
595 //   the root directory if the pattern starts with a slash).
596 // - A pattern is negated if it starts with a bang (`!`).
597 static bool isIgnored(StringRef FilePath) {
598   using namespace llvm::sys::fs;
599   if (!is_regular_file(FilePath))
600     return false;
601 
602   String Path;
603   String AbsPath{FilePath};
604 
605   using namespace llvm::sys::path;
606   make_absolute(AbsPath);
607   remove_dots(AbsPath, /*remove_dot_dot=*/true);
608 
609   if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
610     PrevDir = Dir;
611 
612     for (;;) {
613       Path = Dir;
614       append(Path, ".clang-format-ignore");
615       if (is_regular_file(Path))
616         break;
617       Dir = parent_path(Dir);
618       if (Dir.empty())
619         return false;
620     }
621 
622     IgnoreDir = convert_to_slash(Dir);
623 
624     std::ifstream IgnoreFile{Path.c_str()};
625     if (!IgnoreFile.good())
626       return false;
627 
628     Patterns.clear();
629 
630     for (std::string Line; std::getline(IgnoreFile, Line);) {
631       if (const auto Pattern{StringRef{Line}.trim()};
632           // Skip empty and comment lines.
633           !Pattern.empty() && Pattern[0] != '#') {
634         Patterns.push_back(Pattern);
635       }
636     }
637   }
638 
639   if (IgnoreDir.empty())
640     return false;
641 
642   const auto Pathname{convert_to_slash(AbsPath)};
643   for (const auto &Pat : Patterns) {
644     const bool IsNegated = Pat[0] == '!';
645     StringRef Pattern{Pat};
646     if (IsNegated)
647       Pattern = Pattern.drop_front();
648 
649     if (Pattern.empty())
650       continue;
651 
652     Pattern = Pattern.ltrim();
653 
654     // `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
655     // This doesn't support patterns containing drive names (e.g. `C:`).
656     if (Pattern[0] != '/') {
657       Path = IgnoreDir;
658       append(Path, Style::posix, Pattern);
659       remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
660       Pattern = Path;
661     }
662 
663     if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
664       return true;
665   }
666 
667   return false;
668 }
669 
670 int main(int argc, const char **argv) {
671   InitLLVM X(argc, argv);
672 
673   cl::HideUnrelatedOptions(ClangFormatCategory);
674 
675   cl::SetVersionPrinter(PrintVersion);
676   cl::ParseCommandLineOptions(
677       argc, argv,
678       "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
679       "code.\n\n"
680       "If no arguments are specified, it formats the code from standard input\n"
681       "and writes the result to the standard output.\n"
682       "If <file>s are given, it reformats the files. If -i is specified\n"
683       "together with <file>s, the files are edited in-place. Otherwise, the\n"
684       "result is written to the standard output.\n");
685 
686   if (Help) {
687     cl::PrintHelpMessage();
688     return 0;
689   }
690 
691   if (DumpConfig)
692     return dumpConfig();
693 
694   if (!Files.empty()) {
695     std::ifstream ExternalFileOfFiles{std::string(Files)};
696     std::string Line;
697     unsigned LineNo = 1;
698     while (std::getline(ExternalFileOfFiles, Line)) {
699       FileNames.push_back(Line);
700       LineNo++;
701     }
702     errs() << "Clang-formating " << LineNo << " files\n";
703   }
704 
705   if (FileNames.empty())
706     return clang::format::format("-", FailOnIncompleteFormat);
707 
708   if (FileNames.size() > 1 &&
709       (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
710     errs() << "error: -offset, -length and -lines can only be used for "
711               "single file.\n";
712     return 1;
713   }
714 
715   unsigned FileNo = 1;
716   bool Error = false;
717   for (const auto &FileName : FileNames) {
718     if (isIgnored(FileName))
719       continue;
720     if (Verbose) {
721       errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
722              << FileName << "\n";
723     }
724     Error |= clang::format::format(FileName, FailOnIncompleteFormat);
725   }
726   return Error ? 1 : 0;
727 }
728