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