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