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