brintos

brintos / llvm-project-archived public Read only

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