1*67e74705SXin Li //===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li ///
10*67e74705SXin Li /// \file
11*67e74705SXin Li /// \brief This file implements a clang-format tool that automatically formats
12*67e74705SXin Li /// (fragments of) C++ code.
13*67e74705SXin Li ///
14*67e74705SXin Li //===----------------------------------------------------------------------===//
15*67e74705SXin Li
16*67e74705SXin Li #include "clang/Basic/Diagnostic.h"
17*67e74705SXin Li #include "clang/Basic/DiagnosticOptions.h"
18*67e74705SXin Li #include "clang/Basic/FileManager.h"
19*67e74705SXin Li #include "clang/Basic/SourceManager.h"
20*67e74705SXin Li #include "clang/Basic/Version.h"
21*67e74705SXin Li #include "clang/Format/Format.h"
22*67e74705SXin Li #include "clang/Rewrite/Core/Rewriter.h"
23*67e74705SXin Li #include "llvm/ADT/StringMap.h"
24*67e74705SXin Li #include "llvm/Support/CommandLine.h"
25*67e74705SXin Li #include "llvm/Support/Debug.h"
26*67e74705SXin Li #include "llvm/Support/FileSystem.h"
27*67e74705SXin Li #include "llvm/Support/Signals.h"
28*67e74705SXin Li
29*67e74705SXin Li using namespace llvm;
30*67e74705SXin Li using clang::tooling::Replacements;
31*67e74705SXin Li
32*67e74705SXin Li static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
33*67e74705SXin Li
34*67e74705SXin Li // Mark all our options with this category, everything else (except for -version
35*67e74705SXin Li // and -help) will be hidden.
36*67e74705SXin Li static cl::OptionCategory ClangFormatCategory("Clang-format options");
37*67e74705SXin Li
38*67e74705SXin Li static cl::list<unsigned>
39*67e74705SXin Li Offsets("offset",
40*67e74705SXin Li cl::desc("Format a range starting at this byte offset.\n"
41*67e74705SXin Li "Multiple ranges can be formatted by specifying\n"
42*67e74705SXin Li "several -offset and -length pairs.\n"
43*67e74705SXin Li "Can only be used with one input file."),
44*67e74705SXin Li cl::cat(ClangFormatCategory));
45*67e74705SXin Li static cl::list<unsigned>
46*67e74705SXin Li Lengths("length",
47*67e74705SXin Li cl::desc("Format a range of this length (in bytes).\n"
48*67e74705SXin Li "Multiple ranges can be formatted by specifying\n"
49*67e74705SXin Li "several -offset and -length pairs.\n"
50*67e74705SXin Li "When only a single -offset is specified without\n"
51*67e74705SXin Li "-length, clang-format will format up to the end\n"
52*67e74705SXin Li "of the file.\n"
53*67e74705SXin Li "Can only be used with one input file."),
54*67e74705SXin Li cl::cat(ClangFormatCategory));
55*67e74705SXin Li static cl::list<std::string>
56*67e74705SXin Li LineRanges("lines", cl::desc("<start line>:<end line> - format a range of\n"
57*67e74705SXin Li "lines (both 1-based).\n"
58*67e74705SXin Li "Multiple ranges can be formatted by specifying\n"
59*67e74705SXin Li "several -lines arguments.\n"
60*67e74705SXin Li "Can't be used with -offset and -length.\n"
61*67e74705SXin Li "Can only be used with one input file."),
62*67e74705SXin Li cl::cat(ClangFormatCategory));
63*67e74705SXin Li static cl::opt<std::string>
64*67e74705SXin Li Style("style",
65*67e74705SXin Li cl::desc(clang::format::StyleOptionHelpDescription),
66*67e74705SXin Li cl::init("file"), cl::cat(ClangFormatCategory));
67*67e74705SXin Li static cl::opt<std::string>
68*67e74705SXin Li FallbackStyle("fallback-style",
69*67e74705SXin Li cl::desc("The name of the predefined style used as a\n"
70*67e74705SXin Li "fallback in case clang-format is invoked with\n"
71*67e74705SXin Li "-style=file, but can not find the .clang-format\n"
72*67e74705SXin Li "file to use.\n"
73*67e74705SXin Li "Use -fallback-style=none to skip formatting."),
74*67e74705SXin Li cl::init("LLVM"), cl::cat(ClangFormatCategory));
75*67e74705SXin Li
76*67e74705SXin Li static cl::opt<std::string>
77*67e74705SXin Li AssumeFileName("assume-filename",
78*67e74705SXin Li cl::desc("When reading from stdin, clang-format assumes this\n"
79*67e74705SXin Li "filename to look for a style config file (with\n"
80*67e74705SXin Li "-style=file) and to determine the language."),
81*67e74705SXin Li cl::init("<stdin>"), cl::cat(ClangFormatCategory));
82*67e74705SXin Li
83*67e74705SXin Li static cl::opt<bool> Inplace("i",
84*67e74705SXin Li cl::desc("Inplace edit <file>s, if specified."),
85*67e74705SXin Li cl::cat(ClangFormatCategory));
86*67e74705SXin Li
87*67e74705SXin Li static cl::opt<bool> OutputXML("output-replacements-xml",
88*67e74705SXin Li cl::desc("Output replacements as XML."),
89*67e74705SXin Li cl::cat(ClangFormatCategory));
90*67e74705SXin Li static cl::opt<bool>
91*67e74705SXin Li DumpConfig("dump-config",
92*67e74705SXin Li cl::desc("Dump configuration options to stdout and exit.\n"
93*67e74705SXin Li "Can be used with -style option."),
94*67e74705SXin Li cl::cat(ClangFormatCategory));
95*67e74705SXin Li static cl::opt<unsigned>
96*67e74705SXin Li Cursor("cursor",
97*67e74705SXin Li cl::desc("The position of the cursor when invoking\n"
98*67e74705SXin Li "clang-format from an editor integration"),
99*67e74705SXin Li cl::init(0), cl::cat(ClangFormatCategory));
100*67e74705SXin Li
101*67e74705SXin Li static cl::opt<bool> SortIncludes(
102*67e74705SXin Li "sort-includes",
103*67e74705SXin Li cl::desc("If set, overrides the include sorting behavior determined by the "
104*67e74705SXin Li "SortIncludes style flag"),
105*67e74705SXin Li cl::cat(ClangFormatCategory));
106*67e74705SXin Li
107*67e74705SXin Li static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
108*67e74705SXin Li cl::cat(ClangFormatCategory));
109*67e74705SXin Li
110*67e74705SXin Li namespace clang {
111*67e74705SXin Li namespace format {
112*67e74705SXin Li
createInMemoryFile(StringRef FileName,MemoryBuffer * Source,SourceManager & Sources,FileManager & Files,vfs::InMemoryFileSystem * MemFS)113*67e74705SXin Li static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source,
114*67e74705SXin Li SourceManager &Sources, FileManager &Files,
115*67e74705SXin Li vfs::InMemoryFileSystem *MemFS) {
116*67e74705SXin Li MemFS->addFileNoOwn(FileName, 0, Source);
117*67e74705SXin Li return Sources.createFileID(Files.getFile(FileName), SourceLocation(),
118*67e74705SXin Li SrcMgr::C_User);
119*67e74705SXin Li }
120*67e74705SXin Li
121*67e74705SXin Li // Parses <start line>:<end line> input to a pair of line numbers.
122*67e74705SXin Li // Returns true on error.
parseLineRange(StringRef Input,unsigned & FromLine,unsigned & ToLine)123*67e74705SXin Li static bool parseLineRange(StringRef Input, unsigned &FromLine,
124*67e74705SXin Li unsigned &ToLine) {
125*67e74705SXin Li std::pair<StringRef, StringRef> LineRange = Input.split(':');
126*67e74705SXin Li return LineRange.first.getAsInteger(0, FromLine) ||
127*67e74705SXin Li LineRange.second.getAsInteger(0, ToLine);
128*67e74705SXin Li }
129*67e74705SXin Li
fillRanges(MemoryBuffer * Code,std::vector<tooling::Range> & Ranges)130*67e74705SXin Li static bool fillRanges(MemoryBuffer *Code,
131*67e74705SXin Li std::vector<tooling::Range> &Ranges) {
132*67e74705SXin Li IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
133*67e74705SXin Li new vfs::InMemoryFileSystem);
134*67e74705SXin Li FileManager Files(FileSystemOptions(), InMemoryFileSystem);
135*67e74705SXin Li DiagnosticsEngine Diagnostics(
136*67e74705SXin Li IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
137*67e74705SXin Li new DiagnosticOptions);
138*67e74705SXin Li SourceManager Sources(Diagnostics, Files);
139*67e74705SXin Li FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files,
140*67e74705SXin Li InMemoryFileSystem.get());
141*67e74705SXin Li if (!LineRanges.empty()) {
142*67e74705SXin Li if (!Offsets.empty() || !Lengths.empty()) {
143*67e74705SXin Li errs() << "error: cannot use -lines with -offset/-length\n";
144*67e74705SXin Li return true;
145*67e74705SXin Li }
146*67e74705SXin Li
147*67e74705SXin Li for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
148*67e74705SXin Li unsigned FromLine, ToLine;
149*67e74705SXin Li if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
150*67e74705SXin Li errs() << "error: invalid <start line>:<end line> pair\n";
151*67e74705SXin Li return true;
152*67e74705SXin Li }
153*67e74705SXin Li if (FromLine > ToLine) {
154*67e74705SXin Li errs() << "error: start line should be less than end line\n";
155*67e74705SXin Li return true;
156*67e74705SXin Li }
157*67e74705SXin Li SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
158*67e74705SXin Li SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
159*67e74705SXin Li if (Start.isInvalid() || End.isInvalid())
160*67e74705SXin Li return true;
161*67e74705SXin Li unsigned Offset = Sources.getFileOffset(Start);
162*67e74705SXin Li unsigned Length = Sources.getFileOffset(End) - Offset;
163*67e74705SXin Li Ranges.push_back(tooling::Range(Offset, Length));
164*67e74705SXin Li }
165*67e74705SXin Li return false;
166*67e74705SXin Li }
167*67e74705SXin Li
168*67e74705SXin Li if (Offsets.empty())
169*67e74705SXin Li Offsets.push_back(0);
170*67e74705SXin Li if (Offsets.size() != Lengths.size() &&
171*67e74705SXin Li !(Offsets.size() == 1 && Lengths.empty())) {
172*67e74705SXin Li errs() << "error: number of -offset and -length arguments must match.\n";
173*67e74705SXin Li return true;
174*67e74705SXin Li }
175*67e74705SXin Li for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
176*67e74705SXin Li if (Offsets[i] >= Code->getBufferSize()) {
177*67e74705SXin Li errs() << "error: offset " << Offsets[i] << " is outside the file\n";
178*67e74705SXin Li return true;
179*67e74705SXin Li }
180*67e74705SXin Li SourceLocation Start =
181*67e74705SXin Li Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
182*67e74705SXin Li SourceLocation End;
183*67e74705SXin Li if (i < Lengths.size()) {
184*67e74705SXin Li if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
185*67e74705SXin Li errs() << "error: invalid length " << Lengths[i]
186*67e74705SXin Li << ", offset + length (" << Offsets[i] + Lengths[i]
187*67e74705SXin Li << ") is outside the file.\n";
188*67e74705SXin Li return true;
189*67e74705SXin Li }
190*67e74705SXin Li End = Start.getLocWithOffset(Lengths[i]);
191*67e74705SXin Li } else {
192*67e74705SXin Li End = Sources.getLocForEndOfFile(ID);
193*67e74705SXin Li }
194*67e74705SXin Li unsigned Offset = Sources.getFileOffset(Start);
195*67e74705SXin Li unsigned Length = Sources.getFileOffset(End) - Offset;
196*67e74705SXin Li Ranges.push_back(tooling::Range(Offset, Length));
197*67e74705SXin Li }
198*67e74705SXin Li return false;
199*67e74705SXin Li }
200*67e74705SXin Li
outputReplacementXML(StringRef Text)201*67e74705SXin Li static void outputReplacementXML(StringRef Text) {
202*67e74705SXin Li // FIXME: When we sort includes, we need to make sure the stream is correct
203*67e74705SXin Li // utf-8.
204*67e74705SXin Li size_t From = 0;
205*67e74705SXin Li size_t Index;
206*67e74705SXin Li while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
207*67e74705SXin Li outs() << Text.substr(From, Index - From);
208*67e74705SXin Li switch (Text[Index]) {
209*67e74705SXin Li case '\n':
210*67e74705SXin Li outs() << " ";
211*67e74705SXin Li break;
212*67e74705SXin Li case '\r':
213*67e74705SXin Li outs() << " ";
214*67e74705SXin Li break;
215*67e74705SXin Li case '<':
216*67e74705SXin Li outs() << "<";
217*67e74705SXin Li break;
218*67e74705SXin Li case '&':
219*67e74705SXin Li outs() << "&";
220*67e74705SXin Li break;
221*67e74705SXin Li default:
222*67e74705SXin Li llvm_unreachable("Unexpected character encountered!");
223*67e74705SXin Li }
224*67e74705SXin Li From = Index + 1;
225*67e74705SXin Li }
226*67e74705SXin Li outs() << Text.substr(From);
227*67e74705SXin Li }
228*67e74705SXin Li
outputReplacementsXML(const Replacements & Replaces)229*67e74705SXin Li static void outputReplacementsXML(const Replacements &Replaces) {
230*67e74705SXin Li for (const auto &R : Replaces) {
231*67e74705SXin Li outs() << "<replacement "
232*67e74705SXin Li << "offset='" << R.getOffset() << "' "
233*67e74705SXin Li << "length='" << R.getLength() << "'>";
234*67e74705SXin Li outputReplacementXML(R.getReplacementText());
235*67e74705SXin Li outs() << "</replacement>\n";
236*67e74705SXin Li }
237*67e74705SXin Li }
238*67e74705SXin Li
239*67e74705SXin Li // Returns true on error.
format(StringRef FileName)240*67e74705SXin Li static bool format(StringRef FileName) {
241*67e74705SXin Li ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
242*67e74705SXin Li MemoryBuffer::getFileOrSTDIN(FileName);
243*67e74705SXin Li if (std::error_code EC = CodeOrErr.getError()) {
244*67e74705SXin Li errs() << EC.message() << "\n";
245*67e74705SXin Li return true;
246*67e74705SXin Li }
247*67e74705SXin Li std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
248*67e74705SXin Li if (Code->getBufferSize() == 0)
249*67e74705SXin Li return false; // Empty files are formatted correctly.
250*67e74705SXin Li std::vector<tooling::Range> Ranges;
251*67e74705SXin Li if (fillRanges(Code.get(), Ranges))
252*67e74705SXin Li return true;
253*67e74705SXin Li StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
254*67e74705SXin Li FormatStyle FormatStyle = getStyle(Style, AssumedFileName, FallbackStyle);
255*67e74705SXin Li if (SortIncludes.getNumOccurrences() != 0)
256*67e74705SXin Li FormatStyle.SortIncludes = SortIncludes;
257*67e74705SXin Li unsigned CursorPosition = Cursor;
258*67e74705SXin Li Replacements Replaces = sortIncludes(FormatStyle, Code->getBuffer(), Ranges,
259*67e74705SXin Li AssumedFileName, &CursorPosition);
260*67e74705SXin Li auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
261*67e74705SXin Li if (!ChangedCode) {
262*67e74705SXin Li llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
263*67e74705SXin Li return true;
264*67e74705SXin Li }
265*67e74705SXin Li for (const auto &R : Replaces)
266*67e74705SXin Li Ranges.push_back({R.getOffset(), R.getLength()});
267*67e74705SXin Li
268*67e74705SXin Li bool IncompleteFormat = false;
269*67e74705SXin Li Replacements FormatChanges = reformat(FormatStyle, *ChangedCode, Ranges,
270*67e74705SXin Li AssumedFileName, &IncompleteFormat);
271*67e74705SXin Li Replaces = tooling::mergeReplacements(Replaces, FormatChanges);
272*67e74705SXin Li if (OutputXML) {
273*67e74705SXin Li outs() << "<?xml version='1.0'?>\n<replacements "
274*67e74705SXin Li "xml:space='preserve' incomplete_format='"
275*67e74705SXin Li << (IncompleteFormat ? "true" : "false") << "'>\n";
276*67e74705SXin Li if (Cursor.getNumOccurrences() != 0)
277*67e74705SXin Li outs() << "<cursor>"
278*67e74705SXin Li << tooling::shiftedCodePosition(FormatChanges, CursorPosition)
279*67e74705SXin Li << "</cursor>\n";
280*67e74705SXin Li
281*67e74705SXin Li outputReplacementsXML(Replaces);
282*67e74705SXin Li outs() << "</replacements>\n";
283*67e74705SXin Li } else {
284*67e74705SXin Li IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
285*67e74705SXin Li new vfs::InMemoryFileSystem);
286*67e74705SXin Li FileManager Files(FileSystemOptions(), InMemoryFileSystem);
287*67e74705SXin Li DiagnosticsEngine Diagnostics(
288*67e74705SXin Li IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
289*67e74705SXin Li new DiagnosticOptions);
290*67e74705SXin Li SourceManager Sources(Diagnostics, Files);
291*67e74705SXin Li FileID ID = createInMemoryFile(AssumedFileName, Code.get(), Sources, Files,
292*67e74705SXin Li InMemoryFileSystem.get());
293*67e74705SXin Li Rewriter Rewrite(Sources, LangOptions());
294*67e74705SXin Li tooling::applyAllReplacements(Replaces, Rewrite);
295*67e74705SXin Li if (Inplace) {
296*67e74705SXin Li if (FileName == "-")
297*67e74705SXin Li errs() << "error: cannot use -i when reading from stdin.\n";
298*67e74705SXin Li else if (Rewrite.overwriteChangedFiles())
299*67e74705SXin Li return true;
300*67e74705SXin Li } else {
301*67e74705SXin Li if (Cursor.getNumOccurrences() != 0)
302*67e74705SXin Li outs() << "{ \"Cursor\": "
303*67e74705SXin Li << tooling::shiftedCodePosition(FormatChanges, CursorPosition)
304*67e74705SXin Li << ", \"IncompleteFormat\": "
305*67e74705SXin Li << (IncompleteFormat ? "true" : "false") << " }\n";
306*67e74705SXin Li Rewrite.getEditBuffer(ID).write(outs());
307*67e74705SXin Li }
308*67e74705SXin Li }
309*67e74705SXin Li return false;
310*67e74705SXin Li }
311*67e74705SXin Li
312*67e74705SXin Li } // namespace format
313*67e74705SXin Li } // namespace clang
314*67e74705SXin Li
PrintVersion()315*67e74705SXin Li static void PrintVersion() {
316*67e74705SXin Li raw_ostream &OS = outs();
317*67e74705SXin Li OS << clang::getClangToolFullVersion("clang-format") << '\n';
318*67e74705SXin Li }
319*67e74705SXin Li
main(int argc,const char ** argv)320*67e74705SXin Li int main(int argc, const char **argv) {
321*67e74705SXin Li llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
322*67e74705SXin Li
323*67e74705SXin Li cl::HideUnrelatedOptions(ClangFormatCategory);
324*67e74705SXin Li
325*67e74705SXin Li cl::SetVersionPrinter(PrintVersion);
326*67e74705SXin Li cl::ParseCommandLineOptions(
327*67e74705SXin Li argc, argv,
328*67e74705SXin Li "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf code.\n\n"
329*67e74705SXin Li "If no arguments are specified, it formats the code from standard input\n"
330*67e74705SXin Li "and writes the result to the standard output.\n"
331*67e74705SXin Li "If <file>s are given, it reformats the files. If -i is specified\n"
332*67e74705SXin Li "together with <file>s, the files are edited in-place. Otherwise, the\n"
333*67e74705SXin Li "result is written to the standard output.\n");
334*67e74705SXin Li
335*67e74705SXin Li if (Help)
336*67e74705SXin Li cl::PrintHelpMessage();
337*67e74705SXin Li
338*67e74705SXin Li if (DumpConfig) {
339*67e74705SXin Li std::string Config =
340*67e74705SXin Li clang::format::configurationAsText(clang::format::getStyle(
341*67e74705SXin Li Style, FileNames.empty() ? AssumeFileName : FileNames[0],
342*67e74705SXin Li FallbackStyle));
343*67e74705SXin Li outs() << Config << "\n";
344*67e74705SXin Li return 0;
345*67e74705SXin Li }
346*67e74705SXin Li
347*67e74705SXin Li bool Error = false;
348*67e74705SXin Li switch (FileNames.size()) {
349*67e74705SXin Li case 0:
350*67e74705SXin Li Error = clang::format::format("-");
351*67e74705SXin Li break;
352*67e74705SXin Li case 1:
353*67e74705SXin Li Error = clang::format::format(FileNames[0]);
354*67e74705SXin Li break;
355*67e74705SXin Li default:
356*67e74705SXin Li if (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty()) {
357*67e74705SXin Li errs() << "error: -offset, -length and -lines can only be used for "
358*67e74705SXin Li "single file.\n";
359*67e74705SXin Li return 1;
360*67e74705SXin Li }
361*67e74705SXin Li for (unsigned i = 0; i < FileNames.size(); ++i)
362*67e74705SXin Li Error |= clang::format::format(FileNames[i]);
363*67e74705SXin Li break;
364*67e74705SXin Li }
365*67e74705SXin Li return Error ? 1 : 0;
366*67e74705SXin Li }
367*67e74705SXin Li
368