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 > ToLine) { 250 errs() << "error: start line should be less than end line\n"; 251 return true; 252 } 253 SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); 254 SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); 255 if (Start.isInvalid() || End.isInvalid()) 256 return true; 257 unsigned Offset = Sources.getFileOffset(Start); 258 unsigned Length = Sources.getFileOffset(End) - Offset; 259 Ranges.push_back(tooling::Range(Offset, Length)); 260 } 261 return false; 262 } 263 264 if (Offsets.empty()) 265 Offsets.push_back(0); 266 if (Offsets.size() != Lengths.size() && 267 !(Offsets.size() == 1 && Lengths.empty())) { 268 errs() << "error: number of -offset and -length arguments must match.\n"; 269 return true; 270 } 271 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { 272 if (Offsets[i] >= Code->getBufferSize()) { 273 errs() << "error: offset " << Offsets[i] << " is outside the file\n"; 274 return true; 275 } 276 SourceLocation Start = 277 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); 278 SourceLocation End; 279 if (i < Lengths.size()) { 280 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { 281 errs() << "error: invalid length " << Lengths[i] 282 << ", offset + length (" << Offsets[i] + Lengths[i] 283 << ") is outside the file.\n"; 284 return true; 285 } 286 End = Start.getLocWithOffset(Lengths[i]); 287 } else { 288 End = Sources.getLocForEndOfFile(ID); 289 } 290 unsigned Offset = Sources.getFileOffset(Start); 291 unsigned Length = Sources.getFileOffset(End) - Offset; 292 Ranges.push_back(tooling::Range(Offset, Length)); 293 } 294 return false; 295 } 296 297 static void outputReplacementXML(StringRef Text) { 298 // FIXME: When we sort includes, we need to make sure the stream is correct 299 // utf-8. 300 size_t From = 0; 301 size_t Index; 302 while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { 303 outs() << Text.substr(From, Index - From); 304 switch (Text[Index]) { 305 case '\n': 306 outs() << " "; 307 break; 308 case '\r': 309 outs() << " "; 310 break; 311 case '<': 312 outs() << "<"; 313 break; 314 case '&': 315 outs() << "&"; 316 break; 317 default: 318 llvm_unreachable("Unexpected character encountered!"); 319 } 320 From = Index + 1; 321 } 322 outs() << Text.substr(From); 323 } 324 325 static void outputReplacementsXML(const Replacements &Replaces) { 326 for (const auto &R : Replaces) { 327 outs() << "<replacement " 328 << "offset='" << R.getOffset() << "' " 329 << "length='" << R.getLength() << "'>"; 330 outputReplacementXML(R.getReplacementText()); 331 outs() << "</replacement>\n"; 332 } 333 } 334 335 static bool 336 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName, 337 const std::unique_ptr<llvm::MemoryBuffer> &Code) { 338 if (Replaces.empty()) 339 return false; 340 341 unsigned Errors = 0; 342 if (WarnFormat && !NoWarnFormat) { 343 llvm::SourceMgr Mgr; 344 const char *StartBuf = Code->getBufferStart(); 345 346 Mgr.AddNewSourceBuffer( 347 MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc()); 348 for (const auto &R : Replaces) { 349 SMDiagnostic Diag = Mgr.GetMessage( 350 SMLoc::getFromPointer(StartBuf + R.getOffset()), 351 WarningsAsErrors ? SourceMgr::DiagKind::DK_Error 352 : SourceMgr::DiagKind::DK_Warning, 353 "code should be clang-formatted [-Wclang-format-violations]"); 354 355 Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors)); 356 if (ErrorLimit && ++Errors >= ErrorLimit) 357 break; 358 } 359 } 360 return WarningsAsErrors; 361 } 362 363 static void outputXML(const Replacements &Replaces, 364 const Replacements &FormatChanges, 365 const FormattingAttemptStatus &Status, 366 const cl::opt<unsigned> &Cursor, 367 unsigned CursorPosition) { 368 outs() << "<?xml version='1.0'?>\n<replacements " 369 "xml:space='preserve' incomplete_format='" 370 << (Status.FormatComplete ? "false" : "true") << "'"; 371 if (!Status.FormatComplete) 372 outs() << " line='" << Status.Line << "'"; 373 outs() << ">\n"; 374 if (Cursor.getNumOccurrences() != 0) { 375 outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition) 376 << "</cursor>\n"; 377 } 378 379 outputReplacementsXML(Replaces); 380 outs() << "</replacements>\n"; 381 } 382 383 class ClangFormatDiagConsumer : public DiagnosticConsumer { 384 virtual void anchor() {} 385 386 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 387 const Diagnostic &Info) override { 388 389 SmallVector<char, 16> vec; 390 Info.FormatDiagnostic(vec); 391 errs() << "clang-format error:" << vec << "\n"; 392 } 393 }; 394 395 // Returns true on error. 396 static bool format(StringRef FileName) { 397 if (!OutputXML && Inplace && FileName == "-") { 398 errs() << "error: cannot use -i when reading from stdin.\n"; 399 return false; 400 } 401 // On Windows, overwriting a file with an open file mapping doesn't work, 402 // so read the whole file into memory when formatting in-place. 403 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 404 !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) 405 : MemoryBuffer::getFileOrSTDIN(FileName); 406 if (std::error_code EC = CodeOrErr.getError()) { 407 errs() << EC.message() << "\n"; 408 return true; 409 } 410 std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get()); 411 if (Code->getBufferSize() == 0) 412 return false; // Empty files are formatted correctly. 413 414 StringRef BufStr = Code->getBuffer(); 415 416 const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr); 417 418 if (InvalidBOM) { 419 errs() << "error: encoding with unsupported byte order mark \"" 420 << InvalidBOM << "\" detected"; 421 if (FileName != "-") 422 errs() << " in file '" << FileName << "'"; 423 errs() << ".\n"; 424 return true; 425 } 426 427 std::vector<tooling::Range> Ranges; 428 if (fillRanges(Code.get(), Ranges)) 429 return true; 430 StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName; 431 if (AssumedFileName.empty()) { 432 llvm::errs() << "error: empty filenames are not allowed\n"; 433 return true; 434 } 435 436 llvm::Expected<FormatStyle> FormatStyle = 437 getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), 438 nullptr, WNoErrorList.isSet(WNoError::Unknown)); 439 if (!FormatStyle) { 440 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; 441 return true; 442 } 443 444 StringRef QualifierAlignmentOrder = QualifierAlignment; 445 446 FormatStyle->QualifierAlignment = 447 StringSwitch<FormatStyle::QualifierAlignmentStyle>( 448 QualifierAlignmentOrder.lower()) 449 .Case("right", FormatStyle::QAS_Right) 450 .Case("left", FormatStyle::QAS_Left) 451 .Default(FormatStyle->QualifierAlignment); 452 453 if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) { 454 FormatStyle->QualifierOrder = {"const", "volatile", "type"}; 455 } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) { 456 FormatStyle->QualifierOrder = {"type", "const", "volatile"}; 457 } else if (QualifierAlignmentOrder.contains("type")) { 458 FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom; 459 SmallVector<StringRef> Qualifiers; 460 QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1, 461 /*KeepEmpty=*/false); 462 FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()}; 463 } 464 465 if (SortIncludes.getNumOccurrences() != 0) { 466 if (SortIncludes) 467 FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive; 468 else 469 FormatStyle->SortIncludes = FormatStyle::SI_Never; 470 } 471 unsigned CursorPosition = Cursor; 472 Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, 473 AssumedFileName, &CursorPosition); 474 475 // To format JSON insert a variable to trick the code into thinking its 476 // JavaScript. 477 if (FormatStyle->isJson() && !FormatStyle->DisableFormat) { 478 auto Err = Replaces.add(tooling::Replacement( 479 tooling::Replacement(AssumedFileName, 0, 0, "x = "))); 480 if (Err) 481 llvm::errs() << "Bad Json variable insertion\n"; 482 } 483 484 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); 485 if (!ChangedCode) { 486 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; 487 return true; 488 } 489 // Get new affected ranges after sorting `#includes`. 490 Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); 491 FormattingAttemptStatus Status; 492 Replacements FormatChanges = 493 reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); 494 Replaces = Replaces.merge(FormatChanges); 495 if (OutputXML || DryRun) { 496 if (DryRun) 497 return emitReplacementWarnings(Replaces, AssumedFileName, Code); 498 else 499 outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); 500 } else { 501 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 502 new llvm::vfs::InMemoryFileSystem); 503 FileManager Files(FileSystemOptions(), InMemoryFileSystem); 504 505 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions()); 506 ClangFormatDiagConsumer IgnoreDiagnostics; 507 DiagnosticsEngine Diagnostics( 508 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, 509 &IgnoreDiagnostics, false); 510 SourceManager Sources(Diagnostics, Files); 511 FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files, 512 InMemoryFileSystem.get()); 513 Rewriter Rewrite(Sources, LangOptions()); 514 tooling::applyAllReplacements(Replaces, Rewrite); 515 if (Inplace) { 516 if (Rewrite.overwriteChangedFiles()) 517 return true; 518 } else { 519 if (Cursor.getNumOccurrences() != 0) { 520 outs() << "{ \"Cursor\": " 521 << FormatChanges.getShiftedCodePosition(CursorPosition) 522 << ", \"IncompleteFormat\": " 523 << (Status.FormatComplete ? "false" : "true"); 524 if (!Status.FormatComplete) 525 outs() << ", \"Line\": " << Status.Line; 526 outs() << " }\n"; 527 } 528 Rewrite.getEditBuffer(ID).write(outs()); 529 } 530 } 531 return false; 532 } 533 534 } // namespace format 535 } // namespace clang 536 537 static void PrintVersion(raw_ostream &OS) { 538 OS << clang::getClangToolFullVersion("clang-format") << '\n'; 539 } 540 541 // Dump the configuration. 542 static int dumpConfig() { 543 StringRef FileName; 544 std::unique_ptr<llvm::MemoryBuffer> Code; 545 if (FileNames.empty()) { 546 // We can't read the code to detect the language if there's no 547 // file name, so leave Code empty here. 548 FileName = AssumeFileName; 549 } else { 550 // Read in the code in case the filename alone isn't enough to 551 // detect the language. 552 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 553 MemoryBuffer::getFileOrSTDIN(FileNames[0]); 554 if (std::error_code EC = CodeOrErr.getError()) { 555 llvm::errs() << EC.message() << "\n"; 556 return 1; 557 } 558 FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0]; 559 Code = std::move(CodeOrErr.get()); 560 } 561 llvm::Expected<clang::format::FormatStyle> FormatStyle = 562 clang::format::getStyle(Style, FileName, FallbackStyle, 563 Code ? Code->getBuffer() : ""); 564 if (!FormatStyle) { 565 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; 566 return 1; 567 } 568 std::string Config = clang::format::configurationAsText(*FormatStyle); 569 outs() << Config << "\n"; 570 return 0; 571 } 572 573 int main(int argc, const char **argv) { 574 llvm::InitLLVM X(argc, argv); 575 576 cl::HideUnrelatedOptions(ClangFormatCategory); 577 578 cl::SetVersionPrinter(PrintVersion); 579 cl::ParseCommandLineOptions( 580 argc, argv, 581 "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# " 582 "code.\n\n" 583 "If no arguments are specified, it formats the code from standard input\n" 584 "and writes the result to the standard output.\n" 585 "If <file>s are given, it reformats the files. If -i is specified\n" 586 "together with <file>s, the files are edited in-place. Otherwise, the\n" 587 "result is written to the standard output.\n"); 588 589 if (Help) { 590 cl::PrintHelpMessage(); 591 return 0; 592 } 593 594 if (DumpConfig) 595 return dumpConfig(); 596 597 if (!Files.empty()) { 598 std::ifstream ExternalFileOfFiles{std::string(Files)}; 599 std::string Line; 600 unsigned LineNo = 1; 601 while (std::getline(ExternalFileOfFiles, Line)) { 602 FileNames.push_back(Line); 603 LineNo++; 604 } 605 errs() << "Clang-formating " << LineNo << " files\n"; 606 } 607 608 bool Error = false; 609 if (FileNames.empty()) { 610 Error = clang::format::format("-"); 611 return Error ? 1 : 0; 612 } 613 if (FileNames.size() != 1 && 614 (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) { 615 errs() << "error: -offset, -length and -lines can only be used for " 616 "single file.\n"; 617 return 1; 618 } 619 620 unsigned FileNo = 1; 621 for (const auto &FileName : FileNames) { 622 if (Verbose) { 623 errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] " 624 << FileName << "\n"; 625 } 626 Error |= clang::format::format(FileName); 627 } 628 return Error ? 1 : 0; 629 } 630