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