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/FileSystem.h" 24 #include "llvm/Support/Signals.h" 25 26 using namespace llvm; 27 28 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); 29 30 static cl::list<int> Offsets( 31 "offset", cl::desc("Format a range starting at this file offset.")); 32 static cl::list<int> Lengths( 33 "length", cl::desc("Format a range of this length, -1 for end of file.")); 34 static cl::opt<std::string> Style( 35 "style", 36 cl::desc("Coding style, currently supports: LLVM, Google, Chromium."), 37 cl::init("LLVM")); 38 static cl::opt<bool> Inplace("i", 39 cl::desc("Inplace edit <file>, if specified.")); 40 41 static cl::opt<bool> OutputXML( 42 "output-replacements-xml", cl::desc("Output replacements as XML.")); 43 44 static cl::opt<std::string> FileName(cl::Positional, cl::desc("[<file>]"), 45 cl::init("-")); 46 47 namespace clang { 48 namespace format { 49 50 static 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 59 static 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 68 static 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 137 int 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 } 153