blob: c4969b2c0865d908951b712e1e737b939dd0fade [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
30static cl::list<int> Offsets(
31 "offset", cl::desc("Format a range starting at this file offset."));
32static cl::list<int> Lengths(
33 "length", cl::desc("Format a range of this length, -1 for end of file."));
34static cl::opt<std::string> Style(
35 "style",
36 cl::desc("Coding style, currently supports: LLVM, Google, Chromium."),
37 cl::init("LLVM"));
38static cl::opt<bool> Inplace("i",
39 cl::desc("Inplace edit <file>, if specified."));
40
41static cl::opt<bool> OutputXML(
42 "output-replacements-xml", cl::desc("Output replacements as XML."));
43
44static cl::opt<std::string> FileName(cl::Positional, cl::desc("[<file>]"),
45 cl::init("-"));
46
47namespace clang {
48namespace format {
49
50static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source,
51 SourceManager &Sources, FileManager &Files) {
52 const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" :
53 FileName,
54 Source->getBufferSize(), 0);
55 Sources.overrideFileContents(Entry, Source, true);
56 return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
57}
58
59static FormatStyle getStyle() {
60 FormatStyle TheStyle = getGoogleStyle();
61 if (Style == "LLVM")
62 TheStyle = getLLVMStyle();
63 if (Style == "Chromium")
64 TheStyle = getChromiumStyle();
65 return TheStyle;
66}
67
68static void format() {
69 FileManager Files((FileSystemOptions()));
70 DiagnosticsEngine Diagnostics(
71 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
72 new DiagnosticOptions);
73 SourceManager Sources(Diagnostics, Files);
74 OwningPtr<MemoryBuffer> Code;
75 if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) {
76 llvm::errs() << ec.message() << "\n";
77 return;
78 }
79 FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files);
80 Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts());
81 if (Offsets.empty())
82 Offsets.push_back(0);
83 if (Offsets.size() != Lengths.size() &&
84 !(Offsets.size() == 1 && Lengths.empty())) {
85 llvm::errs() << "Number of -offset and -length arguments must match.\n";
86 return;
87 }
88 std::vector<CharSourceRange> Ranges;
89 for (cl::list<int>::size_type i = 0, e = Offsets.size(); i != e; ++i) {
90 SourceLocation Start =
91 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
92 SourceLocation End;
93 if (i < Lengths.size()) {
94 End = Start.getLocWithOffset(Lengths[i]);
95 } else {
96 End = Sources.getLocForEndOfFile(ID);
97 }
98 Ranges.push_back(CharSourceRange::getCharRange(Start, End));
99 }
100 tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges);
101 if (OutputXML) {
102 llvm::outs() << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n";
103 for (tooling::Replacements::const_iterator I = Replaces.begin(),
104 E = Replaces.end();
105 I != E; ++I) {
106 llvm::outs() << "<replacement "
107 << "offset='" << I->getOffset() << "' "
108 << "length='" << I->getLength() << "'>"
109 << I->getReplacementText() << "</replacement>\n";
110 }
111 llvm::outs() << "</replacements>\n";
112 } else {
113 Rewriter Rewrite(Sources, LangOptions());
114 tooling::applyAllReplacements(Replaces, Rewrite);
115 if (Inplace) {
116 if (Replaces.size() == 0)
117 return; // Nothing changed, don't touch the file.
118
119 std::string ErrorInfo;
120 llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo,
121 llvm::raw_fd_ostream::F_Binary);
122 if (!ErrorInfo.empty()) {
123 llvm::errs() << "Error while writing file: " << ErrorInfo << "\n";
124 return;
125 }
126 Rewrite.getEditBuffer(ID).write(FileStream);
127 FileStream.flush();
128 } else {
129 Rewrite.getEditBuffer(ID).write(outs());
130 }
131 }
132}
133
134} // namespace format
135} // namespace clang
136
137int main(int argc, const char **argv) {
138 llvm::sys::PrintStackTraceOnErrorSignal();
139 cl::ParseCommandLineOptions(
140 argc, argv,
141 "A tool to format C/C++/Obj-C code.\n\n"
142 "Currently supports LLVM and Google style guides.\n"
143 "If no arguments are specified, it formats the code from standard input\n"
144 "and writes the result to the standard output.\n"
145 "If <file> is given, it reformats the file. If -i is specified together\n"
146 "with <file>, the file is edited in-place. Otherwise, the result is\n"
147 "written to the standard output.\n");
148 if (Help)
149 cl::PrintHelpMessage();
150 clang::format::format();
151 return 0;
152}