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 FormatStyle->SortIncludes = SortIncludes; 407 unsigned CursorPosition = Cursor; 408 Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, 409 AssumedFileName, &CursorPosition); 410 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); 411 if (!ChangedCode) { 412 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; 413 return true; 414 } 415 // Get new affected ranges after sorting `#includes`. 416 Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); 417 FormattingAttemptStatus Status; 418 Replacements FormatChanges = 419 reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); 420 Replaces = Replaces.merge(FormatChanges); 421 if (OutputXML || DryRun) { 422 if (DryRun) { 423 return emitReplacementWarnings(Replaces, AssumedFileName, Code); 424 } else { 425 outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); 426 } 427 } else { 428 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( 429 new llvm::vfs::InMemoryFileSystem); 430 FileManager Files(FileSystemOptions(), InMemoryFileSystem); 431 DiagnosticsEngine Diagnostics( 432 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 433 new DiagnosticOptions); 434 SourceManager Sources(Diagnostics, Files); 435 FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files, 436 InMemoryFileSystem.get()); 437 Rewriter Rewrite(Sources, LangOptions()); 438 tooling::applyAllReplacements(Replaces, Rewrite); 439 if (Inplace) { 440 if (Rewrite.overwriteChangedFiles()) 441 return true; 442 } else { 443 if (Cursor.getNumOccurrences() != 0) { 444 outs() << "{ \"Cursor\": " 445 << FormatChanges.getShiftedCodePosition(CursorPosition) 446 << ", \"IncompleteFormat\": " 447 << (Status.FormatComplete ? "false" : "true"); 448 if (!Status.FormatComplete) 449 outs() << ", \"Line\": " << Status.Line; 450 outs() << " }\n"; 451 } 452 Rewrite.getEditBuffer(ID).write(outs()); 453 } 454 } 455 return false; 456 } 457 458 } // namespace format 459 } // namespace clang 460 461 static void PrintVersion(raw_ostream &OS) { 462 OS << clang::getClangToolFullVersion("clang-format") << '\n'; 463 } 464 465 // Dump the configuration. 466 static int dumpConfig() { 467 StringRef FileName; 468 std::unique_ptr<llvm::MemoryBuffer> Code; 469 if (FileNames.empty()) { 470 // We can't read the code to detect the language if there's no 471 // file name, so leave Code empty here. 472 FileName = AssumeFileName; 473 } else { 474 // Read in the code in case the filename alone isn't enough to 475 // detect the language. 476 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 477 MemoryBuffer::getFileOrSTDIN(FileNames[0]); 478 if (std::error_code EC = CodeOrErr.getError()) { 479 llvm::errs() << EC.message() << "\n"; 480 return 1; 481 } 482 FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0]; 483 Code = std::move(CodeOrErr.get()); 484 } 485 llvm::Expected<clang::format::FormatStyle> FormatStyle = 486 clang::format::getStyle(Style, FileName, FallbackStyle, 487 Code ? Code->getBuffer() : ""); 488 if (!FormatStyle) { 489 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; 490 return 1; 491 } 492 std::string Config = clang::format::configurationAsText(*FormatStyle); 493 outs() << Config << "\n"; 494 return 0; 495 } 496 497 int main(int argc, const char **argv) { 498 llvm::InitLLVM X(argc, argv); 499 500 cl::HideUnrelatedOptions(ClangFormatCategory); 501 502 cl::SetVersionPrinter(PrintVersion); 503 cl::ParseCommandLineOptions( 504 argc, argv, 505 "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.\n\n" 506 "If no arguments are specified, it formats the code from standard input\n" 507 "and writes the result to the standard output.\n" 508 "If <file>s are given, it reformats the files. If -i is specified\n" 509 "together with <file>s, the files are edited in-place. Otherwise, the\n" 510 "result is written to the standard output.\n"); 511 512 if (Help) { 513 cl::PrintHelpMessage(); 514 return 0; 515 } 516 517 if (DumpConfig) { 518 return dumpConfig(); 519 } 520 521 bool Error = false; 522 if (FileNames.empty()) { 523 Error = clang::format::format("-"); 524 return Error ? 1 : 0; 525 } 526 if (FileNames.size() != 1 && 527 (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) { 528 errs() << "error: -offset, -length and -lines can only be used for " 529 "single file.\n"; 530 return 1; 531 } 532 for (const auto &FileName : FileNames) { 533 if (Verbose) 534 errs() << "Formatting " << FileName << "\n"; 535 Error |= clang::format::format(FileName); 536 } 537 return Error ? 1 : 0; 538 } 539