ClangFormat.cpp revision 4e65c98f67b80a7f85ecd5b550a5e10a834f702c
1//===-- 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/Debug.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/Signals.h"
26#include "llvm/ADT/StringMap.h"
27
28using namespace llvm;
29
30// Fallback style when no style specified or found in a .clang-format file.
31static const char FallbackStyle[] = "LLVM";
32
33static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
34
35// Mark all our options with this category, everything else (except for -version
36// and -help) will be hidden.
37cl::OptionCategory ClangFormatCategory("Clang-format options");
38
39static cl::list<unsigned>
40    Offsets("offset",
41            cl::desc("Format a range starting at this byte offset.\n"
42                     "Multiple ranges can be formatted by specifying\n"
43                     "several -offset and -length pairs.\n"
44                     "Can only be used with one input file."),
45            cl::cat(ClangFormatCategory));
46static cl::list<unsigned>
47    Lengths("length",
48            cl::desc("Format a range of this length (in bytes).\n"
49                     "Multiple ranges can be formatted by specifying\n"
50                     "several -offset and -length pairs.\n"
51                     "When only a single -offset is specified without\n"
52                     "-length, clang-format will format up to the end\n"
53                     "of the file.\n"
54                     "Can only be used with one input file."),
55            cl::cat(ClangFormatCategory));
56static cl::list<std::string>
57LineRanges("lines", cl::desc("<start line>:<end line> - format a range of\n"
58                             "lines (both 1-based).\n"
59                             "Multiple ranges can be formatted by specifying\n"
60                             "several -lines arguments.\n"
61                             "Can't be used with -offset and -length.\n"
62                             "Can only be used with one input file."),
63           cl::cat(ClangFormatCategory));
64static cl::opt<std::string>
65    Style("style",
66          cl::desc("Coding style, currently supports:\n"
67                   "  LLVM, Google, Chromium, Mozilla, WebKit.\n"
68                   "Use -style=file to load style configuration from\n"
69                   ".clang-format file located in one of the parent\n"
70                   "directories of the source file (or current\n"
71                   "directory for stdin).\n"
72                   "Use -style=\"{key: value, ...}\" to set specific\n"
73                   "parameters, e.g.:\n"
74                   "  -style=\"{BasedOnStyle: llvm, IndentWidth: 8}\""),
75          cl::init("file"), cl::cat(ClangFormatCategory));
76static cl::opt<bool> Inplace("i",
77                             cl::desc("Inplace edit <file>s, if specified."),
78                             cl::cat(ClangFormatCategory));
79
80static cl::opt<bool> OutputXML("output-replacements-xml",
81                               cl::desc("Output replacements as XML."),
82                               cl::cat(ClangFormatCategory));
83static cl::opt<bool>
84    DumpConfig("dump-config",
85               cl::desc("Dump configuration options to stdout and exit.\n"
86                        "Can be used with -style option."),
87               cl::cat(ClangFormatCategory));
88static cl::opt<unsigned>
89    Cursor("cursor",
90           cl::desc("The position of the cursor when invoking\n"
91                    "clang-format from an editor integration"),
92           cl::init(0), cl::cat(ClangFormatCategory));
93
94static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
95                                       cl::cat(ClangFormatCategory));
96
97namespace clang {
98namespace format {
99
100static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source,
101                                 SourceManager &Sources, FileManager &Files) {
102  const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" :
103                                                    FileName,
104                                                Source->getBufferSize(), 0);
105  Sources.overrideFileContents(Entry, Source, true);
106  return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
107}
108
109FormatStyle getStyle(StringRef StyleName, StringRef FileName) {
110  FormatStyle Style;
111  getPredefinedStyle(FallbackStyle, &Style);
112
113  if (StyleName.startswith("{")) {
114    // Parse YAML/JSON style from the command line.
115    if (error_code ec = parseConfiguration(StyleName, &Style)) {
116      llvm::errs() << "Error parsing -style: " << ec.message()
117                   << ", using " << FallbackStyle << " style\n";
118    }
119    return Style;
120  }
121
122  if (!StyleName.equals_lower("file")) {
123    if (!getPredefinedStyle(StyleName, &Style))
124      llvm::errs() << "Invalid value for -style, using " << FallbackStyle
125                   << " style\n";
126    return Style;
127  }
128
129  SmallString<128> Path(FileName);
130  llvm::sys::fs::make_absolute(Path);
131  for (StringRef Directory = llvm::sys::path::parent_path(Path);
132       !Directory.empty();
133       Directory = llvm::sys::path::parent_path(Directory)) {
134    SmallString<128> ConfigFile(Directory);
135    llvm::sys::path::append(ConfigFile, ".clang-format");
136    DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
137    bool IsFile = false;
138    // Ignore errors from is_regular_file: we only need to know if we can read
139    // the file or not.
140    llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
141    if (IsFile) {
142      OwningPtr<MemoryBuffer> Text;
143      if (error_code ec = MemoryBuffer::getFile(ConfigFile, Text)) {
144        llvm::errs() << ec.message() << "\n";
145        continue;
146      }
147      if (error_code ec = parseConfiguration(Text->getBuffer(), &Style)) {
148        llvm::errs() << "Error reading " << ConfigFile << ": " << ec.message()
149                     << "\n";
150        continue;
151      }
152      DEBUG(llvm::dbgs() << "Using configuration file " << ConfigFile << "\n");
153      return Style;
154    }
155  }
156  llvm::errs() << "Can't find usable .clang-format, using " << FallbackStyle
157               << " style\n";
158  return Style;
159}
160
161// Parses <start line>:<end line> input to a pair of line numbers.
162// Returns true on error.
163static bool parseLineRange(StringRef Input, unsigned &FromLine,
164                           unsigned &ToLine) {
165  std::pair<StringRef, StringRef> LineRange = Input.split(':');
166  return LineRange.first.getAsInteger(0, FromLine) ||
167         LineRange.second.getAsInteger(0, ToLine);
168}
169
170static bool fillRanges(SourceManager &Sources, FileID ID,
171                       const MemoryBuffer *Code,
172                       std::vector<CharSourceRange> &Ranges) {
173  if (!LineRanges.empty()) {
174    if (!Offsets.empty() || !Lengths.empty()) {
175      llvm::errs() << "error: cannot use -lines with -offset/-length\n";
176      return true;
177    }
178
179    for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
180      unsigned FromLine, ToLine;
181      if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
182        llvm::errs() << "error: invalid <start line>:<end line> pair\n";
183        return true;
184      }
185      if (FromLine > ToLine) {
186        llvm::errs() << "error: start line should be less than end line\n";
187        return true;
188      }
189      SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
190      SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
191      if (Start.isInvalid() || End.isInvalid())
192        return true;
193      Ranges.push_back(CharSourceRange::getCharRange(Start, End));
194    }
195    return false;
196  }
197
198  if (Offsets.empty())
199    Offsets.push_back(0);
200  if (Offsets.size() != Lengths.size() &&
201      !(Offsets.size() == 1 && Lengths.empty())) {
202    llvm::errs()
203        << "error: number of -offset and -length arguments must match.\n";
204    return true;
205  }
206  for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
207    if (Offsets[i] >= Code->getBufferSize()) {
208      llvm::errs() << "error: offset " << Offsets[i]
209                   << " is outside the file\n";
210      return true;
211    }
212    SourceLocation Start =
213        Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
214    SourceLocation End;
215    if (i < Lengths.size()) {
216      if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
217        llvm::errs() << "error: invalid length " << Lengths[i]
218                     << ", offset + length (" << Offsets[i] + Lengths[i]
219                     << ") is outside the file.\n";
220        return true;
221      }
222      End = Start.getLocWithOffset(Lengths[i]);
223    } else {
224      End = Sources.getLocForEndOfFile(ID);
225    }
226    Ranges.push_back(CharSourceRange::getCharRange(Start, End));
227  }
228  return false;
229}
230
231// Returns true on error.
232static bool format(std::string FileName) {
233  FileManager Files((FileSystemOptions()));
234  DiagnosticsEngine Diagnostics(
235      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
236      new DiagnosticOptions);
237  SourceManager Sources(Diagnostics, Files);
238  OwningPtr<MemoryBuffer> Code;
239  if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) {
240    llvm::errs() << ec.message() << "\n";
241    return true;
242  }
243  if (Code->getBufferSize() == 0)
244    return true; // Empty files are formatted correctly.
245  FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files);
246  std::vector<CharSourceRange> Ranges;
247  if (fillRanges(Sources, ID, Code.get(), Ranges))
248    return true;
249
250  FormatStyle FormatStyle = getStyle(Style, FileName);
251  Lexer Lex(ID, Sources.getBuffer(ID), Sources,
252            getFormattingLangOpts(FormatStyle.Standard));
253  tooling::Replacements Replaces = reformat(FormatStyle, Lex, Sources, Ranges);
254  if (OutputXML) {
255    llvm::outs()
256        << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n";
257    for (tooling::Replacements::const_iterator I = Replaces.begin(),
258                                               E = Replaces.end();
259         I != E; ++I) {
260      llvm::outs() << "<replacement "
261                   << "offset='" << I->getOffset() << "' "
262                   << "length='" << I->getLength() << "'>"
263                   << I->getReplacementText() << "</replacement>\n";
264    }
265    llvm::outs() << "</replacements>\n";
266  } else {
267    Rewriter Rewrite(Sources, LangOptions());
268    tooling::applyAllReplacements(Replaces, Rewrite);
269    if (Inplace) {
270      if (Replaces.size() == 0)
271        return false; // Nothing changed, don't touch the file.
272
273      std::string ErrorInfo;
274      llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo,
275                                      llvm::sys::fs::F_Binary);
276      if (!ErrorInfo.empty()) {
277        llvm::errs() << "Error while writing file: " << ErrorInfo << "\n";
278        return true;
279      }
280      Rewrite.getEditBuffer(ID).write(FileStream);
281      FileStream.flush();
282    } else {
283      if (Cursor.getNumOccurrences() != 0)
284        outs() << "{ \"Cursor\": " << tooling::shiftedCodePosition(
285                                          Replaces, Cursor) << " }\n";
286      Rewrite.getEditBuffer(ID).write(outs());
287    }
288  }
289  return false;
290}
291
292}  // namespace format
293}  // namespace clang
294
295int main(int argc, const char **argv) {
296  llvm::sys::PrintStackTraceOnErrorSignal();
297
298  // Hide unrelated options.
299  StringMap<cl::Option*> Options;
300  cl::getRegisteredOptions(Options);
301  for (StringMap<cl::Option *>::iterator I = Options.begin(), E = Options.end();
302       I != E; ++I) {
303    if (I->second->Category != &ClangFormatCategory && I->first() != "help" &&
304        I->first() != "version")
305      I->second->setHiddenFlag(cl::ReallyHidden);
306  }
307
308  cl::ParseCommandLineOptions(
309      argc, argv,
310      "A tool to format C/C++/Obj-C code.\n\n"
311      "If no arguments are specified, it formats the code from standard input\n"
312      "and writes the result to the standard output.\n"
313      "If <file>s are given, it reformats the files. If -i is specified\n"
314      "together with <file>s, the files are edited in-place. Otherwise, the\n"
315      "result is written to the standard output.\n");
316
317  if (Help)
318    cl::PrintHelpMessage();
319
320  if (DumpConfig) {
321    std::string Config = clang::format::configurationAsText(
322        clang::format::getStyle(Style, FileNames.empty() ? "-" : FileNames[0]));
323    llvm::outs() << Config << "\n";
324    return 0;
325  }
326
327  bool Error = false;
328  switch (FileNames.size()) {
329  case 0:
330    Error = clang::format::format("-");
331    break;
332  case 1:
333    Error = clang::format::format(FileNames[0]);
334    break;
335  default:
336    if (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty()) {
337      llvm::errs() << "error: -offset, -length and -lines can only be used for "
338                      "single file.\n";
339      return 1;
340    }
341    for (unsigned i = 0; i < FileNames.size(); ++i)
342      Error |= clang::format::format(FileNames[i]);
343    break;
344  }
345  return Error ? 1 : 0;
346}
347