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