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