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