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 "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> 28 29 using namespace llvm; 30 using clang::tooling::Replacements; 31 32 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); 33 34 // Mark all our options with this category, everything else (except for -version 35 // and -help) will be hidden. 36 static cl::OptionCategory ClangFormatCategory("Clang-format options"); 37 38 static cl::list<unsigned> 39 Offsets("offset", 40 cl::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."), 44 cl::cat(ClangFormatCategory)); 45 static cl::list<unsigned> 46 Lengths("length", 47 cl::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."), 54 cl::cat(ClangFormatCategory)); 55 static cl::list<std::string> 56 LineRanges("lines", 57 cl::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."), 63 cl::cat(ClangFormatCategory)); 64 static cl::opt<std::string> 65 Style("style", cl::desc(clang::format::StyleOptionHelpDescription), 66 cl::init(clang::format::DefaultFormatStyle), 67 cl::cat(ClangFormatCategory)); 68 static cl::opt<std::string> 69 FallbackStyle("fallback-style", 70 cl::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.\n" 74 "Use -fallback-style=none to skip formatting."), 75 cl::init(clang::format::DefaultFallbackStyle), 76 cl::cat(ClangFormatCategory)); 77 78 static cl::opt<std::string> AssumeFileName( 79 "assume-filename", 80 cl::desc("Override filename used to determine the language.\n" 81 "When reading from stdin, clang-format assumes this\n" 82 "filename to determine the language."), 83 cl::init("<stdin>"), cl::cat(ClangFormatCategory)); 84 85 static cl::opt<bool> Inplace("i", 86 cl::desc("Inplace edit <file>s, if specified."), 87 cl::cat(ClangFormatCategory)); 88 89 static cl::opt<bool> OutputXML("output-replacements-xml", 90 cl::desc("Output replacements as XML."), 91 cl::cat(ClangFormatCategory)); 92 static cl::opt<bool> 93 DumpConfig("dump-config", 94 cl::desc("Dump configuration options to stdout and exit.\n" 95 "Can be used with -style option."), 96 cl::cat(ClangFormatCategory)); 97 static cl::opt<unsigned> 98 Cursor("cursor", 99 cl::desc("The position of the cursor when invoking\n" 100 "clang-format from an editor integration"), 101 cl::init(0), cl::cat(ClangFormatCategory)); 102 103 static cl::opt<bool> 104 SortIncludes("sort-includes", 105 cl::desc("If set, overrides the include sorting behavior\n" 106 "determined by the SortIncludes style flag"), 107 cl::cat(ClangFormatCategory)); 108 109 static cl::opt<std::string> QualifierAlignment( 110 "qualifier-alignment", 111 cl::desc("If set, overrides the qualifier alignment style\n" 112 "determined by the QualifierAlignment style flag"), 113 cl::init(""), cl::cat(ClangFormatCategory)); 114 115 static cl::opt<std::string> 116 Files("files", cl::desc("Provide a list of files to run clang-format"), 117 cl::init(""), cl::cat(ClangFormatCategory)); 118 119 static cl::opt<bool> 120 Verbose("verbose", cl::desc("If set, shows the list of processed files"), 121 cl::cat(ClangFormatCategory)); 122 123 // Use --dry-run to match other LLVM tools when you mean do it but don't 124 // actually do it 125 static cl::opt<bool> 126 DryRun("dry-run", 127 cl::desc("If set, do not actually make the formatting changes"), 128 cl::cat(ClangFormatCategory)); 129 130 // Use -n as a common command as an alias for --dry-run. (git and make use -n) 131 static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"), 132 cl::cat(ClangFormatCategory), cl::aliasopt(DryRun), 133 cl::NotHidden); 134 135 // Emulate being able to turn on/off the warning. 136 static cl::opt<bool> 137 WarnFormat("Wclang-format-violations", 138 cl::desc("Warnings about individual formatting changes needed. " 139 "Used only with --dry-run or -n"), 140 cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); 141 142 static cl::opt<bool> 143 NoWarnFormat("Wno-clang-format-violations", 144 cl::desc("Do not warn about individual formatting changes " 145 "needed. Used only with --dry-run or -n"), 146 cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); 147 148 static cl::opt<unsigned> ErrorLimit( 149 "ferror-limit", 150 cl::desc("Set the maximum number of clang-format errors to emit\n" 151 "before stopping (0 = no limit).\n" 152 "Used only with --dry-run or -n"), 153 cl::init(0), cl::cat(ClangFormatCategory)); 154 155 static cl::opt<bool> 156 WarningsAsErrors("Werror", 157 cl::desc("If set, changes formatting warnings to errors"), 158 cl::cat(ClangFormatCategory)); 159 160 namespace { 161 enum class WNoError { Unknown }; 162 } 163 164 static cl::bits<WNoError> WNoErrorList( 165 "Wno-error", 166 cl::desc("If set don't error out on the specified warning type."), 167 cl::values( 168 clEnumValN(WNoError::Unknown, "unknown", 169 "If set, unknown format options are only warned about.\n" 170 "This can be used to enable formatting, even if the\n" 171 "configuration contains unknown (newer) options.\n" 172 "Use with caution, as this might lead to dramatically\n" 173 "differing format depending on an option being\n" 174 "supported or not.")), 175 cl::cat(ClangFormatCategory)); 176 177 static cl::opt<bool> 178 ShowColors("fcolor-diagnostics", 179 cl::desc("If set, and on a color-capable terminal controls " 180 "whether or not to print diagnostics in color"), 181 cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); 182 183 static cl::opt<bool> 184 NoShowColors("fno-color-diagnostics", 185 cl::desc("If set, and on a color-capable terminal controls " 186 "whether or not to print diagnostics in color"), 187 cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); 188 189 static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"), 190 cl::cat(ClangFormatCategory)); 191 192 namespace clang { 193 namespace format { 194 195 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source, 196 SourceManager &Sources, FileManager &Files, 197 llvm::vfs::InMemoryFileSystem *MemFS) { 198 MemFS->addFileNoOwn(FileName, 0, Source); 199 auto File = Files.getOptionalFileRef(FileName); 200 assert(File && "File not added to MemFS?"); 201 return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User); 202 } 203 204 // Parses <start line>:<end line> input to a pair of line numbers. 205 // Returns true on error. 206 static bool parseLineRange(StringRef Input, unsigned &FromLine, 207 unsigned &ToLine) { 208 std::pair<StringRef, StringRef> LineRange = Input.split(':'); 209 return LineRange.first.getAsInteger(0, FromLine) || 210 LineRange.second.getAsInteger(0, ToLine); 211 } 212 213 static bool fillRanges(MemoryBuffer *Code, 214 std::vector<tooling::Range> &Ranges) { 215 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 216 new llvm::vfs::InMemoryFileSystem); 217 FileManager Files(FileSystemOptions(), InMemoryFileSystem); 218 DiagnosticsEngine Diagnostics( 219 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 220 new DiagnosticOptions); 221 SourceManager Sources(Diagnostics, Files); 222 FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files, 223 InMemoryFileSystem.get()); 224 if (!LineRanges.empty()) { 225 if (!Offsets.empty() || !Lengths.empty()) { 226 errs() << "error: cannot use -lines with -offset/-length\n"; 227 return true; 228 } 229 230 for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) { 231 unsigned FromLine, ToLine; 232 if (parseLineRange(LineRanges[i], FromLine, ToLine)) { 233 errs() << "error: invalid <start line>:<end line> pair\n"; 234 return true; 235 } 236 if (FromLine > ToLine) { 237 errs() << "error: start line should be less than end line\n"; 238 return true; 239 } 240 SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); 241 SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); 242 if (Start.isInvalid() || End.isInvalid()) 243 return true; 244 unsigned Offset = Sources.getFileOffset(Start); 245 unsigned Length = Sources.getFileOffset(End) - Offset; 246 Ranges.push_back(tooling::Range(Offset, Length)); 247 } 248 return false; 249 } 250 251 if (Offsets.empty()) 252 Offsets.push_back(0); 253 if (Offsets.size() != Lengths.size() && 254 !(Offsets.size() == 1 && Lengths.empty())) { 255 errs() << "error: number of -offset and -length arguments must match.\n"; 256 return true; 257 } 258 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { 259 if (Offsets[i] >= Code->getBufferSize()) { 260 errs() << "error: offset " << Offsets[i] << " is outside the file\n"; 261 return true; 262 } 263 SourceLocation Start = 264 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); 265 SourceLocation End; 266 if (i < Lengths.size()) { 267 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { 268 errs() << "error: invalid length " << Lengths[i] 269 << ", offset + length (" << Offsets[i] + Lengths[i] 270 << ") is outside the file.\n"; 271 return true; 272 } 273 End = Start.getLocWithOffset(Lengths[i]); 274 } else { 275 End = Sources.getLocForEndOfFile(ID); 276 } 277 unsigned Offset = Sources.getFileOffset(Start); 278 unsigned Length = Sources.getFileOffset(End) - Offset; 279 Ranges.push_back(tooling::Range(Offset, Length)); 280 } 281 return false; 282 } 283 284 static void outputReplacementXML(StringRef Text) { 285 // FIXME: When we sort includes, we need to make sure the stream is correct 286 // utf-8. 287 size_t From = 0; 288 size_t Index; 289 while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { 290 outs() << Text.substr(From, Index - From); 291 switch (Text[Index]) { 292 case '\n': 293 outs() << " "; 294 break; 295 case '\r': 296 outs() << " "; 297 break; 298 case '<': 299 outs() << "<"; 300 break; 301 case '&': 302 outs() << "&"; 303 break; 304 default: 305 llvm_unreachable("Unexpected character encountered!"); 306 } 307 From = Index + 1; 308 } 309 outs() << Text.substr(From); 310 } 311 312 static void outputReplacementsXML(const Replacements &Replaces) { 313 for (const auto &R : Replaces) { 314 outs() << "<replacement " 315 << "offset='" << R.getOffset() << "' " 316 << "length='" << R.getLength() << "'>"; 317 outputReplacementXML(R.getReplacementText()); 318 outs() << "</replacement>\n"; 319 } 320 } 321 322 static bool 323 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName, 324 const std::unique_ptr<llvm::MemoryBuffer> &Code) { 325 if (Replaces.empty()) 326 return false; 327 328 unsigned Errors = 0; 329 if (WarnFormat && !NoWarnFormat) { 330 llvm::SourceMgr Mgr; 331 const char *StartBuf = Code->getBufferStart(); 332 333 Mgr.AddNewSourceBuffer( 334 MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc()); 335 for (const auto &R : Replaces) { 336 SMDiagnostic Diag = Mgr.GetMessage( 337 SMLoc::getFromPointer(StartBuf + R.getOffset()), 338 WarningsAsErrors ? SourceMgr::DiagKind::DK_Error 339 : SourceMgr::DiagKind::DK_Warning, 340 "code should be clang-formatted [-Wclang-format-violations]"); 341 342 Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors)); 343 if (ErrorLimit && ++Errors >= ErrorLimit) 344 break; 345 } 346 } 347 return WarningsAsErrors; 348 } 349 350 static void outputXML(const Replacements &Replaces, 351 const Replacements &FormatChanges, 352 const FormattingAttemptStatus &Status, 353 const cl::opt<unsigned> &Cursor, 354 unsigned CursorPosition) { 355 outs() << "<?xml version='1.0'?>\n<replacements " 356 "xml:space='preserve' incomplete_format='" 357 << (Status.FormatComplete ? "false" : "true") << "'"; 358 if (!Status.FormatComplete) 359 outs() << " line='" << Status.Line << "'"; 360 outs() << ">\n"; 361 if (Cursor.getNumOccurrences() != 0) { 362 outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition) 363 << "</cursor>\n"; 364 } 365 366 outputReplacementsXML(Replaces); 367 outs() << "</replacements>\n"; 368 } 369 370 class ClangFormatDiagConsumer : public DiagnosticConsumer { 371 virtual void anchor() {} 372 373 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 374 const Diagnostic &Info) override { 375 376 SmallVector<char, 16> vec; 377 Info.FormatDiagnostic(vec); 378 errs() << "clang-format error:" << vec << "\n"; 379 } 380 }; 381 382 // Returns true on error. 383 static bool format(StringRef FileName) { 384 if (!OutputXML && Inplace && FileName == "-") { 385 errs() << "error: cannot use -i when reading from stdin.\n"; 386 return false; 387 } 388 // On Windows, overwriting a file with an open file mapping doesn't work, 389 // so read the whole file into memory when formatting in-place. 390 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 391 !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) 392 : MemoryBuffer::getFileOrSTDIN(FileName); 393 if (std::error_code EC = CodeOrErr.getError()) { 394 errs() << EC.message() << "\n"; 395 return true; 396 } 397 std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get()); 398 if (Code->getBufferSize() == 0) 399 return false; // Empty files are formatted correctly. 400 401 StringRef BufStr = Code->getBuffer(); 402 403 const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr); 404 405 if (InvalidBOM) { 406 errs() << "error: encoding with unsupported byte order mark \"" 407 << InvalidBOM << "\" detected"; 408 if (FileName != "-") 409 errs() << " in file '" << FileName << "'"; 410 errs() << ".\n"; 411 return true; 412 } 413 414 std::vector<tooling::Range> Ranges; 415 if (fillRanges(Code.get(), Ranges)) 416 return true; 417 StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName; 418 if (AssumedFileName.empty()) { 419 llvm::errs() << "error: empty filenames are not allowed\n"; 420 return true; 421 } 422 423 llvm::Expected<FormatStyle> FormatStyle = 424 getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), 425 nullptr, WNoErrorList.isSet(WNoError::Unknown)); 426 if (!FormatStyle) { 427 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; 428 return true; 429 } 430 431 StringRef QualifierAlignmentOrder = QualifierAlignment; 432 433 FormatStyle->QualifierAlignment = 434 StringSwitch<FormatStyle::QualifierAlignmentStyle>( 435 QualifierAlignmentOrder.lower()) 436 .Case("right", FormatStyle::QAS_Right) 437 .Case("left", FormatStyle::QAS_Left) 438 .Default(FormatStyle->QualifierAlignment); 439 440 if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) { 441 FormatStyle->QualifierOrder = {"const", "volatile", "type"}; 442 } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) { 443 FormatStyle->QualifierOrder = {"type", "const", "volatile"}; 444 } else if (QualifierAlignmentOrder.contains("type")) { 445 FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom; 446 SmallVector<StringRef> Qualifiers; 447 QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1, 448 /*KeepEmpty=*/false); 449 FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()}; 450 } 451 452 if (SortIncludes.getNumOccurrences() != 0) { 453 if (SortIncludes) 454 FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive; 455 else 456 FormatStyle->SortIncludes = FormatStyle::SI_Never; 457 } 458 unsigned CursorPosition = Cursor; 459 Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, 460 AssumedFileName, &CursorPosition); 461 462 // To format JSON insert a variable to trick the code into thinking its 463 // JavaScript. 464 if (FormatStyle->isJson() && !FormatStyle->DisableFormat) { 465 auto Err = Replaces.add(tooling::Replacement( 466 tooling::Replacement(AssumedFileName, 0, 0, "x = "))); 467 if (Err) 468 llvm::errs() << "Bad Json variable insertion\n"; 469 } 470 471 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); 472 if (!ChangedCode) { 473 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; 474 return true; 475 } 476 // Get new affected ranges after sorting `#includes`. 477 Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); 478 FormattingAttemptStatus Status; 479 Replacements FormatChanges = 480 reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); 481 Replaces = Replaces.merge(FormatChanges); 482 if (OutputXML || DryRun) { 483 if (DryRun) 484 return emitReplacementWarnings(Replaces, AssumedFileName, Code); 485 else 486 outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); 487 } else { 488 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 489 new llvm::vfs::InMemoryFileSystem); 490 FileManager Files(FileSystemOptions(), InMemoryFileSystem); 491 492 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions()); 493 ClangFormatDiagConsumer IgnoreDiagnostics; 494 DiagnosticsEngine Diagnostics( 495 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, 496 &IgnoreDiagnostics, false); 497 SourceManager Sources(Diagnostics, Files); 498 FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files, 499 InMemoryFileSystem.get()); 500 Rewriter Rewrite(Sources, LangOptions()); 501 tooling::applyAllReplacements(Replaces, Rewrite); 502 if (Inplace) { 503 if (Rewrite.overwriteChangedFiles()) 504 return true; 505 } else { 506 if (Cursor.getNumOccurrences() != 0) { 507 outs() << "{ \"Cursor\": " 508 << FormatChanges.getShiftedCodePosition(CursorPosition) 509 << ", \"IncompleteFormat\": " 510 << (Status.FormatComplete ? "false" : "true"); 511 if (!Status.FormatComplete) 512 outs() << ", \"Line\": " << Status.Line; 513 outs() << " }\n"; 514 } 515 Rewrite.getEditBuffer(ID).write(outs()); 516 } 517 } 518 return false; 519 } 520 521 } // namespace format 522 } // namespace clang 523 524 static void PrintVersion(raw_ostream &OS) { 525 OS << clang::getClangToolFullVersion("clang-format") << '\n'; 526 } 527 528 // Dump the configuration. 529 static int dumpConfig() { 530 StringRef FileName; 531 std::unique_ptr<llvm::MemoryBuffer> Code; 532 if (FileNames.empty()) { 533 // We can't read the code to detect the language if there's no 534 // file name, so leave Code empty here. 535 FileName = AssumeFileName; 536 } else { 537 // Read in the code in case the filename alone isn't enough to 538 // detect the language. 539 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 540 MemoryBuffer::getFileOrSTDIN(FileNames[0]); 541 if (std::error_code EC = CodeOrErr.getError()) { 542 llvm::errs() << EC.message() << "\n"; 543 return 1; 544 } 545 FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0]; 546 Code = std::move(CodeOrErr.get()); 547 } 548 llvm::Expected<clang::format::FormatStyle> FormatStyle = 549 clang::format::getStyle(Style, FileName, FallbackStyle, 550 Code ? Code->getBuffer() : ""); 551 if (!FormatStyle) { 552 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; 553 return 1; 554 } 555 std::string Config = clang::format::configurationAsText(*FormatStyle); 556 outs() << Config << "\n"; 557 return 0; 558 } 559 560 int main(int argc, const char **argv) { 561 llvm::InitLLVM X(argc, argv); 562 563 cl::HideUnrelatedOptions(ClangFormatCategory); 564 565 cl::SetVersionPrinter(PrintVersion); 566 cl::ParseCommandLineOptions( 567 argc, argv, 568 "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# " 569 "code.\n\n" 570 "If no arguments are specified, it formats the code from standard input\n" 571 "and writes the result to the standard output.\n" 572 "If <file>s are given, it reformats the files. If -i is specified\n" 573 "together with <file>s, the files are edited in-place. Otherwise, the\n" 574 "result is written to the standard output.\n"); 575 576 if (Help) { 577 cl::PrintHelpMessage(); 578 return 0; 579 } 580 581 if (DumpConfig) 582 return dumpConfig(); 583 584 if (!Files.empty()) { 585 std::ifstream ExternalFileOfFiles{std::string(Files)}; 586 std::string Line; 587 unsigned LineNo = 1; 588 while (std::getline(ExternalFileOfFiles, Line)) { 589 FileNames.push_back(Line); 590 LineNo++; 591 } 592 errs() << "Clang-formating " << LineNo << " files\n"; 593 } 594 595 bool Error = false; 596 if (FileNames.empty()) { 597 Error = clang::format::format("-"); 598 return Error ? 1 : 0; 599 } 600 if (FileNames.size() != 1 && 601 (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) { 602 errs() << "error: -offset, -length and -lines can only be used for " 603 "single file.\n"; 604 return 1; 605 } 606 607 unsigned FileNo = 1; 608 for (const auto &FileName : FileNames) { 609 if (Verbose) { 610 errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] " 611 << FileName << "\n"; 612 } 613 Error |= clang::format::format(FileName); 614 } 615 return Error ? 1 : 0; 616 } 617