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
createInMemoryFile(StringRef FileName,MemoryBuffer * Source,SourceManager & Sources,FileManager & Files,llvm::vfs::InMemoryFileSystem * MemFS)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.
parseLineRange(StringRef Input,unsigned & FromLine,unsigned & ToLine)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
fillRanges(MemoryBuffer * Code,std::vector<tooling::Range> & Ranges)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
outputReplacementXML(StringRef Text)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
outputReplacementsXML(const Replacements & Replaces)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
emitReplacementWarnings(const Replacements & Replaces,StringRef AssumedFileName,const std::unique_ptr<llvm::MemoryBuffer> & Code)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
outputXML(const Replacements & Replaces,const Replacements & FormatChanges,const FormattingAttemptStatus & Status,const cl::opt<unsigned> & Cursor,unsigned CursorPosition)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.
format(StringRef FileName)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
PrintVersion(raw_ostream & OS)443 static void PrintVersion(raw_ostream &OS) {
444 OS << clang::getClangToolFullVersion("clang-format") << '\n';
445 }
446
447 // Dump the configuration.
dumpConfig()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
main(int argc,const char ** argv)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