Path: blob/main/contrib/llvm-project/clang/tools/clang-format/ClangFormat.cpp
35260 views
//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements a clang-format tool that automatically formats10/// (fragments of) C++ code.11///12//===----------------------------------------------------------------------===//1314#include "../../lib/Format/MatchFilePath.h"15#include "clang/Basic/Diagnostic.h"16#include "clang/Basic/DiagnosticOptions.h"17#include "clang/Basic/FileManager.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Basic/Version.h"20#include "clang/Format/Format.h"21#include "clang/Rewrite/Core/Rewriter.h"22#include "llvm/ADT/StringSwitch.h"23#include "llvm/Support/CommandLine.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/InitLLVM.h"26#include "llvm/Support/Process.h"27#include <fstream>2829using namespace llvm;30using clang::tooling::Replacements;3132static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);3334// Mark all our options with this category, everything else (except for -version35// and -help) will be hidden.36static cl::OptionCategory ClangFormatCategory("Clang-format options");3738static cl::list<unsigned>39Offsets("offset",40cl::desc("Format a range starting at this byte offset.\n"41"Multiple ranges can be formatted by specifying\n"42"several -offset and -length pairs.\n"43"Can only be used with one input file."),44cl::cat(ClangFormatCategory));45static cl::list<unsigned>46Lengths("length",47cl::desc("Format a range of this length (in bytes).\n"48"Multiple ranges can be formatted by specifying\n"49"several -offset and -length pairs.\n"50"When only a single -offset is specified without\n"51"-length, clang-format will format up to the end\n"52"of the file.\n"53"Can only be used with one input file."),54cl::cat(ClangFormatCategory));55static cl::list<std::string>56LineRanges("lines",57cl::desc("<start line>:<end line> - format a range of\n"58"lines (both 1-based).\n"59"Multiple ranges can be formatted by specifying\n"60"several -lines arguments.\n"61"Can't be used with -offset and -length.\n"62"Can only be used with one input file."),63cl::cat(ClangFormatCategory));64static cl::opt<std::string>65Style("style", cl::desc(clang::format::StyleOptionHelpDescription),66cl::init(clang::format::DefaultFormatStyle),67cl::cat(ClangFormatCategory));68static cl::opt<std::string>69FallbackStyle("fallback-style",70cl::desc("The name of the predefined style used as a\n"71"fallback in case clang-format is invoked with\n"72"-style=file, but can not find the .clang-format\n"73"file to use. Defaults to 'LLVM'.\n"74"Use -fallback-style=none to skip formatting."),75cl::init(clang::format::DefaultFallbackStyle),76cl::cat(ClangFormatCategory));7778static cl::opt<std::string> AssumeFileName(79"assume-filename",80cl::desc("Set filename used to determine the language and to find\n"81".clang-format file.\n"82"Only used when reading from stdin.\n"83"If this is not passed, the .clang-format file is searched\n"84"relative to the current working directory when reading stdin.\n"85"Unrecognized filenames are treated as C++.\n"86"supported:\n"87" CSharp: .cs\n"88" Java: .java\n"89" JavaScript: .mjs .js .ts\n"90" Json: .json\n"91" Objective-C: .m .mm\n"92" Proto: .proto .protodevel\n"93" TableGen: .td\n"94" TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"95" Verilog: .sv .svh .v .vh"),96cl::init("<stdin>"), cl::cat(ClangFormatCategory));9798static cl::opt<bool> Inplace("i",99cl::desc("Inplace edit <file>s, if specified."),100cl::cat(ClangFormatCategory));101102static cl::opt<bool> OutputXML("output-replacements-xml",103cl::desc("Output replacements as XML."),104cl::cat(ClangFormatCategory));105static cl::opt<bool>106DumpConfig("dump-config",107cl::desc("Dump configuration options to stdout and exit.\n"108"Can be used with -style option."),109cl::cat(ClangFormatCategory));110static cl::opt<unsigned>111Cursor("cursor",112cl::desc("The position of the cursor when invoking\n"113"clang-format from an editor integration"),114cl::init(0), cl::cat(ClangFormatCategory));115116static cl::opt<bool>117SortIncludes("sort-includes",118cl::desc("If set, overrides the include sorting behavior\n"119"determined by the SortIncludes style flag"),120cl::cat(ClangFormatCategory));121122static cl::opt<std::string> QualifierAlignment(123"qualifier-alignment",124cl::desc("If set, overrides the qualifier alignment style\n"125"determined by the QualifierAlignment style flag"),126cl::init(""), cl::cat(ClangFormatCategory));127128static cl::opt<std::string> Files(129"files",130cl::desc("A file containing a list of files to process, one per line."),131cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));132133static cl::opt<bool>134Verbose("verbose", cl::desc("If set, shows the list of processed files"),135cl::cat(ClangFormatCategory));136137// Use --dry-run to match other LLVM tools when you mean do it but don't138// actually do it139static cl::opt<bool>140DryRun("dry-run",141cl::desc("If set, do not actually make the formatting changes"),142cl::cat(ClangFormatCategory));143144// Use -n as a common command as an alias for --dry-run. (git and make use -n)145static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),146cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),147cl::NotHidden);148149// Emulate being able to turn on/off the warning.150static cl::opt<bool>151WarnFormat("Wclang-format-violations",152cl::desc("Warnings about individual formatting changes needed. "153"Used only with --dry-run or -n"),154cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);155156static cl::opt<bool>157NoWarnFormat("Wno-clang-format-violations",158cl::desc("Do not warn about individual formatting changes "159"needed. Used only with --dry-run or -n"),160cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);161162static cl::opt<unsigned> ErrorLimit(163"ferror-limit",164cl::desc("Set the maximum number of clang-format errors to emit\n"165"before stopping (0 = no limit).\n"166"Used only with --dry-run or -n"),167cl::init(0), cl::cat(ClangFormatCategory));168169static cl::opt<bool>170WarningsAsErrors("Werror",171cl::desc("If set, changes formatting warnings to errors"),172cl::cat(ClangFormatCategory));173174namespace {175enum class WNoError { Unknown };176}177178static cl::bits<WNoError> WNoErrorList(179"Wno-error",180cl::desc("If set don't error out on the specified warning type."),181cl::values(182clEnumValN(WNoError::Unknown, "unknown",183"If set, unknown format options are only warned about.\n"184"This can be used to enable formatting, even if the\n"185"configuration contains unknown (newer) options.\n"186"Use with caution, as this might lead to dramatically\n"187"differing format depending on an option being\n"188"supported or not.")),189cl::cat(ClangFormatCategory));190191static cl::opt<bool>192ShowColors("fcolor-diagnostics",193cl::desc("If set, and on a color-capable terminal controls "194"whether or not to print diagnostics in color"),195cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);196197static cl::opt<bool>198NoShowColors("fno-color-diagnostics",199cl::desc("If set, and on a color-capable terminal controls "200"whether or not to print diagnostics in color"),201cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);202203static cl::list<std::string> FileNames(cl::Positional,204cl::desc("[@<file>] [<file> ...]"),205cl::cat(ClangFormatCategory));206207static cl::opt<bool> FailOnIncompleteFormat(208"fail-on-incomplete-format",209cl::desc("If set, fail with exit code 1 on incomplete format."),210cl::init(false), cl::cat(ClangFormatCategory));211212static cl::opt<bool> ListIgnored("list-ignored",213cl::desc("List ignored files."),214cl::cat(ClangFormatCategory), cl::Hidden);215216namespace clang {217namespace format {218219static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,220SourceManager &Sources, FileManager &Files,221llvm::vfs::InMemoryFileSystem *MemFS) {222MemFS->addFileNoOwn(FileName, 0, Source);223auto File = Files.getOptionalFileRef(FileName);224assert(File && "File not added to MemFS?");225return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);226}227228// Parses <start line>:<end line> input to a pair of line numbers.229// Returns true on error.230static bool parseLineRange(StringRef Input, unsigned &FromLine,231unsigned &ToLine) {232std::pair<StringRef, StringRef> LineRange = Input.split(':');233return LineRange.first.getAsInteger(0, FromLine) ||234LineRange.second.getAsInteger(0, ToLine);235}236237static bool fillRanges(MemoryBuffer *Code,238std::vector<tooling::Range> &Ranges) {239IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(240new llvm::vfs::InMemoryFileSystem);241FileManager Files(FileSystemOptions(), InMemoryFileSystem);242DiagnosticsEngine Diagnostics(243IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),244new DiagnosticOptions);245SourceManager Sources(Diagnostics, Files);246FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,247InMemoryFileSystem.get());248if (!LineRanges.empty()) {249if (!Offsets.empty() || !Lengths.empty()) {250errs() << "error: cannot use -lines with -offset/-length\n";251return true;252}253254for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {255unsigned FromLine, ToLine;256if (parseLineRange(LineRanges[i], FromLine, ToLine)) {257errs() << "error: invalid <start line>:<end line> pair\n";258return true;259}260if (FromLine < 1) {261errs() << "error: start line should be at least 1\n";262return true;263}264if (FromLine > ToLine) {265errs() << "error: start line should not exceed end line\n";266return true;267}268SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);269SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);270if (Start.isInvalid() || End.isInvalid())271return true;272unsigned Offset = Sources.getFileOffset(Start);273unsigned Length = Sources.getFileOffset(End) - Offset;274Ranges.push_back(tooling::Range(Offset, Length));275}276return false;277}278279if (Offsets.empty())280Offsets.push_back(0);281if (Offsets.size() != Lengths.size() &&282!(Offsets.size() == 1 && Lengths.empty())) {283errs() << "error: number of -offset and -length arguments must match.\n";284return true;285}286for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {287if (Offsets[i] >= Code->getBufferSize()) {288errs() << "error: offset " << Offsets[i] << " is outside the file\n";289return true;290}291SourceLocation Start =292Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);293SourceLocation End;294if (i < Lengths.size()) {295if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {296errs() << "error: invalid length " << Lengths[i]297<< ", offset + length (" << Offsets[i] + Lengths[i]298<< ") is outside the file.\n";299return true;300}301End = Start.getLocWithOffset(Lengths[i]);302} else {303End = Sources.getLocForEndOfFile(ID);304}305unsigned Offset = Sources.getFileOffset(Start);306unsigned Length = Sources.getFileOffset(End) - Offset;307Ranges.push_back(tooling::Range(Offset, Length));308}309return false;310}311312static void outputReplacementXML(StringRef Text) {313// FIXME: When we sort includes, we need to make sure the stream is correct314// utf-8.315size_t From = 0;316size_t Index;317while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {318outs() << Text.substr(From, Index - From);319switch (Text[Index]) {320case '\n':321outs() << " ";322break;323case '\r':324outs() << " ";325break;326case '<':327outs() << "<";328break;329case '&':330outs() << "&";331break;332default:333llvm_unreachable("Unexpected character encountered!");334}335From = Index + 1;336}337outs() << Text.substr(From);338}339340static void outputReplacementsXML(const Replacements &Replaces) {341for (const auto &R : Replaces) {342outs() << "<replacement "343<< "offset='" << R.getOffset() << "' "344<< "length='" << R.getLength() << "'>";345outputReplacementXML(R.getReplacementText());346outs() << "</replacement>\n";347}348}349350static bool351emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,352const std::unique_ptr<llvm::MemoryBuffer> &Code) {353if (Replaces.empty())354return false;355356unsigned Errors = 0;357if (WarnFormat && !NoWarnFormat) {358SourceMgr Mgr;359const char *StartBuf = Code->getBufferStart();360361Mgr.AddNewSourceBuffer(362MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());363for (const auto &R : Replaces) {364SMDiagnostic Diag = Mgr.GetMessage(365SMLoc::getFromPointer(StartBuf + R.getOffset()),366WarningsAsErrors ? SourceMgr::DiagKind::DK_Error367: SourceMgr::DiagKind::DK_Warning,368"code should be clang-formatted [-Wclang-format-violations]");369370Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));371if (ErrorLimit && ++Errors >= ErrorLimit)372break;373}374}375return WarningsAsErrors;376}377378static void outputXML(const Replacements &Replaces,379const Replacements &FormatChanges,380const FormattingAttemptStatus &Status,381const cl::opt<unsigned> &Cursor,382unsigned CursorPosition) {383outs() << "<?xml version='1.0'?>\n<replacements "384"xml:space='preserve' incomplete_format='"385<< (Status.FormatComplete ? "false" : "true") << "'";386if (!Status.FormatComplete)387outs() << " line='" << Status.Line << "'";388outs() << ">\n";389if (Cursor.getNumOccurrences() != 0) {390outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)391<< "</cursor>\n";392}393394outputReplacementsXML(Replaces);395outs() << "</replacements>\n";396}397398class ClangFormatDiagConsumer : public DiagnosticConsumer {399virtual void anchor() {}400401void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,402const Diagnostic &Info) override {403404SmallVector<char, 16> vec;405Info.FormatDiagnostic(vec);406errs() << "clang-format error:" << vec << "\n";407}408};409410// Returns true on error.411static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {412const bool IsSTDIN = FileName == "-";413if (!OutputXML && Inplace && IsSTDIN) {414errs() << "error: cannot use -i when reading from stdin.\n";415return false;416}417// On Windows, overwriting a file with an open file mapping doesn't work,418// so read the whole file into memory when formatting in-place.419ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =420!OutputXML && Inplace421? MemoryBuffer::getFileAsStream(FileName)422: MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);423if (std::error_code EC = CodeOrErr.getError()) {424errs() << EC.message() << "\n";425return true;426}427std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());428if (Code->getBufferSize() == 0)429return false; // Empty files are formatted correctly.430431StringRef BufStr = Code->getBuffer();432433const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);434435if (InvalidBOM) {436errs() << "error: encoding with unsupported byte order mark \""437<< InvalidBOM << "\" detected";438if (!IsSTDIN)439errs() << " in file '" << FileName << "'";440errs() << ".\n";441return true;442}443444std::vector<tooling::Range> Ranges;445if (fillRanges(Code.get(), Ranges))446return true;447StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;448if (AssumedFileName.empty()) {449llvm::errs() << "error: empty filenames are not allowed\n";450return true;451}452453Expected<FormatStyle> FormatStyle =454getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),455nullptr, WNoErrorList.isSet(WNoError::Unknown));456if (!FormatStyle) {457llvm::errs() << toString(FormatStyle.takeError()) << "\n";458return true;459}460461StringRef QualifierAlignmentOrder = QualifierAlignment;462463FormatStyle->QualifierAlignment =464StringSwitch<FormatStyle::QualifierAlignmentStyle>(465QualifierAlignmentOrder.lower())466.Case("right", FormatStyle::QAS_Right)467.Case("left", FormatStyle::QAS_Left)468.Default(FormatStyle->QualifierAlignment);469470if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {471FormatStyle->QualifierOrder = {"const", "volatile", "type"};472} else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {473FormatStyle->QualifierOrder = {"type", "const", "volatile"};474} else if (QualifierAlignmentOrder.contains("type")) {475FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;476SmallVector<StringRef> Qualifiers;477QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,478/*KeepEmpty=*/false);479FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};480}481482if (SortIncludes.getNumOccurrences() != 0) {483if (SortIncludes)484FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;485else486FormatStyle->SortIncludes = FormatStyle::SI_Never;487}488unsigned CursorPosition = Cursor;489Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,490AssumedFileName, &CursorPosition);491492// To format JSON insert a variable to trick the code into thinking its493// JavaScript.494if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {495auto Err = Replaces.add(tooling::Replacement(496tooling::Replacement(AssumedFileName, 0, 0, "x = ")));497if (Err)498llvm::errs() << "Bad Json variable insertion\n";499}500501auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);502if (!ChangedCode) {503llvm::errs() << toString(ChangedCode.takeError()) << "\n";504return true;505}506// Get new affected ranges after sorting `#includes`.507Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);508FormattingAttemptStatus Status;509Replacements FormatChanges =510reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);511Replaces = Replaces.merge(FormatChanges);512if (OutputXML || DryRun) {513if (DryRun)514return emitReplacementWarnings(Replaces, AssumedFileName, Code);515outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);516} else {517IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(518new llvm::vfs::InMemoryFileSystem);519FileManager Files(FileSystemOptions(), InMemoryFileSystem);520521IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());522ClangFormatDiagConsumer IgnoreDiagnostics;523DiagnosticsEngine Diagnostics(524IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,525&IgnoreDiagnostics, false);526SourceManager Sources(Diagnostics, Files);527FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,528InMemoryFileSystem.get());529Rewriter Rewrite(Sources, LangOptions());530tooling::applyAllReplacements(Replaces, Rewrite);531if (Inplace) {532if (Rewrite.overwriteChangedFiles())533return true;534} else {535if (Cursor.getNumOccurrences() != 0) {536outs() << "{ \"Cursor\": "537<< FormatChanges.getShiftedCodePosition(CursorPosition)538<< ", \"IncompleteFormat\": "539<< (Status.FormatComplete ? "false" : "true");540if (!Status.FormatComplete)541outs() << ", \"Line\": " << Status.Line;542outs() << " }\n";543}544Rewrite.getEditBuffer(ID).write(outs());545}546}547return ErrorOnIncompleteFormat && !Status.FormatComplete;548}549550} // namespace format551} // namespace clang552553static void PrintVersion(raw_ostream &OS) {554OS << clang::getClangToolFullVersion("clang-format") << '\n';555}556557// Dump the configuration.558static int dumpConfig() {559std::unique_ptr<llvm::MemoryBuffer> Code;560// We can't read the code to detect the language if there's no file name.561if (!FileNames.empty()) {562// Read in the code in case the filename alone isn't enough to detect the563// language.564ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =565MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);566if (std::error_code EC = CodeOrErr.getError()) {567llvm::errs() << EC.message() << "\n";568return 1;569}570Code = std::move(CodeOrErr.get());571}572Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(573Style,574FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],575FallbackStyle, Code ? Code->getBuffer() : "");576if (!FormatStyle) {577llvm::errs() << toString(FormatStyle.takeError()) << "\n";578return 1;579}580std::string Config = clang::format::configurationAsText(*FormatStyle);581outs() << Config << "\n";582return 0;583}584585using String = SmallString<128>;586static String IgnoreDir; // Directory of .clang-format-ignore file.587static String PrevDir; // Directory of previous `FilePath`.588static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.589590// Check whether `FilePath` is ignored according to the nearest591// .clang-format-ignore file based on the rules below:592// - A blank line is skipped.593// - Leading and trailing spaces of a line are trimmed.594// - A line starting with a hash (`#`) is a comment.595// - A non-comment line is a single pattern.596// - The slash (`/`) is used as the directory separator.597// - A pattern is relative to the directory of the .clang-format-ignore file (or598// the root directory if the pattern starts with a slash).599// - A pattern is negated if it starts with a bang (`!`).600static bool isIgnored(StringRef FilePath) {601using namespace llvm::sys::fs;602if (!is_regular_file(FilePath))603return false;604605String Path;606String AbsPath{FilePath};607608using namespace llvm::sys::path;609make_absolute(AbsPath);610remove_dots(AbsPath, /*remove_dot_dot=*/true);611612if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {613PrevDir = Dir;614615for (;;) {616Path = Dir;617append(Path, ".clang-format-ignore");618if (is_regular_file(Path))619break;620Dir = parent_path(Dir);621if (Dir.empty())622return false;623}624625IgnoreDir = convert_to_slash(Dir);626627std::ifstream IgnoreFile{Path.c_str()};628if (!IgnoreFile.good())629return false;630631Patterns.clear();632633for (std::string Line; std::getline(IgnoreFile, Line);) {634if (const auto Pattern{StringRef{Line}.trim()};635// Skip empty and comment lines.636!Pattern.empty() && Pattern[0] != '#') {637Patterns.push_back(Pattern);638}639}640}641642if (IgnoreDir.empty())643return false;644645const auto Pathname{convert_to_slash(AbsPath)};646for (const auto &Pat : Patterns) {647const bool IsNegated = Pat[0] == '!';648StringRef Pattern{Pat};649if (IsNegated)650Pattern = Pattern.drop_front();651652if (Pattern.empty())653continue;654655Pattern = Pattern.ltrim();656657// `Pattern` is relative to `IgnoreDir` unless it starts with a slash.658// This doesn't support patterns containing drive names (e.g. `C:`).659if (Pattern[0] != '/') {660Path = IgnoreDir;661append(Path, Style::posix, Pattern);662remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);663Pattern = Path;664}665666if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)667return true;668}669670return false;671}672673int main(int argc, const char **argv) {674InitLLVM X(argc, argv);675676cl::HideUnrelatedOptions(ClangFormatCategory);677678cl::SetVersionPrinter(PrintVersion);679cl::ParseCommandLineOptions(680argc, argv,681"A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "682"code.\n\n"683"If no arguments are specified, it formats the code from standard input\n"684"and writes the result to the standard output.\n"685"If <file>s are given, it reformats the files. If -i is specified\n"686"together with <file>s, the files are edited in-place. Otherwise, the\n"687"result is written to the standard output.\n");688689if (Help) {690cl::PrintHelpMessage();691return 0;692}693694if (DumpConfig)695return dumpConfig();696697if (!Files.empty()) {698std::ifstream ExternalFileOfFiles{std::string(Files)};699std::string Line;700unsigned LineNo = 1;701while (std::getline(ExternalFileOfFiles, Line)) {702FileNames.push_back(Line);703LineNo++;704}705errs() << "Clang-formating " << LineNo << " files\n";706}707708if (FileNames.empty())709return clang::format::format("-", FailOnIncompleteFormat);710711if (FileNames.size() > 1 &&712(!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {713errs() << "error: -offset, -length and -lines can only be used for "714"single file.\n";715return 1;716}717718unsigned FileNo = 1;719bool Error = false;720for (const auto &FileName : FileNames) {721const bool Ignored = isIgnored(FileName);722if (ListIgnored) {723if (Ignored)724outs() << FileName << '\n';725continue;726}727if (Ignored)728continue;729if (Verbose) {730errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "731<< FileName << "\n";732}733Error |= clang::format::format(FileName, FailOnIncompleteFormat);734}735return Error ? 1 : 0;736}737738739