Hi silvas, klimek, doug.gregor,

http://llvm-reviews.chandlerc.com/D80

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D80?vs=333&id=337#toc

Files:
  include/clang/Format/Format.h
  lib/CMakeLists.txt
  lib/Format/CMakeLists.txt
  lib/Format/Format.cpp
  lib/Format/Makefile
  lib/Format/UnwrappedLineParser.cpp
  lib/Format/UnwrappedLineParser.h
  lib/Makefile
  unittests/CMakeLists.txt
  unittests/Format/CMakeLists.txt
  unittests/Format/FormatTest.cpp
  unittests/Format/Makefile
Index: include/clang/Format/Format.h
===================================================================
--- /dev/null
+++ include/clang/Format/Format.h
@@ -0,0 +1,47 @@
+//===--- Format.h - Format C++ code -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  \file
+//  Various functions to configurably format source code.
+//
+//  This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+//  where it can be used to format real code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FORMAT_FORMAT_H_
+#define LLVM_CLANG_FORMAT_FORMAT_H
+
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+
+class Lexer;
+class SourceManager;
+
+namespace format {
+
+/// \brief A character range of source code.
+struct CodeRange {
+  CodeRange(unsigned Offset, unsigned Length)
+    : Offset(Offset), Length(Length) {}
+
+  unsigned Offset;
+  unsigned Length;
+};
+
+/// \brief Reformats the given Ranges in the token stream coming out of \c Lex.
+tooling::Replacements reformat(Lexer &Lex, SourceManager &SourceMgr,
+                               std::vector<CodeRange> Ranges);
+
+} // end namespace format
+} // end namespace clang
+
+#endif // LLVM_CLANG_FORMAT_FORMAT_H
Index: lib/CMakeLists.txt
===================================================================
--- lib/CMakeLists.txt
+++ lib/CMakeLists.txt
@@ -16,3 +16,4 @@
 add_subdirectory(FrontendTool)
 add_subdirectory(Tooling)
 add_subdirectory(StaticAnalyzer)
+add_subdirectory(Format)
Index: lib/Format/CMakeLists.txt
===================================================================
--- /dev/null
+++ lib/Format/CMakeLists.txt
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangFormat
+  UnwrappedLineParser.cpp
+  Format.cpp
+  )
+
+add_dependencies(clangFormat
+  ClangAttrClasses
+  ClangAttrList
+  ClangDeclNodes
+  ClangDiagnosticCommon
+  ClangDiagnosticFrontend
+  ClangStmtNodes
+  )
+
+target_link_libraries(clangFormat
+  clangBasic
+  clangFrontend
+  clangAST
+  clangASTMatchers
+  clangRewriteCore
+  clangRewriteFrontend
+  )
Index: lib/Format/Format.cpp
===================================================================
--- /dev/null
+++ lib/Format/Format.cpp
@@ -0,0 +1,252 @@
+//===--- Format.cpp - Format C++ code -------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  \file
+//  \brief This file implements functions declared in Format.h. This will be
+//  split into separate files as we go.
+//
+//  This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+//  where it can be used to format real code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Format/Format.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+
+#include "UnwrappedLineParser.h"
+
+namespace clang {
+namespace format {
+
+using llvm::MutableArrayRef;
+
+class UnwrappedLineFormatter {
+public:
+  UnwrappedLineFormatter(SourceManager &SourceMgr,
+                         const UnwrappedLine &Line,
+                         tooling::Replacements &Replaces)
+      : SourceMgr(SourceMgr), Line(Line), Replaces(Replaces) {}
+
+  void format() {
+    addNewline(Line.Tokens[0], Line.Level);
+    count = 0;
+    IndentState State;
+    State.ParenLevel = 0;
+    State.Column = Line.Level * 2 + Line.Tokens[0].Tok.getLength();
+
+    State.UsedIndent.push_back(Line.Level * 2);
+    State.Indent.push_back(Line.Level * 2 + 4);
+
+    // Start iterating at 1 as we have correctly formatted of Token #0 above.
+    for (unsigned i = 1, n = Line.Tokens.size(); i != n; ++i) {
+      bool InsertNewLine = Line.Tokens[i].NewlinesBefore > 0;
+      if (!InsertNewLine) {
+        unsigned NoBreak = numLines(State, false, i + 1,
+                               Line.Tokens.size()-1, 100000);
+        unsigned Break = numLines(State, true, i + 1, Line.Tokens.size()-1, 100000);
+        InsertNewLine = Break < NoBreak;
+      }
+      addToken(i, InsertNewLine, false, State);
+    }
+  }
+
+private:
+  /// \brief The current state when indenting a unwrapped line.
+  ///
+  /// As the indenting tries different combinations this is copied by value.
+  struct IndentState {
+    /// \brief The current parenthesis level, i.e. the number of opening minus
+    /// the number of closing parenthesis left of the current position.
+    unsigned ParenLevel;
+
+    /// \brief The number of used columns in the current line.
+    unsigned Column;
+
+    /// \brief The position to which a specific parenthesis level needs to be
+    /// indented.
+    std::vector<unsigned> Indent;
+
+    /// \brief The indents actively used by a parenthesis level.
+    ///
+    /// This is used to prevent situations like:
+    /// \code
+    ///   callA(callB(
+    ///       callC()),
+    ///         callD()).
+    /// \endcode
+    /// We might (configurably) not want callC() to be indented less callD()
+    /// as it has a higher indent level.
+    std::vector<unsigned> UsedIndent;
+  };
+
+  /// Append the token at \c Index to \c State.
+  void addToken(unsigned Index, bool Newline, bool DryRun, IndentState &State) {
+    if (Line.Tokens[Index].Tok.getKind() == tok::l_paren) {
+      State.UsedIndent.push_back(State.UsedIndent.back());
+      State.Indent.push_back(State.UsedIndent.back() + 4);
+      ++State.ParenLevel;
+    }
+    if (Newline) {
+      if (!DryRun)
+        replaceWhitespace(Line.Tokens[Index], 1,
+                          State.Indent[State.ParenLevel]);
+      State.Column = State.Indent[State.ParenLevel] +
+          Line.Tokens[Index].Tok.getLength();
+      State.UsedIndent[State.ParenLevel] = State.Indent[State.ParenLevel];
+    } else {
+      bool Space = spaceRequiredBetween(Line.Tokens[Index - 1].Tok,
+                                        Line.Tokens[Index].Tok);
+      //if (Line.Tokens[Index].NewlinesBefore == 0)
+      //  Space = Line.Tokens[Index].WhiteSpaceLength > 0;
+      if (!DryRun)
+        replaceWhitespace(Line.Tokens[Index], 0, Space ? 1 : 0);
+      if (Line.Tokens[Index - 1].Tok.getKind() == tok::l_paren)
+        State.Indent[State.ParenLevel] = State.Column;
+      State.Column += Line.Tokens[Index].Tok.getLength() + (Space ? 1 : 0);
+    }
+
+    if (Line.Tokens[Index].Tok.getKind() == tok::r_paren) {
+      // FIXME: We should be able to handle this kind of code.
+      assert(State.ParenLevel != 0 && "Unexpected ')'.");
+      --State.ParenLevel;
+      State.Indent.pop_back();
+    }
+  }
+
+  bool canBreakAfter(Token tok) {
+    return tok.getKind() == tok::comma || tok.getKind() == tok::semi ||
+        tok.getKind() == tok::l_paren;
+  }
+
+  /// \brief Calculate the number of lines needed to format the remaining part
+  /// of the unwrapped line.
+  ///
+  /// Assumes the formatting of the \c Token until \p EndIndex has led to
+  /// the \c IndentState \p State. If \p NewLine is set, a new line will be
+  /// added after the previous token.
+  ///
+  /// \param EndIndex is the last token belonging to the unwrapped line.
+  ///
+  /// \param StopAt is used for optimization. If we can determine that we'll
+  /// definitely need at least \p StopAt additional lines, we already know of a
+  /// better solution.
+  unsigned numLines(IndentState State, bool NewLine, unsigned Index,
+                    unsigned EndIndex, unsigned StopAt) {
+    count++;
+
+    // We are at the end of the unwrapped line, so we don't need any more lines.
+    if (Index > EndIndex)
+      return 0;
+
+    addToken(Index - 1, NewLine, true, State);
+    if (NewLine)
+      --StopAt;
+
+    // Exceeding 80 columns is bad.
+    if (State.Column > 80)
+      return 10000;
+
+    if (StopAt < 1)
+      return 10000;
+
+    unsigned NoBreak = numLines(State, false, Index + 1, EndIndex, StopAt);
+    if (!canBreakAfter(Line.Tokens[Index - 1].Tok))
+      return NoBreak + (NewLine ? 1 : 0);
+    unsigned Break = numLines(State, true, Index + 1, EndIndex,
+                         std::min(StopAt, NoBreak));
+    return std::min(NoBreak, Break) + (NewLine ? 1 : 0);
+  }
+
+  /// \brief Replaces the whitespace in front of \p Tok. Only call once for
+  /// each \c FormatToken.
+  void replaceWhitespace(const FormatToken &Tok, unsigned NewLines,
+                         unsigned Spaces) {
+    Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart,
+                                         Tok.WhiteSpaceLength,
+                                         std::string(NewLines, '\n') +
+                                         std::string(Spaces, ' ')));
+  }
+
+  bool isIfForOrWhile(Token Tok) {
+    if (Tok.getKind() != tok::raw_identifier)
+      return false;
+    StringRef Data(SourceMgr.getCharacterData(Tok.getLocation()),
+                   Tok.getLength());
+    return Data == "for" || Data == "while" || Data == "if";
+  }
+
+  bool spaceRequiredBetween(Token Left, Token Right) {
+
+    if (Left.is(tok::period) || Right.is(tok::period))
+      return false;
+    if (Left.is(tok::colon) || Right.is(tok::colon))
+      return false;
+    if (Left.is(tok::plusplus) && Right.is(tok::raw_identifier))
+      return false;
+    if (Left.is(tok::l_paren))
+      return false;
+    if (Right.is(tok::r_paren) || Right.is(tok::semi) || Right.is(tok::comma))
+      return false;
+    if (Right.is(tok::l_paren)) {
+      return isIfForOrWhile(Left);
+    }
+    return true;
+  }
+
+  /// \brief Add a new line and the required indent before \p Token.
+  void addNewline(const FormatToken &Token, unsigned Level) {
+      //unsigned Index, unsigned Level) {
+    if (Token.WhiteSpaceStart.isValid()) {
+      unsigned Newlines = Token.NewlinesBefore;
+      unsigned Offset = SourceMgr.getFileOffset(Token.WhiteSpaceStart);
+      if (Newlines == 0 && Offset != 0)
+        Newlines = 1;
+      replaceWhitespace(Token, Newlines, Level * 2);
+    }
+  }
+
+  SourceManager &SourceMgr;
+  const UnwrappedLine &Line;
+  tooling::Replacements &Replaces;
+  unsigned int count;
+};
+
+class Formatter : public UnwrappedLineConsumer {
+public:
+  Formatter(Lexer &Lex, SourceManager &SourceMgr,
+            const std::vector<CodeRange> &Ranges)
+      : Lex(Lex), SourceMgr(SourceMgr) {}
+
+  tooling::Replacements format() {
+    UnwrappedLineParser Parser(Lex, SourceMgr, *this);
+    Parser.parse();
+    return Replaces;
+  }
+
+private:
+  virtual void formatUnwrappedLine(const UnwrappedLine &TheLine) {
+    UnwrappedLineFormatter Formatter(SourceMgr, TheLine, Replaces);
+    Formatter.format();
+  }
+
+  Lexer &Lex;
+  SourceManager &SourceMgr;
+  tooling::Replacements Replaces;
+};
+
+tooling::Replacements reformat(Lexer &Lex, SourceManager &SourceMgr,
+                               std::vector<CodeRange> Ranges) {
+  Formatter formatter(Lex, SourceMgr, Ranges);
+  return formatter.format();
+}
+
+}  // namespace format
+}  // namespace clang
Index: lib/Format/Makefile
===================================================================
--- /dev/null
+++ lib/Format/Makefile
@@ -0,0 +1,13 @@
+##===- clang/lib/Tooling/Makefile ---------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+LIBRARYNAME := clangTooling
+
+include $(CLANG_LEVEL)/Makefile
Index: lib/Format/UnwrappedLineParser.cpp
===================================================================
--- /dev/null
+++ lib/Format/UnwrappedLineParser.cpp
@@ -0,0 +1,204 @@
+//===--- UnwrappedLineParser.cpp - Format C++ code ------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  \file
+//  \brief This file contains the implementation of the UnwrappedLineParser,
+//  which turns a stream of tokens into UnwrappedLines.
+//
+//  This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+//  where it can be used to format real code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnwrappedLineParser.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace format {
+
+UnwrappedLineParser::UnwrappedLineParser(Lexer &Lex, SourceManager &SourceMgr,
+                                         UnwrappedLineConsumer &Callback)
+    : Lex(Lex), SourceMgr(SourceMgr), Callback(Callback) {
+  Lex.SetKeepWhitespaceMode(true);
+}
+
+void UnwrappedLineParser::parse() {
+  parseToken();
+  parseLevel();
+}
+
+void UnwrappedLineParser::parseLevel() {
+  do {
+    switch(FormatTok.Tok.getKind()) {
+      case tok::hash:
+        parsePPDirective();
+        break;
+      case tok::comment:
+        parseComment();
+        break;
+      case tok::l_brace:
+        parseBlock();
+        addUnwrappedLine();
+        break;
+      case tok::r_brace:
+        return;
+      default:
+        parseStatement();
+        break;
+    }
+  } while (!eof());
+}
+
+void UnwrappedLineParser::parseBlock() {
+  nextToken();
+  addUnwrappedLine();
+  ++Line.Level;
+  parseLevel();
+  --Line.Level;
+  if (FormatTok.Tok.getKind() != tok::r_brace) abort();
+  nextToken();
+  if (FormatTok.Tok.getKind() == tok::semi)
+    nextToken();
+}
+
+void UnwrappedLineParser::parsePPDirective() {
+  while (!eof()) {
+    nextToken();
+    if (FormatTok.NewlinesBefore > 0) return;
+  }
+}
+
+void UnwrappedLineParser::parseComment() {
+  while (!eof()) {
+    nextToken();
+    if (FormatTok.NewlinesBefore > 0) {
+      addUnwrappedLine();
+      return;
+    }
+  }
+}
+
+void UnwrappedLineParser::parseStatement() {
+  do {
+    switch (FormatTok.Tok.getKind()) {
+      case tok::semi:
+        nextToken();
+        addUnwrappedLine();
+        return;
+      case tok::l_paren:
+        parseParens();
+        break;
+      case tok::l_brace:
+        parseBlock();
+        addUnwrappedLine();
+        return;
+      case tok::raw_identifier:
+        if (tokenText() == "if") {
+          parseIfThenElse();
+          return;
+        }
+      default:
+        nextToken();
+        break;
+    }
+  } while (!eof());
+} 
+
+void UnwrappedLineParser::parseParens() {
+  assert(FormatTok.Tok.getKind() == tok::l_paren && "'(' expected.");
+  nextToken();
+  do {
+    switch (FormatTok.Tok.getKind()) {
+      case tok::l_paren:
+        parseParens();
+        break;
+      case tok::r_paren:
+        nextToken();
+        return;
+      default:
+        nextToken();
+        break;
+    }
+  } while (!eof());
+}
+
+void UnwrappedLineParser::parseIfThenElse() {
+  assert(FormatTok.Tok.getKind() == tok::raw_identifier &&
+         "Identifier expected");
+  nextToken();
+  parseParens();
+  bool NeedsUnwrappedLine = false;
+  if (FormatTok.Tok.getKind() == tok::l_brace) {
+    parseBlock();
+    NeedsUnwrappedLine = true;
+  } else {
+    addUnwrappedLine();
+    ++Line.Level;
+    parseStatement();
+    --Line.Level;
+  }
+  if (FormatTok.Tok.is(tok::raw_identifier) && tokenText() == "else") {
+    nextToken();
+    if (FormatTok.Tok.getKind() == tok::l_brace) {
+      parseBlock();
+      addUnwrappedLine();
+    } else {
+      addUnwrappedLine();
+      ++Line.Level;
+      parseStatement();
+      --Line.Level;
+    }
+  } else if (NeedsUnwrappedLine) {
+    addUnwrappedLine();
+  }
+}
+
+void UnwrappedLineParser::addUnwrappedLine() {
+  Callback.formatUnwrappedLine(Line);
+  Line.Tokens.clear();
+}
+
+bool UnwrappedLineParser::eof() const {
+  return FormatTok.Tok.getKind() == tok::eof;
+}
+
+void UnwrappedLineParser::nextToken() {
+  if (eof())
+    return;
+  Line.Tokens.push_back(FormatTok);
+  parseToken();
+}
+
+void UnwrappedLineParser::parseToken() {
+  FormatTok = FormatToken();
+  Lex.LexFromRawLexer(FormatTok.Tok);
+  FormatTok.WhiteSpaceStart = FormatTok.Tok.getLocation();
+
+  // Consume and record whitespace until we find a significant
+  // token.
+  while (FormatTok.Tok.getKind() == tok::unknown) {
+    StringRef Data = tokenText();
+    if (std::find(Data.begin(), Data.end(), '\n') != Data.end())
+      ++FormatTok.NewlinesBefore;
+    FormatTok.WhiteSpaceLength += FormatTok.Tok.getLength();
+
+    if (eof()) return;
+    Lex.LexFromRawLexer(FormatTok.Tok);
+  }
+}
+
+StringRef UnwrappedLineParser::tokenText() {
+  StringRef Data(SourceMgr.getCharacterData(FormatTok.Tok.getLocation()),
+                 FormatTok.Tok.getLength());
+  return Data;
+}
+
+} // end namespace format
+} // end namespace clang
Index: lib/Format/UnwrappedLineParser.h
===================================================================
--- /dev/null
+++ lib/Format/UnwrappedLineParser.h
@@ -0,0 +1,108 @@
+//===--- UnwrappedLineParser.cpp - Format C++ code ------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  \file
+//  \brief This file contains the declaration of the UnwrappedLineParser,
+//  which turns a stream of tokens into UnwrappedLines.
+//
+//  This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+//  where it can be used to format real code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FORMAT_UNWRAPPED_LINE_PARSER_H
+#define LLVM_CLANG_FORMAT_UNWRAPPED_LINE_PARSER_H
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace format {
+
+/// \brief A wrapper around a \c Token storing information about the
+/// whitespace characters preceeding it.
+struct FormatToken {
+  FormatToken() : NewlinesBefore(0), WhiteSpaceLength(0) {}
+
+  /// \brief The \c Token.
+  Token Tok;
+
+  /// \brief The number of newlines immediately before the \c Token.
+  ///
+  /// This can be used to determine what the user wrote in the original code
+  /// and thereby e.g. leave an empty line between two function definitions.
+  unsigned NewlinesBefore;
+
+  /// \brief The location of the start of the whitespace immediately preceeding
+  /// the \c Token.
+  ///
+  /// Used together with \c WhiteSpaceLength to create a \c Replacement.
+  SourceLocation WhiteSpaceStart;
+
+  /// \brief The length in characters of the whitespace immediately preceeding
+  /// the \c Token.
+  unsigned WhiteSpaceLength;
+};
+
+/// \brief An unwrapped line is a sequence of \c Token, that we would like to
+/// put on a single line if there was no column limit.
+///
+/// This is used as a main interface between the \c UnwrappedLineParser and the
+/// \c UnwrappedLineFormatter. The key property is that changing the formatting
+/// within an unwrapped line does not affect any other unwrapped lines.
+struct UnwrappedLine {
+  UnwrappedLine() : Level(0) {}
+
+  /// \brief The \c Token comprising this \c UnwrappedLine.
+  SmallVector<FormatToken, 16> Tokens;
+
+  /// \brief The indent level of the \c UnwrappedLine.
+  unsigned Level;
+};
+
+class UnwrappedLineConsumer {
+public:
+  virtual void formatUnwrappedLine(const UnwrappedLine &Line) = 0;
+};
+
+class UnwrappedLineParser {
+public:
+  UnwrappedLineParser(Lexer &Lex, SourceManager &SourceMgr,
+                     UnwrappedLineConsumer &Callback);
+
+  void parse();
+
+private:
+  void parseLevel();
+  void parseBlock();
+  void parsePPDirective();
+  void parseComment();
+  void parseStatement();
+  void parseParens();
+  void parseIfThenElse();
+  void addUnwrappedLine();
+  bool eof() const;
+  void nextToken();
+  void parseToken();
+
+  /// Returns the text of \c FormatTok.
+  StringRef tokenText();
+
+  UnwrappedLine Line;
+  FormatToken FormatTok;
+
+  Lexer &Lex;
+  SourceManager &SourceMgr;
+  UnwrappedLineConsumer &Callback;
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif // LLVM_CLANG_FORMAT_UNWRAPPED_LINE_PARSER_H
Index: lib/Makefile
===================================================================
--- lib/Makefile
+++ lib/Makefile
@@ -10,7 +10,7 @@
 
 PARALLEL_DIRS = Headers Basic Lex Parse AST ASTMatchers Sema CodeGen Analysis \
                 StaticAnalyzer Edit Rewrite ARCMigrate Serialization Frontend \
-                FrontendTool Tooling Driver
+                FrontendTool Tooling Driver Format
 
 include $(CLANG_LEVEL)/Makefile
 
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -15,3 +15,4 @@
 add_subdirectory(Lex)
 add_subdirectory(Frontend)
 add_subdirectory(Tooling)
+add_subdirectory(Format)
Index: unittests/Format/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/Format/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  asmparser
+  support
+  mc
+  )
+
+add_clang_unittest(FormatTests
+  FormatTest.cpp
+  )
+
+target_link_libraries(FormatTests
+  clangAST
+  clangFormat
+  clangTooling
+  clangRewriteCore
+  )
Index: unittests/Format/FormatTest.cpp
===================================================================
--- /dev/null
+++ unittests/Format/FormatTest.cpp
@@ -0,0 +1,162 @@
+//===- unittest/Format/FormatTest.cpp - Formatting unit tests -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../Tooling/RewriterTestContext.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Format/Format.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace format {
+
+class FormatTest : public ::testing::Test {
+protected:
+  std::string format(llvm::StringRef Code, unsigned offset, unsigned length) {
+    RewriterTestContext Context;
+    FileID ID = Context.createInMemoryFile("input.cc", Code);
+    std::vector<CodeRange> Ranges(1, CodeRange(offset, length));
+    Lexer Lex(ID, Context.Sources.getBuffer(ID), Context.Sources,
+              LangOptions());
+    tooling::Replacements Replace = reformat(Lex, Context.Sources, Ranges);
+    EXPECT_TRUE(applyAllReplacements(Replace, Context.Rewrite));
+    //llvm::outs() << Context.getRewrittenText(ID) << "\n";
+    return Context.getRewrittenText(ID);
+  }
+};
+
+TEST_F(FormatTest, DoesNotChangeCorrectlyFormatedCode) {
+  EXPECT_EQ(";", format(";", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsGlobalStatementsAt0) {
+  EXPECT_EQ("int i;", format("  int i;", 0, 1));
+  EXPECT_EQ("\nint i;", format(" \n\t \r  int i;", 0, 1));
+  EXPECT_EQ("int i;\nint j;", format("    int i; int j;", 0, 1));
+  EXPECT_EQ("int i;\nint j;", format("    int i;\n  int j;", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsUnwrappedLinesAtFirstFormat) {
+  EXPECT_EQ("int\n    i;", format("int\ni;", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsNestedBlockStatements) {
+  EXPECT_EQ("{\n  {\n    {\n    }\n  }\n}", format("{{{}}}", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsForLoop) {
+  EXPECT_EQ("for (int i = 0; i < 10; ++i);",
+            format("for(int i=0;i<10;++i);", 0 , 1));
+  EXPECT_EQ("for (int i = 0;\n     i < 10;\n     ++i);",
+            format("for(int i=0;\ni<10;\n++i);", 0 , 1));
+}
+
+TEST_F(FormatTest, FormatsWhileLoop) {
+  EXPECT_EQ("while (true) {\n}", format("while(true){}", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsNestedCall) {
+  EXPECT_EQ("Method(1,\n"
+            "       2(\n"
+            "           3));",
+            format("Method(1,\n2(\n3));", 0, 1));
+  EXPECT_EQ("Method(1(2,\n"
+            "         3()));", format("Method(1(2,\n3()));", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsAwesomeMethodCall) {
+  EXPECT_EQ(
+      "SomeLongMethodName(SomeReallyLongMethod(CallOtherReallyLongMethod(\n"
+      "    parameter, parameter, parameter)), SecondLongCall(some_parameter));",
+      format(
+          "SomeLongMethodName(SomeReallyLongMethod(CallOtherReallyLongMethod(\n"
+          "parameter , parameter, parameter)), SecondLongCall("
+          "some_parameter) );", 0, 1));
+  EXPECT_EQ(
+      "SomeLongMethodName(SomeReallyLongMethod(CallOtherReallyLongMethod(\n"
+      "    parameter, parameter, parameter)), SecondLongCall(some_parameter));",
+      format(
+          "SomeLongMethodName(SomeReallyLongMethod(CallOtherReallyLongMethod("
+          "parameter,parameter,parameter)),SecondLongCall("
+          "some_parameter) );", 0, 1));
+}
+
+TEST_F(FormatTest, FormatsFunctionDefinition) {
+  EXPECT_EQ(
+      "void f(int a, int b, int c, int d, int e, int f, int g,"
+      " int h, int j, int f,\n       int c, int ddddddddddddd) {\n}",
+      format("void f(int a, int b, int c, int d, int e, int f, int g,"
+        "int h, int j, int f, int c, int ddddddddddddd) {}", 0, 1));
+}
+
+TEST_F(FormatTest, FormatIfWithoutCompountStatement) {
+  EXPECT_EQ(
+      "if (true)\n  f();\ng();",
+      format("if (true) f(); g();", 0, 1));
+  EXPECT_EQ(
+      "if (a)\n  if (b)\n    if (c)\n      g();\nh();",
+      format("if(a)if(b)if(c)g();h();", 0, 1));
+  EXPECT_EQ(
+      "if (a)\n  if (b) {\n    f();\n  }\ng();",
+      format("if(a)if(b) {f();}g();", 0, 1));
+}
+
+TEST_F(FormatTest, ParseIfThenElse) {
+  EXPECT_EQ(
+      "if (true)\n"
+      "  if (true)\n"
+      "    if (true)\n"
+      "      f();\n"
+      "    else\n"
+      "      g();\n"
+      "  else\n"
+      "    h();\n"
+      "else\n"
+      "  i();",
+      format("if(true)\nif(true)\nif(true)\nf();\n"
+             "else\ng();\nelse\nh();\nelse\ni();", 0, 1));
+  EXPECT_EQ(
+      "if (true)\n"
+      "  if (true)\n"
+      "    if (true) {\n"
+      "      if (true)\n"
+      "        f();\n"
+      "    } else {\n"
+      "      g();\n"
+      "    }\n"
+      "  else\n"
+      "    h();\n"
+      "else {\n"
+      "  i();\n"
+      "}",
+      format("if(true)\nif(true)\nif(true){\nif(true)f();\n"
+             "}else{\ng();\n}\nelse\nh();\nelse{\ni();\n}", 0, 1));
+}
+
+TEST_F(FormatTest, UnderstandsSingleLineComments) {
+  EXPECT_EQ(
+      "// line 1\n// line 2\nvoid f() {\n}\n",
+      format("// line 1\n// line 2\nvoid f() {}\n", 0, 1));
+
+  EXPECT_EQ(
+      "void f() {\n  // Doesn't do anything\n}",
+      format("void f() {\n// Doesn't do anything\n}", 0, 1));
+}
+
+TEST_F(FormatTest, DoesNotBreakSemiAfterClassDecl) {
+  EXPECT_EQ(
+      "class A {\n};\n", format("class A{};\n", 0, 1));
+}
+
+TEST_F(FormatTest, UnderstandsPPKeywords) {
+  EXPECT_EQ(
+      "#include <a.h>\\\nest\nb\n", format("#include <a.h>\\\nest\nb\n", 0, 1));
+}
+
+} // end namespace tooling
+} // end namespace clang
Index: unittests/Format/Makefile
===================================================================
--- /dev/null
+++ unittests/Format/Makefile
@@ -0,0 +1,19 @@
+##===- unittests/Format/Makefile ---------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../..
+TESTNAME = Format
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
+USEDLIBS = clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \
+           clangDriver.a clangParse.a clangRewriteCore.a
+           clangRewriteFrontend.a clangSema.a clangAnalysis.a clangEdit.a \
+           clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to