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