Home | History | Annotate | Download | only in clang-format
      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 
     28 using namespace llvm;
     29 
     30 // Default style to use when no style specified or specified style not found.
     31 static const char *DefaultStyle = "LLVM";
     32 
     33 static 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.
     37 cl::OptionCategory ClangFormatCategory("Clang-format options");
     38 
     39 static 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));
     46 static 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));
     56 static cl::list<std::string>
     57 LineRanges("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));
     64 static cl::opt<std::string>
     65     Style("style",
     66           cl::desc("Coding style, currently supports:\n"
     67                    "  LLVM, Google, Chromium, Mozilla.\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(DefaultStyle), cl::cat(ClangFormatCategory));
     76 static cl::opt<bool> Inplace("i",
     77                              cl::desc("Inplace edit <file>s, if specified."),
     78                              cl::cat(ClangFormatCategory));
     79 
     80 static cl::opt<bool> OutputXML("output-replacements-xml",
     81                                cl::desc("Output replacements as XML."),
     82                                cl::cat(ClangFormatCategory));
     83 static 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));
     88 static cl::opt<unsigned>
     89     Cursor("cursor",
     90            cl::desc("The position of the cursor when invoking clang-format from"
     91                     " an editor integration"),
     92            cl::init(0), cl::cat(ClangFormatCategory));
     93 
     94 static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
     95                                        cl::cat(ClangFormatCategory));
     96 
     97 namespace clang {
     98 namespace format {
     99 
    100 static 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 
    109 FormatStyle getStyle(StringRef StyleName, StringRef FileName) {
    110   FormatStyle Style;
    111   getPredefinedStyle(DefaultStyle, &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 " << DefaultStyle << " 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 " << DefaultStyle
    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 " << DefaultStyle
    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.
    163 static 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 
    170 static 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.
    232 static 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 
    295 int 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