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