blob: 57833edf0f3195a48323ae77d5610fa50810a651 [file] [log] [blame]
djasper7f663602013-03-20 09:53:23 +00001//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This file implements a clang-format tool that automatically formats
12/// (fragments of) C++ code.
13///
14//===----------------------------------------------------------------------===//
15
16#include "clang/Basic/Diagnostic.h"
17#include "clang/Basic/DiagnosticOptions.h"
18#include "clang/Basic/FileManager.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Format/Format.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/Rewrite/Core/Rewriter.h"
23#include "llvm/Support/FileSystem.h"
24#include "llvm/Support/Signals.h"
25
26using namespace llvm;
27
28static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
29
alexfh4df43642013-04-24 12:46:44 +000030static cl::list<unsigned>
31Offsets("offset", cl::desc("Format a range starting at this file offset. Can "
32 "only be used with one input file."));
33static cl::list<unsigned>
34Lengths("length", cl::desc("Format a range of this length. "
35 "When it's not specified, end of file is used. "
36 "Can only be used with one input file."));
djasper7f663602013-03-20 09:53:23 +000037static cl::opt<std::string> Style(
38 "style",
alexfh2d776172013-05-06 14:11:27 +000039 cl::desc("Coding style, currently supports: LLVM, Google, Chromium, Mozilla."),
djasper7f663602013-03-20 09:53:23 +000040 cl::init("LLVM"));
41static cl::opt<bool> Inplace("i",
alexfh4df43642013-04-24 12:46:44 +000042 cl::desc("Inplace edit <file>s, if specified."));
djasper7f663602013-03-20 09:53:23 +000043
44static cl::opt<bool> OutputXML(
45 "output-replacements-xml", cl::desc("Output replacements as XML."));
46
alexfh4df43642013-04-24 12:46:44 +000047static cl::list<std::string> FileNames(cl::Positional,
48 cl::desc("[<file> ...]"));
djasper7f663602013-03-20 09:53:23 +000049
50namespace clang {
51namespace format {
52
53static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source,
54 SourceManager &Sources, FileManager &Files) {
55 const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" :
56 FileName,
57 Source->getBufferSize(), 0);
58 Sources.overrideFileContents(Entry, Source, true);
59 return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
60}
61
62static FormatStyle getStyle() {
63 FormatStyle TheStyle = getGoogleStyle();
64 if (Style == "LLVM")
65 TheStyle = getLLVMStyle();
alexfh2d776172013-05-06 14:11:27 +000066 else if (Style == "Chromium")
djasper7f663602013-03-20 09:53:23 +000067 TheStyle = getChromiumStyle();
alexfh2d776172013-05-06 14:11:27 +000068 else if (Style == "Mozilla")
69 TheStyle = getMozillaStyle();
70 else if (Style != "Google")
71 llvm::errs() << "Unknown style " << Style << ", using Google style.\n";
72
djasper7f663602013-03-20 09:53:23 +000073 return TheStyle;
74}
75
alexfh4df43642013-04-24 12:46:44 +000076// Returns true on error.
77static bool format(std::string FileName) {
djasper7f663602013-03-20 09:53:23 +000078 FileManager Files((FileSystemOptions()));
79 DiagnosticsEngine Diagnostics(
80 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
81 new DiagnosticOptions);
82 SourceManager Sources(Diagnostics, Files);
83 OwningPtr<MemoryBuffer> Code;
84 if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) {
85 llvm::errs() << ec.message() << "\n";
alexfh4df43642013-04-24 12:46:44 +000086 return true;
djasper7f663602013-03-20 09:53:23 +000087 }
88 FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files);
89 Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts());
90 if (Offsets.empty())
91 Offsets.push_back(0);
92 if (Offsets.size() != Lengths.size() &&
93 !(Offsets.size() == 1 && Lengths.empty())) {
alexfh4df43642013-04-24 12:46:44 +000094 llvm::errs()
95 << "error: number of -offset and -length arguments must match.\n";
96 return true;
djasper7f663602013-03-20 09:53:23 +000097 }
98 std::vector<CharSourceRange> Ranges;
alexfh4df43642013-04-24 12:46:44 +000099 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
100 if (Offsets[i] >= Code->getBufferSize()) {
101 llvm::errs() << "error: offset " << Offsets[i]
102 << " is outside the file\n";
103 return true;
104 }
djasper7f663602013-03-20 09:53:23 +0000105 SourceLocation Start =
106 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
107 SourceLocation End;
108 if (i < Lengths.size()) {
alexfh4df43642013-04-24 12:46:44 +0000109 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
110 llvm::errs() << "error: invalid length " << Lengths[i]
111 << ", offset + length (" << Offsets[i] + Lengths[i]
112 << ") is outside the file.\n";
113 return true;
114 }
djasper7f663602013-03-20 09:53:23 +0000115 End = Start.getLocWithOffset(Lengths[i]);
116 } else {
117 End = Sources.getLocForEndOfFile(ID);
118 }
119 Ranges.push_back(CharSourceRange::getCharRange(Start, End));
120 }
121 tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges);
122 if (OutputXML) {
alexfh4df43642013-04-24 12:46:44 +0000123 llvm::outs()
124 << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n";
djasper7f663602013-03-20 09:53:23 +0000125 for (tooling::Replacements::const_iterator I = Replaces.begin(),
126 E = Replaces.end();
127 I != E; ++I) {
128 llvm::outs() << "<replacement "
129 << "offset='" << I->getOffset() << "' "
130 << "length='" << I->getLength() << "'>"
131 << I->getReplacementText() << "</replacement>\n";
132 }
133 llvm::outs() << "</replacements>\n";
134 } else {
135 Rewriter Rewrite(Sources, LangOptions());
136 tooling::applyAllReplacements(Replaces, Rewrite);
137 if (Inplace) {
138 if (Replaces.size() == 0)
alexfh4df43642013-04-24 12:46:44 +0000139 return false; // Nothing changed, don't touch the file.
djasper7f663602013-03-20 09:53:23 +0000140
141 std::string ErrorInfo;
142 llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo,
143 llvm::raw_fd_ostream::F_Binary);
144 if (!ErrorInfo.empty()) {
145 llvm::errs() << "Error while writing file: " << ErrorInfo << "\n";
alexfh4df43642013-04-24 12:46:44 +0000146 return true;
djasper7f663602013-03-20 09:53:23 +0000147 }
148 Rewrite.getEditBuffer(ID).write(FileStream);
149 FileStream.flush();
150 } else {
151 Rewrite.getEditBuffer(ID).write(outs());
152 }
153 }
alexfh4df43642013-04-24 12:46:44 +0000154 return false;
djasper7f663602013-03-20 09:53:23 +0000155}
156
157} // namespace format
158} // namespace clang
159
160int main(int argc, const char **argv) {
161 llvm::sys::PrintStackTraceOnErrorSignal();
162 cl::ParseCommandLineOptions(
163 argc, argv,
164 "A tool to format C/C++/Obj-C code.\n\n"
djasper7f663602013-03-20 09:53:23 +0000165 "If no arguments are specified, it formats the code from standard input\n"
166 "and writes the result to the standard output.\n"
alexfh4df43642013-04-24 12:46:44 +0000167 "If <file>s are given, it reformats the files. If -i is specified \n"
168 "together with <file>s, the files are edited in-place. Otherwise, the \n"
169 "result is written to the standard output.\n");
170
djasper7f663602013-03-20 09:53:23 +0000171 if (Help)
172 cl::PrintHelpMessage();
alexfh4df43642013-04-24 12:46:44 +0000173
174 bool Error = false;
175 switch (FileNames.size()) {
176 case 0:
177 Error = clang::format::format("-");
178 break;
179 case 1:
180 Error = clang::format::format(FileNames[0]);
181 break;
182 default:
183 if (!Offsets.empty() || !Lengths.empty()) {
184 llvm::errs() << "error: \"-offset\" and \"-length\" can only be used for "
185 "single file.\n";
186 return 1;
187 }
188 for (unsigned i = 0; i < FileNames.size(); ++i)
189 Error |= clang::format::format(FileNames[i]);
190 break;
191 }
192 return Error ? 1 : 0;
djasper7f663602013-03-20 09:53:23 +0000193}