blob: ea70dafe03984f06b91cc013ca9db4cffa0dae3e [file] [log] [blame]
Daniel Jasper7c4a9a02013-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"
Alexander Kornienkodd256312013-05-10 11:56:10 +000023#include "llvm/Support/Debug.h"
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000024#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/Signals.h"
26
27using namespace llvm;
28
29static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
30
Alexander Kornienkodbaa5b02013-05-10 13:18:17 +000031static cl::list<unsigned> Offsets(
32 "offset",
33 cl::desc(
34 "Format a range starting at this byte offset. Multiple ranges can be "
35 "formatted by specifying several -offset and -length pairs. Can "
36 "only be used with one input file."));
37static cl::list<unsigned> Lengths(
38 "length",
39 cl::desc("Format a range of this length (in bytes). Multiple ranges can be "
40 "formatted by specifying several -offset and -length pairs. When "
41 "only a single -offset is specified without -length, clang-format "
42 "will format up to the end of the file. Can only be used with one "
43 "input file."));
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000044static cl::opt<std::string> Style(
45 "style",
Alexander Kornienkodd256312013-05-10 11:56:10 +000046 cl::desc(
47 "Coding style, currently supports: LLVM, Google, Chromium, Mozilla. "
48 "Use '-style file' to load style configuration from .clang-format file "
49 "located in one of the parent directories of the source file (or "
50 "current directory for stdin)."),
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000051 cl::init("LLVM"));
52static cl::opt<bool> Inplace("i",
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +000053 cl::desc("Inplace edit <file>s, if specified."));
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000054
55static cl::opt<bool> OutputXML(
56 "output-replacements-xml", cl::desc("Output replacements as XML."));
Alexander Kornienkodd256312013-05-10 11:56:10 +000057static cl::opt<bool>
58 DumpConfig("dump-config",
59 cl::desc("Dump configuration options to stdout and exit. Can be used with -style option."));
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000060
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +000061static cl::list<std::string> FileNames(cl::Positional,
62 cl::desc("[<file> ...]"));
Daniel Jasper7c4a9a02013-03-20 09:53:23 +000063
64namespace clang {
65namespace format {
66
67static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source,
68 SourceManager &Sources, FileManager &Files) {
69 const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" :
70 FileName,
71 Source->getBufferSize(), 0);
72 Sources.overrideFileContents(Entry, Source, true);
73 return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
74}
75
Alexander Kornienkodd256312013-05-10 11:56:10 +000076FormatStyle getStyle(StringRef StyleName, StringRef FileName) {
77 if (!StyleName.equals_lower("file"))
78 return getPredefinedStyle(StyleName);
Alexander Kornienkofb594862013-05-06 14:11:27 +000079
Alexander Kornienkodd256312013-05-10 11:56:10 +000080 SmallString<128> Path(FileName);
81 llvm::sys::fs::make_absolute(Path);
82 for (StringRef Directory = llvm::sys::path::parent_path(Path);
83 !Directory.empty();
84 Directory = llvm::sys::path::parent_path(Directory)) {
85 SmallString<128> ConfigFile(Directory);
86 llvm::sys::path::append(ConfigFile, ".clang-format");
87 DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
88 bool IsFile = false;
Alexander Kornienko0ca318b2013-05-10 13:04:20 +000089 // Ignore errors from is_regular_file: we only need to know if we can read
90 // the file or not.
Alexander Kornienkodd256312013-05-10 11:56:10 +000091 llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
92 if (IsFile) {
93 OwningPtr<MemoryBuffer> Text;
94 if (error_code ec = MemoryBuffer::getFile(ConfigFile, Text)) {
95 llvm::errs() << ec.message() << "\n";
96 continue;
97 }
98 FormatStyle Style;
99 if (error_code ec = parseConfiguration(Text->getBuffer(), &Style)) {
100 llvm::errs() << "Error reading " << ConfigFile << ": " << ec.message()
101 << "\n";
102 continue;
103 }
104 DEBUG(llvm::dbgs() << "Using configuration file " << ConfigFile << "\n");
105 return Style;
106 }
107 }
108 llvm::errs() << "Can't find usable .clang-format, using LLVM style\n";
109 return getLLVMStyle();
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000110}
111
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000112// Returns true on error.
113static bool format(std::string FileName) {
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000114 FileManager Files((FileSystemOptions()));
115 DiagnosticsEngine Diagnostics(
116 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
117 new DiagnosticOptions);
118 SourceManager Sources(Diagnostics, Files);
119 OwningPtr<MemoryBuffer> Code;
120 if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) {
121 llvm::errs() << ec.message() << "\n";
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000122 return true;
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000123 }
124 FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files);
125 Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts());
126 if (Offsets.empty())
127 Offsets.push_back(0);
128 if (Offsets.size() != Lengths.size() &&
129 !(Offsets.size() == 1 && Lengths.empty())) {
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000130 llvm::errs()
131 << "error: number of -offset and -length arguments must match.\n";
132 return true;
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000133 }
134 std::vector<CharSourceRange> Ranges;
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000135 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
136 if (Offsets[i] >= Code->getBufferSize()) {
137 llvm::errs() << "error: offset " << Offsets[i]
138 << " is outside the file\n";
139 return true;
140 }
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000141 SourceLocation Start =
142 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
143 SourceLocation End;
144 if (i < Lengths.size()) {
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000145 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
146 llvm::errs() << "error: invalid length " << Lengths[i]
147 << ", offset + length (" << Offsets[i] + Lengths[i]
148 << ") is outside the file.\n";
149 return true;
150 }
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000151 End = Start.getLocWithOffset(Lengths[i]);
152 } else {
153 End = Sources.getLocForEndOfFile(ID);
154 }
155 Ranges.push_back(CharSourceRange::getCharRange(Start, End));
156 }
Alexander Kornienkodd256312013-05-10 11:56:10 +0000157 tooling::Replacements Replaces =
158 reformat(getStyle(Style, FileName), Lex, Sources, Ranges);
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000159 if (OutputXML) {
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000160 llvm::outs()
161 << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n";
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000162 for (tooling::Replacements::const_iterator I = Replaces.begin(),
163 E = Replaces.end();
164 I != E; ++I) {
165 llvm::outs() << "<replacement "
166 << "offset='" << I->getOffset() << "' "
167 << "length='" << I->getLength() << "'>"
168 << I->getReplacementText() << "</replacement>\n";
169 }
170 llvm::outs() << "</replacements>\n";
171 } else {
172 Rewriter Rewrite(Sources, LangOptions());
173 tooling::applyAllReplacements(Replaces, Rewrite);
174 if (Inplace) {
175 if (Replaces.size() == 0)
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000176 return false; // Nothing changed, don't touch the file.
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000177
178 std::string ErrorInfo;
179 llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo,
180 llvm::raw_fd_ostream::F_Binary);
181 if (!ErrorInfo.empty()) {
182 llvm::errs() << "Error while writing file: " << ErrorInfo << "\n";
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000183 return true;
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000184 }
185 Rewrite.getEditBuffer(ID).write(FileStream);
186 FileStream.flush();
187 } else {
188 Rewrite.getEditBuffer(ID).write(outs());
189 }
190 }
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000191 return false;
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000192}
193
194} // namespace format
195} // namespace clang
196
197int main(int argc, const char **argv) {
198 llvm::sys::PrintStackTraceOnErrorSignal();
199 cl::ParseCommandLineOptions(
200 argc, argv,
201 "A tool to format C/C++/Obj-C code.\n\n"
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000202 "If no arguments are specified, it formats the code from standard input\n"
203 "and writes the result to the standard output.\n"
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000204 "If <file>s are given, it reformats the files. If -i is specified \n"
205 "together with <file>s, the files are edited in-place. Otherwise, the \n"
206 "result is written to the standard output.\n");
207
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000208 if (Help)
209 cl::PrintHelpMessage();
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000210
Alexander Kornienkodd256312013-05-10 11:56:10 +0000211 if (DumpConfig) {
212 std::string Config = clang::format::configurationAsText(
213 clang::format::getStyle(Style, FileNames.empty() ? "-" : FileNames[0]));
214 llvm::outs() << Config << "\n";
215 return 0;
216 }
217
Alexander Kornienko4cdc0cd2013-04-24 12:46:44 +0000218 bool Error = false;
219 switch (FileNames.size()) {
220 case 0:
221 Error = clang::format::format("-");
222 break;
223 case 1:
224 Error = clang::format::format(FileNames[0]);
225 break;
226 default:
227 if (!Offsets.empty() || !Lengths.empty()) {
228 llvm::errs() << "error: \"-offset\" and \"-length\" can only be used for "
229 "single file.\n";
230 return 1;
231 }
232 for (unsigned i = 0; i < FileNames.size(); ++i)
233 Error |= clang::format::format(FileNames[i]);
234 break;
235 }
236 return Error ? 1 : 0;
Daniel Jasper7c4a9a02013-03-20 09:53:23 +0000237}