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