1e5dd7070Spatrick //===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// \file
10e5dd7070Spatrick /// This file implements a clang-format tool that automatically formats
11e5dd7070Spatrick /// (fragments of) C++ code.
12e5dd7070Spatrick ///
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick
15e5dd7070Spatrick #include "clang/Basic/Diagnostic.h"
16e5dd7070Spatrick #include "clang/Basic/DiagnosticOptions.h"
17e5dd7070Spatrick #include "clang/Basic/FileManager.h"
18e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
19e5dd7070Spatrick #include "clang/Basic/Version.h"
20e5dd7070Spatrick #include "clang/Format/Format.h"
21e5dd7070Spatrick #include "clang/Rewrite/Core/Rewriter.h"
22*12c85518Srobert #include "llvm/ADT/StringSwitch.h"
23e5dd7070Spatrick #include "llvm/Support/CommandLine.h"
24e5dd7070Spatrick #include "llvm/Support/FileSystem.h"
25e5dd7070Spatrick #include "llvm/Support/InitLLVM.h"
26e5dd7070Spatrick #include "llvm/Support/Process.h"
27*12c85518Srobert #include <fstream>
28e5dd7070Spatrick
29e5dd7070Spatrick using namespace llvm;
30e5dd7070Spatrick using clang::tooling::Replacements;
31e5dd7070Spatrick
32e5dd7070Spatrick static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
33e5dd7070Spatrick
34e5dd7070Spatrick // Mark all our options with this category, everything else (except for -version
35e5dd7070Spatrick // and -help) will be hidden.
36e5dd7070Spatrick static cl::OptionCategory ClangFormatCategory("Clang-format options");
37e5dd7070Spatrick
38e5dd7070Spatrick static cl::list<unsigned>
39e5dd7070Spatrick Offsets("offset",
40e5dd7070Spatrick cl::desc("Format a range starting at this byte offset.\n"
41e5dd7070Spatrick "Multiple ranges can be formatted by specifying\n"
42e5dd7070Spatrick "several -offset and -length pairs.\n"
43e5dd7070Spatrick "Can only be used with one input file."),
44e5dd7070Spatrick cl::cat(ClangFormatCategory));
45e5dd7070Spatrick static cl::list<unsigned>
46e5dd7070Spatrick Lengths("length",
47e5dd7070Spatrick cl::desc("Format a range of this length (in bytes).\n"
48e5dd7070Spatrick "Multiple ranges can be formatted by specifying\n"
49e5dd7070Spatrick "several -offset and -length pairs.\n"
50e5dd7070Spatrick "When only a single -offset is specified without\n"
51e5dd7070Spatrick "-length, clang-format will format up to the end\n"
52e5dd7070Spatrick "of the file.\n"
53e5dd7070Spatrick "Can only be used with one input file."),
54e5dd7070Spatrick cl::cat(ClangFormatCategory));
55e5dd7070Spatrick static cl::list<std::string>
56e5dd7070Spatrick LineRanges("lines",
57e5dd7070Spatrick cl::desc("<start line>:<end line> - format a range of\n"
58e5dd7070Spatrick "lines (both 1-based).\n"
59e5dd7070Spatrick "Multiple ranges can be formatted by specifying\n"
60e5dd7070Spatrick "several -lines arguments.\n"
61e5dd7070Spatrick "Can't be used with -offset and -length.\n"
62e5dd7070Spatrick "Can only be used with one input file."),
63e5dd7070Spatrick cl::cat(ClangFormatCategory));
64e5dd7070Spatrick static cl::opt<std::string>
65e5dd7070Spatrick Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
66e5dd7070Spatrick cl::init(clang::format::DefaultFormatStyle),
67e5dd7070Spatrick cl::cat(ClangFormatCategory));
68e5dd7070Spatrick static cl::opt<std::string>
69e5dd7070Spatrick FallbackStyle("fallback-style",
70e5dd7070Spatrick cl::desc("The name of the predefined style used as a\n"
71e5dd7070Spatrick "fallback in case clang-format is invoked with\n"
72e5dd7070Spatrick "-style=file, but can not find the .clang-format\n"
73*12c85518Srobert "file to use. Defaults to 'LLVM'.\n"
74e5dd7070Spatrick "Use -fallback-style=none to skip formatting."),
75e5dd7070Spatrick cl::init(clang::format::DefaultFallbackStyle),
76e5dd7070Spatrick cl::cat(ClangFormatCategory));
77e5dd7070Spatrick
78e5dd7070Spatrick static cl::opt<std::string> AssumeFileName(
79e5dd7070Spatrick "assume-filename",
80*12c85518Srobert cl::desc("Set filename used to determine the language and to find\n"
81*12c85518Srobert ".clang-format file.\n"
82*12c85518Srobert "Only used when reading from stdin.\n"
83*12c85518Srobert "If this is not passed, the .clang-format file is searched\n"
84*12c85518Srobert "relative to the current working directory when reading stdin.\n"
85*12c85518Srobert "Unrecognized filenames are treated as C++.\n"
86*12c85518Srobert "supported:\n"
87*12c85518Srobert " CSharp: .cs\n"
88*12c85518Srobert " Java: .java\n"
89*12c85518Srobert " JavaScript: .mjs .js .ts\n"
90*12c85518Srobert " Json: .json\n"
91*12c85518Srobert " Objective-C: .m .mm\n"
92*12c85518Srobert " Proto: .proto .protodevel\n"
93*12c85518Srobert " TableGen: .td\n"
94*12c85518Srobert " TextProto: .textpb .pb.txt .textproto .asciipb\n"
95*12c85518Srobert " Verilog: .sv .svh .v .vh"),
96e5dd7070Spatrick cl::init("<stdin>"), cl::cat(ClangFormatCategory));
97e5dd7070Spatrick
98e5dd7070Spatrick static cl::opt<bool> Inplace("i",
99e5dd7070Spatrick cl::desc("Inplace edit <file>s, if specified."),
100e5dd7070Spatrick cl::cat(ClangFormatCategory));
101e5dd7070Spatrick
102e5dd7070Spatrick static cl::opt<bool> OutputXML("output-replacements-xml",
103e5dd7070Spatrick cl::desc("Output replacements as XML."),
104e5dd7070Spatrick cl::cat(ClangFormatCategory));
105e5dd7070Spatrick static cl::opt<bool>
106e5dd7070Spatrick DumpConfig("dump-config",
107e5dd7070Spatrick cl::desc("Dump configuration options to stdout and exit.\n"
108e5dd7070Spatrick "Can be used with -style option."),
109e5dd7070Spatrick cl::cat(ClangFormatCategory));
110e5dd7070Spatrick static cl::opt<unsigned>
111e5dd7070Spatrick Cursor("cursor",
112e5dd7070Spatrick cl::desc("The position of the cursor when invoking\n"
113e5dd7070Spatrick "clang-format from an editor integration"),
114e5dd7070Spatrick cl::init(0), cl::cat(ClangFormatCategory));
115e5dd7070Spatrick
116*12c85518Srobert static cl::opt<bool>
117*12c85518Srobert SortIncludes("sort-includes",
118*12c85518Srobert cl::desc("If set, overrides the include sorting behavior\n"
119*12c85518Srobert "determined by the SortIncludes style flag"),
120e5dd7070Spatrick cl::cat(ClangFormatCategory));
121e5dd7070Spatrick
122*12c85518Srobert static cl::opt<std::string> QualifierAlignment(
123*12c85518Srobert "qualifier-alignment",
124*12c85518Srobert cl::desc("If set, overrides the qualifier alignment style\n"
125*12c85518Srobert "determined by the QualifierAlignment style flag"),
126*12c85518Srobert cl::init(""), cl::cat(ClangFormatCategory));
127*12c85518Srobert
128*12c85518Srobert static cl::opt<std::string> Files(
129*12c85518Srobert "files",
130*12c85518Srobert cl::desc("A file containing a list of files to process, one per line."),
131*12c85518Srobert cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
132*12c85518Srobert
133e5dd7070Spatrick static cl::opt<bool>
134e5dd7070Spatrick Verbose("verbose", cl::desc("If set, shows the list of processed files"),
135e5dd7070Spatrick cl::cat(ClangFormatCategory));
136e5dd7070Spatrick
137e5dd7070Spatrick // Use --dry-run to match other LLVM tools when you mean do it but don't
138e5dd7070Spatrick // actually do it
139e5dd7070Spatrick static cl::opt<bool>
140e5dd7070Spatrick DryRun("dry-run",
141e5dd7070Spatrick cl::desc("If set, do not actually make the formatting changes"),
142e5dd7070Spatrick cl::cat(ClangFormatCategory));
143e5dd7070Spatrick
144e5dd7070Spatrick // Use -n as a common command as an alias for --dry-run. (git and make use -n)
145e5dd7070Spatrick static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
146e5dd7070Spatrick cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
147e5dd7070Spatrick cl::NotHidden);
148e5dd7070Spatrick
149e5dd7070Spatrick // Emulate being able to turn on/off the warning.
150e5dd7070Spatrick static cl::opt<bool>
151e5dd7070Spatrick WarnFormat("Wclang-format-violations",
152e5dd7070Spatrick cl::desc("Warnings about individual formatting changes needed. "
153e5dd7070Spatrick "Used only with --dry-run or -n"),
154e5dd7070Spatrick cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
155e5dd7070Spatrick
156e5dd7070Spatrick static cl::opt<bool>
157e5dd7070Spatrick NoWarnFormat("Wno-clang-format-violations",
158e5dd7070Spatrick cl::desc("Do not warn about individual formatting changes "
159e5dd7070Spatrick "needed. Used only with --dry-run or -n"),
160e5dd7070Spatrick cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
161e5dd7070Spatrick
162e5dd7070Spatrick static cl::opt<unsigned> ErrorLimit(
163e5dd7070Spatrick "ferror-limit",
164*12c85518Srobert cl::desc("Set the maximum number of clang-format errors to emit\n"
165*12c85518Srobert "before stopping (0 = no limit).\n"
166*12c85518Srobert "Used only with --dry-run or -n"),
167e5dd7070Spatrick cl::init(0), cl::cat(ClangFormatCategory));
168e5dd7070Spatrick
169e5dd7070Spatrick static cl::opt<bool>
170e5dd7070Spatrick WarningsAsErrors("Werror",
171e5dd7070Spatrick cl::desc("If set, changes formatting warnings to errors"),
172e5dd7070Spatrick cl::cat(ClangFormatCategory));
173e5dd7070Spatrick
174a9ac8606Spatrick namespace {
175a9ac8606Spatrick enum class WNoError { Unknown };
176a9ac8606Spatrick }
177a9ac8606Spatrick
178a9ac8606Spatrick static cl::bits<WNoError> WNoErrorList(
179a9ac8606Spatrick "Wno-error",
180a9ac8606Spatrick cl::desc("If set don't error out on the specified warning type."),
181a9ac8606Spatrick cl::values(
182a9ac8606Spatrick clEnumValN(WNoError::Unknown, "unknown",
183a9ac8606Spatrick "If set, unknown format options are only warned about.\n"
184a9ac8606Spatrick "This can be used to enable formatting, even if the\n"
185a9ac8606Spatrick "configuration contains unknown (newer) options.\n"
186a9ac8606Spatrick "Use with caution, as this might lead to dramatically\n"
187a9ac8606Spatrick "differing format depending on an option being\n"
188a9ac8606Spatrick "supported or not.")),
189a9ac8606Spatrick cl::cat(ClangFormatCategory));
190a9ac8606Spatrick
191e5dd7070Spatrick static cl::opt<bool>
192e5dd7070Spatrick ShowColors("fcolor-diagnostics",
193e5dd7070Spatrick cl::desc("If set, and on a color-capable terminal controls "
194e5dd7070Spatrick "whether or not to print diagnostics in color"),
195e5dd7070Spatrick cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
196e5dd7070Spatrick
197e5dd7070Spatrick static cl::opt<bool>
198e5dd7070Spatrick NoShowColors("fno-color-diagnostics",
199e5dd7070Spatrick cl::desc("If set, and on a color-capable terminal controls "
200e5dd7070Spatrick "whether or not to print diagnostics in color"),
201e5dd7070Spatrick cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
202e5dd7070Spatrick
203e5dd7070Spatrick static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
204e5dd7070Spatrick cl::cat(ClangFormatCategory));
205e5dd7070Spatrick
206e5dd7070Spatrick namespace clang {
207e5dd7070Spatrick namespace format {
208e5dd7070Spatrick
createInMemoryFile(StringRef FileName,MemoryBufferRef Source,SourceManager & Sources,FileManager & Files,llvm::vfs::InMemoryFileSystem * MemFS)209a9ac8606Spatrick static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
210e5dd7070Spatrick SourceManager &Sources, FileManager &Files,
211e5dd7070Spatrick llvm::vfs::InMemoryFileSystem *MemFS) {
212e5dd7070Spatrick MemFS->addFileNoOwn(FileName, 0, Source);
213a9ac8606Spatrick auto File = Files.getOptionalFileRef(FileName);
214a9ac8606Spatrick assert(File && "File not added to MemFS?");
215a9ac8606Spatrick return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
216e5dd7070Spatrick }
217e5dd7070Spatrick
218e5dd7070Spatrick // Parses <start line>:<end line> input to a pair of line numbers.
219e5dd7070Spatrick // Returns true on error.
parseLineRange(StringRef Input,unsigned & FromLine,unsigned & ToLine)220e5dd7070Spatrick static bool parseLineRange(StringRef Input, unsigned &FromLine,
221e5dd7070Spatrick unsigned &ToLine) {
222e5dd7070Spatrick std::pair<StringRef, StringRef> LineRange = Input.split(':');
223e5dd7070Spatrick return LineRange.first.getAsInteger(0, FromLine) ||
224e5dd7070Spatrick LineRange.second.getAsInteger(0, ToLine);
225e5dd7070Spatrick }
226e5dd7070Spatrick
fillRanges(MemoryBuffer * Code,std::vector<tooling::Range> & Ranges)227e5dd7070Spatrick static bool fillRanges(MemoryBuffer *Code,
228e5dd7070Spatrick std::vector<tooling::Range> &Ranges) {
229e5dd7070Spatrick IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
230e5dd7070Spatrick new llvm::vfs::InMemoryFileSystem);
231e5dd7070Spatrick FileManager Files(FileSystemOptions(), InMemoryFileSystem);
232e5dd7070Spatrick DiagnosticsEngine Diagnostics(
233e5dd7070Spatrick IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
234e5dd7070Spatrick new DiagnosticOptions);
235e5dd7070Spatrick SourceManager Sources(Diagnostics, Files);
236a9ac8606Spatrick FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
237e5dd7070Spatrick InMemoryFileSystem.get());
238e5dd7070Spatrick if (!LineRanges.empty()) {
239e5dd7070Spatrick if (!Offsets.empty() || !Lengths.empty()) {
240e5dd7070Spatrick errs() << "error: cannot use -lines with -offset/-length\n";
241e5dd7070Spatrick return true;
242e5dd7070Spatrick }
243e5dd7070Spatrick
244e5dd7070Spatrick for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
245e5dd7070Spatrick unsigned FromLine, ToLine;
246e5dd7070Spatrick if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
247e5dd7070Spatrick errs() << "error: invalid <start line>:<end line> pair\n";
248e5dd7070Spatrick return true;
249e5dd7070Spatrick }
250*12c85518Srobert if (FromLine < 1) {
251*12c85518Srobert errs() << "error: start line should be at least 1\n";
252*12c85518Srobert return true;
253*12c85518Srobert }
254e5dd7070Spatrick if (FromLine > ToLine) {
255*12c85518Srobert errs() << "error: start line should not exceed end line\n";
256e5dd7070Spatrick return true;
257e5dd7070Spatrick }
258e5dd7070Spatrick SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
259e5dd7070Spatrick SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
260e5dd7070Spatrick if (Start.isInvalid() || End.isInvalid())
261e5dd7070Spatrick return true;
262e5dd7070Spatrick unsigned Offset = Sources.getFileOffset(Start);
263e5dd7070Spatrick unsigned Length = Sources.getFileOffset(End) - Offset;
264e5dd7070Spatrick Ranges.push_back(tooling::Range(Offset, Length));
265e5dd7070Spatrick }
266e5dd7070Spatrick return false;
267e5dd7070Spatrick }
268e5dd7070Spatrick
269e5dd7070Spatrick if (Offsets.empty())
270e5dd7070Spatrick Offsets.push_back(0);
271e5dd7070Spatrick if (Offsets.size() != Lengths.size() &&
272e5dd7070Spatrick !(Offsets.size() == 1 && Lengths.empty())) {
273e5dd7070Spatrick errs() << "error: number of -offset and -length arguments must match.\n";
274e5dd7070Spatrick return true;
275e5dd7070Spatrick }
276e5dd7070Spatrick for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
277e5dd7070Spatrick if (Offsets[i] >= Code->getBufferSize()) {
278e5dd7070Spatrick errs() << "error: offset " << Offsets[i] << " is outside the file\n";
279e5dd7070Spatrick return true;
280e5dd7070Spatrick }
281e5dd7070Spatrick SourceLocation Start =
282e5dd7070Spatrick Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
283e5dd7070Spatrick SourceLocation End;
284e5dd7070Spatrick if (i < Lengths.size()) {
285e5dd7070Spatrick if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
286e5dd7070Spatrick errs() << "error: invalid length " << Lengths[i]
287e5dd7070Spatrick << ", offset + length (" << Offsets[i] + Lengths[i]
288e5dd7070Spatrick << ") is outside the file.\n";
289e5dd7070Spatrick return true;
290e5dd7070Spatrick }
291e5dd7070Spatrick End = Start.getLocWithOffset(Lengths[i]);
292e5dd7070Spatrick } else {
293e5dd7070Spatrick End = Sources.getLocForEndOfFile(ID);
294e5dd7070Spatrick }
295e5dd7070Spatrick unsigned Offset = Sources.getFileOffset(Start);
296e5dd7070Spatrick unsigned Length = Sources.getFileOffset(End) - Offset;
297e5dd7070Spatrick Ranges.push_back(tooling::Range(Offset, Length));
298e5dd7070Spatrick }
299e5dd7070Spatrick return false;
300e5dd7070Spatrick }
301e5dd7070Spatrick
outputReplacementXML(StringRef Text)302e5dd7070Spatrick static void outputReplacementXML(StringRef Text) {
303e5dd7070Spatrick // FIXME: When we sort includes, we need to make sure the stream is correct
304e5dd7070Spatrick // utf-8.
305e5dd7070Spatrick size_t From = 0;
306e5dd7070Spatrick size_t Index;
307e5dd7070Spatrick while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
308e5dd7070Spatrick outs() << Text.substr(From, Index - From);
309e5dd7070Spatrick switch (Text[Index]) {
310e5dd7070Spatrick case '\n':
311e5dd7070Spatrick outs() << " ";
312e5dd7070Spatrick break;
313e5dd7070Spatrick case '\r':
314e5dd7070Spatrick outs() << " ";
315e5dd7070Spatrick break;
316e5dd7070Spatrick case '<':
317e5dd7070Spatrick outs() << "<";
318e5dd7070Spatrick break;
319e5dd7070Spatrick case '&':
320e5dd7070Spatrick outs() << "&";
321e5dd7070Spatrick break;
322e5dd7070Spatrick default:
323e5dd7070Spatrick llvm_unreachable("Unexpected character encountered!");
324e5dd7070Spatrick }
325e5dd7070Spatrick From = Index + 1;
326e5dd7070Spatrick }
327e5dd7070Spatrick outs() << Text.substr(From);
328e5dd7070Spatrick }
329e5dd7070Spatrick
outputReplacementsXML(const Replacements & Replaces)330e5dd7070Spatrick static void outputReplacementsXML(const Replacements &Replaces) {
331e5dd7070Spatrick for (const auto &R : Replaces) {
332e5dd7070Spatrick outs() << "<replacement "
333e5dd7070Spatrick << "offset='" << R.getOffset() << "' "
334e5dd7070Spatrick << "length='" << R.getLength() << "'>";
335e5dd7070Spatrick outputReplacementXML(R.getReplacementText());
336e5dd7070Spatrick outs() << "</replacement>\n";
337e5dd7070Spatrick }
338e5dd7070Spatrick }
339e5dd7070Spatrick
340e5dd7070Spatrick static bool
emitReplacementWarnings(const Replacements & Replaces,StringRef AssumedFileName,const std::unique_ptr<llvm::MemoryBuffer> & Code)341e5dd7070Spatrick emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
342e5dd7070Spatrick const std::unique_ptr<llvm::MemoryBuffer> &Code) {
343e5dd7070Spatrick if (Replaces.empty())
344e5dd7070Spatrick return false;
345e5dd7070Spatrick
346e5dd7070Spatrick unsigned Errors = 0;
347e5dd7070Spatrick if (WarnFormat && !NoWarnFormat) {
348e5dd7070Spatrick llvm::SourceMgr Mgr;
349e5dd7070Spatrick const char *StartBuf = Code->getBufferStart();
350e5dd7070Spatrick
351e5dd7070Spatrick Mgr.AddNewSourceBuffer(
352e5dd7070Spatrick MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
353e5dd7070Spatrick for (const auto &R : Replaces) {
354e5dd7070Spatrick SMDiagnostic Diag = Mgr.GetMessage(
355e5dd7070Spatrick SMLoc::getFromPointer(StartBuf + R.getOffset()),
356e5dd7070Spatrick WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
357e5dd7070Spatrick : SourceMgr::DiagKind::DK_Warning,
358e5dd7070Spatrick "code should be clang-formatted [-Wclang-format-violations]");
359e5dd7070Spatrick
360e5dd7070Spatrick Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
361e5dd7070Spatrick if (ErrorLimit && ++Errors >= ErrorLimit)
362e5dd7070Spatrick break;
363e5dd7070Spatrick }
364e5dd7070Spatrick }
365e5dd7070Spatrick return WarningsAsErrors;
366e5dd7070Spatrick }
367e5dd7070Spatrick
outputXML(const Replacements & Replaces,const Replacements & FormatChanges,const FormattingAttemptStatus & Status,const cl::opt<unsigned> & Cursor,unsigned CursorPosition)368e5dd7070Spatrick static void outputXML(const Replacements &Replaces,
369e5dd7070Spatrick const Replacements &FormatChanges,
370e5dd7070Spatrick const FormattingAttemptStatus &Status,
371e5dd7070Spatrick const cl::opt<unsigned> &Cursor,
372e5dd7070Spatrick unsigned CursorPosition) {
373e5dd7070Spatrick outs() << "<?xml version='1.0'?>\n<replacements "
374e5dd7070Spatrick "xml:space='preserve' incomplete_format='"
375e5dd7070Spatrick << (Status.FormatComplete ? "false" : "true") << "'";
376e5dd7070Spatrick if (!Status.FormatComplete)
377e5dd7070Spatrick outs() << " line='" << Status.Line << "'";
378e5dd7070Spatrick outs() << ">\n";
379*12c85518Srobert if (Cursor.getNumOccurrences() != 0) {
380e5dd7070Spatrick outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
381e5dd7070Spatrick << "</cursor>\n";
382*12c85518Srobert }
383e5dd7070Spatrick
384e5dd7070Spatrick outputReplacementsXML(Replaces);
385e5dd7070Spatrick outs() << "</replacements>\n";
386e5dd7070Spatrick }
387e5dd7070Spatrick
388*12c85518Srobert class ClangFormatDiagConsumer : public DiagnosticConsumer {
anchor()389*12c85518Srobert virtual void anchor() {}
390*12c85518Srobert
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)391*12c85518Srobert void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
392*12c85518Srobert const Diagnostic &Info) override {
393*12c85518Srobert
394*12c85518Srobert SmallVector<char, 16> vec;
395*12c85518Srobert Info.FormatDiagnostic(vec);
396*12c85518Srobert errs() << "clang-format error:" << vec << "\n";
397*12c85518Srobert }
398*12c85518Srobert };
399*12c85518Srobert
400e5dd7070Spatrick // Returns true on error.
format(StringRef FileName)401e5dd7070Spatrick static bool format(StringRef FileName) {
402e5dd7070Spatrick if (!OutputXML && Inplace && FileName == "-") {
403e5dd7070Spatrick errs() << "error: cannot use -i when reading from stdin.\n";
404e5dd7070Spatrick return false;
405e5dd7070Spatrick }
406e5dd7070Spatrick // On Windows, overwriting a file with an open file mapping doesn't work,
407e5dd7070Spatrick // so read the whole file into memory when formatting in-place.
408e5dd7070Spatrick ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
409e5dd7070Spatrick !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
410e5dd7070Spatrick : MemoryBuffer::getFileOrSTDIN(FileName);
411e5dd7070Spatrick if (std::error_code EC = CodeOrErr.getError()) {
412e5dd7070Spatrick errs() << EC.message() << "\n";
413e5dd7070Spatrick return true;
414e5dd7070Spatrick }
415e5dd7070Spatrick std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
416e5dd7070Spatrick if (Code->getBufferSize() == 0)
417e5dd7070Spatrick return false; // Empty files are formatted correctly.
418e5dd7070Spatrick
419e5dd7070Spatrick StringRef BufStr = Code->getBuffer();
420e5dd7070Spatrick
421e5dd7070Spatrick const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
422e5dd7070Spatrick
423e5dd7070Spatrick if (InvalidBOM) {
424e5dd7070Spatrick errs() << "error: encoding with unsupported byte order mark \""
425e5dd7070Spatrick << InvalidBOM << "\" detected";
426e5dd7070Spatrick if (FileName != "-")
427e5dd7070Spatrick errs() << " in file '" << FileName << "'";
428e5dd7070Spatrick errs() << ".\n";
429e5dd7070Spatrick return true;
430e5dd7070Spatrick }
431e5dd7070Spatrick
432e5dd7070Spatrick std::vector<tooling::Range> Ranges;
433e5dd7070Spatrick if (fillRanges(Code.get(), Ranges))
434e5dd7070Spatrick return true;
435e5dd7070Spatrick StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
436e5dd7070Spatrick if (AssumedFileName.empty()) {
437e5dd7070Spatrick llvm::errs() << "error: empty filenames are not allowed\n";
438e5dd7070Spatrick return true;
439e5dd7070Spatrick }
440e5dd7070Spatrick
441e5dd7070Spatrick llvm::Expected<FormatStyle> FormatStyle =
442a9ac8606Spatrick getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
443a9ac8606Spatrick nullptr, WNoErrorList.isSet(WNoError::Unknown));
444e5dd7070Spatrick if (!FormatStyle) {
445e5dd7070Spatrick llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
446e5dd7070Spatrick return true;
447e5dd7070Spatrick }
448e5dd7070Spatrick
449*12c85518Srobert StringRef QualifierAlignmentOrder = QualifierAlignment;
450*12c85518Srobert
451*12c85518Srobert FormatStyle->QualifierAlignment =
452*12c85518Srobert StringSwitch<FormatStyle::QualifierAlignmentStyle>(
453*12c85518Srobert QualifierAlignmentOrder.lower())
454*12c85518Srobert .Case("right", FormatStyle::QAS_Right)
455*12c85518Srobert .Case("left", FormatStyle::QAS_Left)
456*12c85518Srobert .Default(FormatStyle->QualifierAlignment);
457*12c85518Srobert
458*12c85518Srobert if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
459*12c85518Srobert FormatStyle->QualifierOrder = {"const", "volatile", "type"};
460*12c85518Srobert } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
461*12c85518Srobert FormatStyle->QualifierOrder = {"type", "const", "volatile"};
462*12c85518Srobert } else if (QualifierAlignmentOrder.contains("type")) {
463*12c85518Srobert FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
464*12c85518Srobert SmallVector<StringRef> Qualifiers;
465*12c85518Srobert QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
466*12c85518Srobert /*KeepEmpty=*/false);
467*12c85518Srobert FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
468*12c85518Srobert }
469*12c85518Srobert
470a9ac8606Spatrick if (SortIncludes.getNumOccurrences() != 0) {
471a9ac8606Spatrick if (SortIncludes)
472a9ac8606Spatrick FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
473a9ac8606Spatrick else
474a9ac8606Spatrick FormatStyle->SortIncludes = FormatStyle::SI_Never;
475a9ac8606Spatrick }
476e5dd7070Spatrick unsigned CursorPosition = Cursor;
477e5dd7070Spatrick Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
478e5dd7070Spatrick AssumedFileName, &CursorPosition);
479a9ac8606Spatrick
480a9ac8606Spatrick // To format JSON insert a variable to trick the code into thinking its
481a9ac8606Spatrick // JavaScript.
482*12c85518Srobert if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
483a9ac8606Spatrick auto Err = Replaces.add(tooling::Replacement(
484a9ac8606Spatrick tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
485*12c85518Srobert if (Err)
486a9ac8606Spatrick llvm::errs() << "Bad Json variable insertion\n";
487a9ac8606Spatrick }
488a9ac8606Spatrick
489e5dd7070Spatrick auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
490e5dd7070Spatrick if (!ChangedCode) {
491e5dd7070Spatrick llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
492e5dd7070Spatrick return true;
493e5dd7070Spatrick }
494e5dd7070Spatrick // Get new affected ranges after sorting `#includes`.
495e5dd7070Spatrick Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
496e5dd7070Spatrick FormattingAttemptStatus Status;
497e5dd7070Spatrick Replacements FormatChanges =
498e5dd7070Spatrick reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
499e5dd7070Spatrick Replaces = Replaces.merge(FormatChanges);
500e5dd7070Spatrick if (OutputXML || DryRun) {
501*12c85518Srobert if (DryRun)
502e5dd7070Spatrick return emitReplacementWarnings(Replaces, AssumedFileName, Code);
503*12c85518Srobert else
504e5dd7070Spatrick outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
505e5dd7070Spatrick } else {
506e5dd7070Spatrick IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
507e5dd7070Spatrick new llvm::vfs::InMemoryFileSystem);
508e5dd7070Spatrick FileManager Files(FileSystemOptions(), InMemoryFileSystem);
509*12c85518Srobert
510*12c85518Srobert IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
511*12c85518Srobert ClangFormatDiagConsumer IgnoreDiagnostics;
512e5dd7070Spatrick DiagnosticsEngine Diagnostics(
513*12c85518Srobert IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
514*12c85518Srobert &IgnoreDiagnostics, false);
515e5dd7070Spatrick SourceManager Sources(Diagnostics, Files);
516a9ac8606Spatrick FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
517e5dd7070Spatrick InMemoryFileSystem.get());
518e5dd7070Spatrick Rewriter Rewrite(Sources, LangOptions());
519e5dd7070Spatrick tooling::applyAllReplacements(Replaces, Rewrite);
520e5dd7070Spatrick if (Inplace) {
521e5dd7070Spatrick if (Rewrite.overwriteChangedFiles())
522e5dd7070Spatrick return true;
523e5dd7070Spatrick } else {
524e5dd7070Spatrick if (Cursor.getNumOccurrences() != 0) {
525e5dd7070Spatrick outs() << "{ \"Cursor\": "
526e5dd7070Spatrick << FormatChanges.getShiftedCodePosition(CursorPosition)
527e5dd7070Spatrick << ", \"IncompleteFormat\": "
528e5dd7070Spatrick << (Status.FormatComplete ? "false" : "true");
529e5dd7070Spatrick if (!Status.FormatComplete)
530e5dd7070Spatrick outs() << ", \"Line\": " << Status.Line;
531e5dd7070Spatrick outs() << " }\n";
532e5dd7070Spatrick }
533e5dd7070Spatrick Rewrite.getEditBuffer(ID).write(outs());
534e5dd7070Spatrick }
535e5dd7070Spatrick }
536e5dd7070Spatrick return false;
537e5dd7070Spatrick }
538e5dd7070Spatrick
539e5dd7070Spatrick } // namespace format
540e5dd7070Spatrick } // namespace clang
541e5dd7070Spatrick
PrintVersion(raw_ostream & OS)542e5dd7070Spatrick static void PrintVersion(raw_ostream &OS) {
543e5dd7070Spatrick OS << clang::getClangToolFullVersion("clang-format") << '\n';
544e5dd7070Spatrick }
545e5dd7070Spatrick
546e5dd7070Spatrick // Dump the configuration.
dumpConfig()547e5dd7070Spatrick static int dumpConfig() {
548e5dd7070Spatrick StringRef FileName;
549e5dd7070Spatrick std::unique_ptr<llvm::MemoryBuffer> Code;
550e5dd7070Spatrick if (FileNames.empty()) {
551e5dd7070Spatrick // We can't read the code to detect the language if there's no
552e5dd7070Spatrick // file name, so leave Code empty here.
553e5dd7070Spatrick FileName = AssumeFileName;
554e5dd7070Spatrick } else {
555e5dd7070Spatrick // Read in the code in case the filename alone isn't enough to
556e5dd7070Spatrick // detect the language.
557e5dd7070Spatrick ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
558e5dd7070Spatrick MemoryBuffer::getFileOrSTDIN(FileNames[0]);
559e5dd7070Spatrick if (std::error_code EC = CodeOrErr.getError()) {
560e5dd7070Spatrick llvm::errs() << EC.message() << "\n";
561e5dd7070Spatrick return 1;
562e5dd7070Spatrick }
563e5dd7070Spatrick FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
564e5dd7070Spatrick Code = std::move(CodeOrErr.get());
565e5dd7070Spatrick }
566e5dd7070Spatrick llvm::Expected<clang::format::FormatStyle> FormatStyle =
567e5dd7070Spatrick clang::format::getStyle(Style, FileName, FallbackStyle,
568e5dd7070Spatrick Code ? Code->getBuffer() : "");
569e5dd7070Spatrick if (!FormatStyle) {
570e5dd7070Spatrick llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
571e5dd7070Spatrick return 1;
572e5dd7070Spatrick }
573e5dd7070Spatrick std::string Config = clang::format::configurationAsText(*FormatStyle);
574e5dd7070Spatrick outs() << Config << "\n";
575e5dd7070Spatrick return 0;
576e5dd7070Spatrick }
577e5dd7070Spatrick
main(int argc,const char ** argv)578e5dd7070Spatrick int main(int argc, const char **argv) {
579e5dd7070Spatrick llvm::InitLLVM X(argc, argv);
580e5dd7070Spatrick
581e5dd7070Spatrick cl::HideUnrelatedOptions(ClangFormatCategory);
582e5dd7070Spatrick
583e5dd7070Spatrick cl::SetVersionPrinter(PrintVersion);
584e5dd7070Spatrick cl::ParseCommandLineOptions(
585e5dd7070Spatrick argc, argv,
586a9ac8606Spatrick "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
587a9ac8606Spatrick "code.\n\n"
588e5dd7070Spatrick "If no arguments are specified, it formats the code from standard input\n"
589e5dd7070Spatrick "and writes the result to the standard output.\n"
590e5dd7070Spatrick "If <file>s are given, it reformats the files. If -i is specified\n"
591e5dd7070Spatrick "together with <file>s, the files are edited in-place. Otherwise, the\n"
592e5dd7070Spatrick "result is written to the standard output.\n");
593e5dd7070Spatrick
594e5dd7070Spatrick if (Help) {
595e5dd7070Spatrick cl::PrintHelpMessage();
596e5dd7070Spatrick return 0;
597e5dd7070Spatrick }
598e5dd7070Spatrick
599*12c85518Srobert if (DumpConfig)
600e5dd7070Spatrick return dumpConfig();
601*12c85518Srobert
602*12c85518Srobert if (!Files.empty()) {
603*12c85518Srobert std::ifstream ExternalFileOfFiles{std::string(Files)};
604*12c85518Srobert std::string Line;
605*12c85518Srobert unsigned LineNo = 1;
606*12c85518Srobert while (std::getline(ExternalFileOfFiles, Line)) {
607*12c85518Srobert FileNames.push_back(Line);
608*12c85518Srobert LineNo++;
609*12c85518Srobert }
610*12c85518Srobert errs() << "Clang-formating " << LineNo << " files\n";
611e5dd7070Spatrick }
612e5dd7070Spatrick
613e5dd7070Spatrick bool Error = false;
614e5dd7070Spatrick if (FileNames.empty()) {
615e5dd7070Spatrick Error = clang::format::format("-");
616e5dd7070Spatrick return Error ? 1 : 0;
617e5dd7070Spatrick }
618e5dd7070Spatrick if (FileNames.size() != 1 &&
619e5dd7070Spatrick (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
620e5dd7070Spatrick errs() << "error: -offset, -length and -lines can only be used for "
621e5dd7070Spatrick "single file.\n";
622e5dd7070Spatrick return 1;
623e5dd7070Spatrick }
624*12c85518Srobert
625*12c85518Srobert unsigned FileNo = 1;
626e5dd7070Spatrick for (const auto &FileName : FileNames) {
627*12c85518Srobert if (Verbose) {
628*12c85518Srobert errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
629*12c85518Srobert << FileName << "\n";
630*12c85518Srobert }
631e5dd7070Spatrick Error |= clang::format::format(FileName);
632e5dd7070Spatrick }
633e5dd7070Spatrick return Error ? 1 : 0;
634e5dd7070Spatrick }
635