ClangFormat.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. //===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. ///
  10. /// \file
  11. /// \brief This file implements a clang-format tool that automatically formats
  12. /// (fragments of) C++ code.
  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/StringMap.h"
  23. #include "llvm/Support/CommandLine.h"
  24. #include "llvm/Support/Debug.h"
  25. #include "llvm/Support/FileSystem.h"
  26. #include "llvm/Support/Signals.h"
  27. using namespace llvm;
  28. using clang::tooling::Replacements;
  29. static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
  30. // Mark all our options with this category, everything else (except for -version
  31. // and -help) will be hidden.
  32. static cl::OptionCategory ClangFormatCategory("Clang-format options");
  33. static cl::list<unsigned>
  34. Offsets("offset",
  35. cl::desc("Format a range starting at this byte offset.\n"
  36. "Multiple ranges can be formatted by specifying\n"
  37. "several -offset and -length pairs.\n"
  38. "Can only be used with one input file."),
  39. cl::cat(ClangFormatCategory));
  40. static cl::list<unsigned>
  41. Lengths("length",
  42. cl::desc("Format a range of this length (in bytes).\n"
  43. "Multiple ranges can be formatted by specifying\n"
  44. "several -offset and -length pairs.\n"
  45. "When only a single -offset is specified without\n"
  46. "-length, clang-format will format up to the end\n"
  47. "of the file.\n"
  48. "Can only be used with one input file."),
  49. cl::cat(ClangFormatCategory));
  50. static cl::list<std::string>
  51. LineRanges("lines", cl::desc("<start line>:<end line> - format a range of\n"
  52. "lines (both 1-based).\n"
  53. "Multiple ranges can be formatted by specifying\n"
  54. "several -lines arguments.\n"
  55. "Can't be used with -offset and -length.\n"
  56. "Can only be used with one input file."),
  57. cl::cat(ClangFormatCategory));
  58. static cl::opt<std::string>
  59. Style("style",
  60. cl::desc(clang::format::StyleOptionHelpDescription),
  61. cl::init("file"), cl::cat(ClangFormatCategory));
  62. static cl::opt<std::string>
  63. FallbackStyle("fallback-style",
  64. cl::desc("The name of the predefined style used as a\n"
  65. "fallback in case clang-format is invoked with\n"
  66. "-style=file, but can not find the .clang-format\n"
  67. "file to use.\n"
  68. "Use -fallback-style=none to skip formatting."),
  69. cl::init("LLVM"), cl::cat(ClangFormatCategory));
  70. static cl::opt<std::string>
  71. AssumeFileName("assume-filename",
  72. cl::desc("When reading from stdin, clang-format assumes this\n"
  73. "filename to look for a style config file (with\n"
  74. "-style=file) and to determine the language."),
  75. cl::init("<stdin>"), cl::cat(ClangFormatCategory));
  76. static cl::opt<bool> Inplace("i",
  77. cl::desc("Inplace edit <file>s, if specified."),
  78. cl::cat(ClangFormatCategory));
  79. static cl::opt<bool> OutputXML("output-replacements-xml",
  80. cl::desc("Output replacements as XML."),
  81. cl::cat(ClangFormatCategory));
  82. static cl::opt<bool>
  83. DumpConfig("dump-config",
  84. cl::desc("Dump configuration options to stdout and exit.\n"
  85. "Can be used with -style option."),
  86. cl::cat(ClangFormatCategory));
  87. static cl::opt<unsigned>
  88. Cursor("cursor",
  89. cl::desc("The position of the cursor when invoking\n"
  90. "clang-format from an editor integration"),
  91. cl::init(0), cl::cat(ClangFormatCategory));
  92. static cl::opt<bool> SortIncludes(
  93. "sort-includes",
  94. cl::desc("If set, overrides the include sorting behavior determined by the "
  95. "SortIncludes style flag"),
  96. cl::cat(ClangFormatCategory));
  97. static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
  98. cl::cat(ClangFormatCategory));
  99. namespace clang {
  100. namespace format {
  101. static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source,
  102. SourceManager &Sources, FileManager &Files,
  103. vfs::InMemoryFileSystem *MemFS) {
  104. MemFS->addFileNoOwn(FileName, 0, Source);
  105. return Sources.createFileID(Files.getFile(FileName), SourceLocation(),
  106. SrcMgr::C_User);
  107. }
  108. // Parses <start line>:<end line> input to a pair of line numbers.
  109. // Returns true on error.
  110. static bool parseLineRange(StringRef Input, unsigned &FromLine,
  111. unsigned &ToLine) {
  112. std::pair<StringRef, StringRef> LineRange = Input.split(':');
  113. return LineRange.first.getAsInteger(0, FromLine) ||
  114. LineRange.second.getAsInteger(0, ToLine);
  115. }
  116. static bool fillRanges(MemoryBuffer *Code,
  117. std::vector<tooling::Range> &Ranges) {
  118. IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
  119. new vfs::InMemoryFileSystem);
  120. FileManager Files(FileSystemOptions(), InMemoryFileSystem);
  121. DiagnosticsEngine Diagnostics(
  122. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
  123. new DiagnosticOptions);
  124. SourceManager Sources(Diagnostics, Files);
  125. FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files,
  126. InMemoryFileSystem.get());
  127. if (!LineRanges.empty()) {
  128. if (!Offsets.empty() || !Lengths.empty()) {
  129. llvm::errs() << "error: cannot use -lines with -offset/-length\n";
  130. return true;
  131. }
  132. for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
  133. unsigned FromLine, ToLine;
  134. if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
  135. llvm::errs() << "error: invalid <start line>:<end line> pair\n";
  136. return true;
  137. }
  138. if (FromLine > ToLine) {
  139. llvm::errs() << "error: start line should be less than end line\n";
  140. return true;
  141. }
  142. SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
  143. SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
  144. if (Start.isInvalid() || End.isInvalid())
  145. return true;
  146. unsigned Offset = Sources.getFileOffset(Start);
  147. unsigned Length = Sources.getFileOffset(End) - Offset;
  148. Ranges.push_back(tooling::Range(Offset, Length));
  149. }
  150. return false;
  151. }
  152. if (Offsets.empty())
  153. Offsets.push_back(0);
  154. if (Offsets.size() != Lengths.size() &&
  155. !(Offsets.size() == 1 && Lengths.empty())) {
  156. llvm::errs()
  157. << "error: number of -offset and -length arguments must match.\n";
  158. return true;
  159. }
  160. for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
  161. if (Offsets[i] >= Code->getBufferSize()) {
  162. llvm::errs() << "error: offset " << Offsets[i]
  163. << " is outside the file\n";
  164. return true;
  165. }
  166. SourceLocation Start =
  167. Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
  168. SourceLocation End;
  169. if (i < Lengths.size()) {
  170. if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
  171. llvm::errs() << "error: invalid length " << Lengths[i]
  172. << ", offset + length (" << Offsets[i] + Lengths[i]
  173. << ") is outside the file.\n";
  174. return true;
  175. }
  176. End = Start.getLocWithOffset(Lengths[i]);
  177. } else {
  178. End = Sources.getLocForEndOfFile(ID);
  179. }
  180. unsigned Offset = Sources.getFileOffset(Start);
  181. unsigned Length = Sources.getFileOffset(End) - Offset;
  182. Ranges.push_back(tooling::Range(Offset, Length));
  183. }
  184. return false;
  185. }
  186. static void outputReplacementXML(StringRef Text) {
  187. // FIXME: When we sort includes, we need to make sure the stream is correct
  188. // utf-8.
  189. size_t From = 0;
  190. size_t Index;
  191. while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
  192. llvm::outs() << Text.substr(From, Index - From);
  193. switch (Text[Index]) {
  194. case '\n':
  195. llvm::outs() << "&#10;";
  196. break;
  197. case '\r':
  198. llvm::outs() << "&#13;";
  199. break;
  200. case '<':
  201. llvm::outs() << "&lt;";
  202. break;
  203. case '&':
  204. llvm::outs() << "&amp;";
  205. break;
  206. default:
  207. llvm_unreachable("Unexpected character encountered!");
  208. }
  209. From = Index + 1;
  210. }
  211. llvm::outs() << Text.substr(From);
  212. }
  213. static void outputReplacementsXML(const Replacements &Replaces) {
  214. for (const auto &R : Replaces) {
  215. outs() << "<replacement "
  216. << "offset='" << R.getOffset() << "' "
  217. << "length='" << R.getLength() << "'>";
  218. outputReplacementXML(R.getReplacementText());
  219. outs() << "</replacement>\n";
  220. }
  221. }
  222. // Returns true on error.
  223. static bool format(StringRef FileName) {
  224. ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
  225. MemoryBuffer::getFileOrSTDIN(FileName);
  226. if (std::error_code EC = CodeOrErr.getError()) {
  227. llvm::errs() << EC.message() << "\n";
  228. return true;
  229. }
  230. std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
  231. if (Code->getBufferSize() == 0)
  232. return false; // Empty files are formatted correctly.
  233. std::vector<tooling::Range> Ranges;
  234. if (fillRanges(Code.get(), Ranges))
  235. return true;
  236. StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
  237. FormatStyle FormatStyle = getStyle(Style, AssumedFileName, FallbackStyle);
  238. if (SortIncludes.getNumOccurrences() != 0)
  239. FormatStyle.SortIncludes = SortIncludes;
  240. Replacements Replaces =
  241. sortIncludes(FormatStyle, Code->getBuffer(), Ranges, AssumedFileName);
  242. std::string ChangedCode =
  243. tooling::applyAllReplacements(Code->getBuffer(), Replaces);
  244. for (const auto &R : Replaces)
  245. Ranges.push_back({R.getOffset(), R.getLength()});
  246. bool IncompleteFormat = false;
  247. Replaces = tooling::mergeReplacements(
  248. Replaces, reformat(FormatStyle, ChangedCode, Ranges, AssumedFileName,
  249. &IncompleteFormat));
  250. if (OutputXML) {
  251. llvm::outs() << "<?xml version='1.0'?>\n<replacements "
  252. "xml:space='preserve' incomplete_format='"
  253. << (IncompleteFormat ? "true" : "false") << "'>\n";
  254. if (Cursor.getNumOccurrences() != 0)
  255. llvm::outs() << "<cursor>"
  256. << tooling::shiftedCodePosition(Replaces, Cursor)
  257. << "</cursor>\n";
  258. outputReplacementsXML(Replaces);
  259. llvm::outs() << "</replacements>\n";
  260. } else {
  261. IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
  262. new vfs::InMemoryFileSystem);
  263. FileManager Files(FileSystemOptions(), InMemoryFileSystem);
  264. DiagnosticsEngine Diagnostics(
  265. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
  266. new DiagnosticOptions);
  267. SourceManager Sources(Diagnostics, Files);
  268. FileID ID = createInMemoryFile(AssumedFileName, Code.get(), Sources, Files,
  269. InMemoryFileSystem.get());
  270. Rewriter Rewrite(Sources, LangOptions());
  271. tooling::applyAllReplacements(Replaces, Rewrite);
  272. if (Inplace) {
  273. if (FileName == "-")
  274. llvm::errs() << "error: cannot use -i when reading from stdin.\n";
  275. else if (Rewrite.overwriteChangedFiles())
  276. return true;
  277. } else {
  278. if (Cursor.getNumOccurrences() != 0)
  279. outs() << "{ \"Cursor\": "
  280. << tooling::shiftedCodePosition(Replaces, Cursor)
  281. << ", \"IncompleteFormat\": "
  282. << (IncompleteFormat ? "true" : "false") << " }\n";
  283. Rewrite.getEditBuffer(ID).write(outs());
  284. }
  285. }
  286. return false;
  287. }
  288. } // namespace format
  289. } // namespace clang
  290. static void PrintVersion() {
  291. raw_ostream &OS = outs();
  292. OS << clang::getClangToolFullVersion("clang-format") << '\n';
  293. }
  294. int main(int argc, const char **argv) {
  295. llvm::sys::PrintStackTraceOnErrorSignal();
  296. cl::HideUnrelatedOptions(ClangFormatCategory);
  297. cl::SetVersionPrinter(PrintVersion);
  298. cl::ParseCommandLineOptions(
  299. argc, argv,
  300. "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf code.\n\n"
  301. "If no arguments are specified, it formats the code from standard input\n"
  302. "and writes the result to the standard output.\n"
  303. "If <file>s are given, it reformats the files. If -i is specified\n"
  304. "together with <file>s, the files are edited in-place. Otherwise, the\n"
  305. "result is written to the standard output.\n");
  306. if (Help)
  307. cl::PrintHelpMessage();
  308. if (DumpConfig) {
  309. std::string Config =
  310. clang::format::configurationAsText(clang::format::getStyle(
  311. Style, FileNames.empty() ? AssumeFileName : FileNames[0],
  312. FallbackStyle));
  313. llvm::outs() << Config << "\n";
  314. return 0;
  315. }
  316. bool Error = false;
  317. switch (FileNames.size()) {
  318. case 0:
  319. Error = clang::format::format("-");
  320. break;
  321. case 1:
  322. Error = clang::format::format(FileNames[0]);
  323. break;
  324. default:
  325. if (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty()) {
  326. llvm::errs() << "error: -offset, -length and -lines can only be used for "
  327. "single file.\n";
  328. return 1;
  329. }
  330. for (unsigned i = 0; i < FileNames.size(); ++i)
  331. Error |= clang::format::format(FileNames[i]);
  332. break;
  333. }
  334. return Error ? 1 : 0;
  335. }