Refactoring and rewriting the ContinuationParser as a recursive descent
parser. All thanks to klimek!
Hi klimek, doug.gregor,
http://llvm-reviews.chandlerc.com/D80
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D80?vs=202&id=325#toc
Files:
include/clang/Format/Format.h
lib/ASTMatchers/ASTMatchFinder.cpp
lib/CMakeLists.txt
lib/Format/CMakeLists.txt
lib/Format/ContinuationParser.cpp
lib/Format/ContinuationParser.h
lib/Format/Format.cpp
lib/Format/Makefile
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,46 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+// where it can be used to format real code.
+//
+// Various functions to configurably format source 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 &Sources,
+ std::vector<CodeRange> Ranges);
+
+} // end namespace format
+} // end namespace clang
+
+#endif // LLVM_CLANG_FORMAT_FORMAT_H
Index: lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- lib/ASTMatchers/ASTMatchFinder.cpp
+++ lib/ASTMatchers/ASTMatchFinder.cpp
@@ -183,8 +183,6 @@
// We assume that the QualType and the contained type are on the same
// hierarchy level. Thus, we try to match either of them.
bool TraverseType(QualType TypeNode) {
- if (TypeNode.isNull())
- return true;
ScopedIncrement ScopedDepth(&CurrentDepth);
// Match the Type.
if (!match(*TypeNode))
@@ -195,8 +193,6 @@
// We assume that the TypeLoc, contained QualType and contained Type all are
// on the same hierarchy level. Thus, we try to match all of them.
bool TraverseTypeLoc(TypeLoc TypeLocNode) {
- if (TypeLocNode.isNull())
- return true;
ScopedIncrement ScopedDepth(&CurrentDepth);
// Match the Type.
if (!match(*TypeLocNode.getType()))
@@ -212,12 +208,10 @@
return (NNS == NULL) || traverse(*NNS);
}
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
- if (!NNS)
- return true;
ScopedIncrement ScopedDepth(&CurrentDepth);
if (!match(*NNS.getNestedNameSpecifier()))
return false;
- return traverse(NNS);
+ return !NNS || traverse(NNS);
}
bool shouldVisitTemplateInstantiations() const { return true; }
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
+ ContinuationParser.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/ContinuationParser.cpp
===================================================================
--- /dev/null
+++ lib/Format/ContinuationParser.cpp
@@ -0,0 +1,192 @@
+//===--- ContinuationParser.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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+// where it can be used to format real code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ContinuationParser.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace format {
+
+ContinuationParser::ContinuationParser(Lexer &Lex, SourceManager &Sources,
+ ContinuationConsumer &Callback)
+ : Lex(Lex), Sources(Sources), Callback(Callback) {
+ Lex.SetKeepWhitespaceMode(true);
+}
+
+void ContinuationParser::parse() {
+ parseToken();
+ parseLevel();
+}
+
+void ContinuationParser::parseLevel() {
+ do {
+ switch(FormatTok.Tok.getKind()) {
+ case tok::hash:
+ parsePPDirective();
+ break;
+ case tok::comment:
+ parseComment();
+ break;
+ case tok::l_brace:
+ parseBlock();
+ break;
+ case tok::r_brace:
+ return;
+ default:
+ parseStatement();
+ break;
+ }
+ } while (!eof());
+}
+
+void ContinuationParser::parseBlock() {
+ nextToken();
+ addContinuation();
+ ++Cont.Level;
+ parseLevel();
+ --Cont.Level;
+ if (FormatTok.Tok.getKind() != tok::r_brace) abort();
+ nextToken();
+ addContinuation();
+ if (FormatTok.Tok.getKind() == tok::semi)
+ nextToken();
+}
+
+void ContinuationParser::parsePPDirective() {
+ while (!eof()) {
+ nextToken();
+ if (FormatTok.NewlinesBefore > 0) return;
+ }
+}
+
+void ContinuationParser::parseComment() {
+ while (!eof()) {
+ nextToken();
+ if (FormatTok.NewlinesBefore > 0) {
+ addContinuation();
+ return;
+ }
+ }
+}
+
+void ContinuationParser::parseStatement() {
+ do {
+ switch (FormatTok.Tok.getKind()) {
+ case tok::semi:
+ {
+ nextToken();
+ addContinuation();
+ return;
+ }
+ case tok::l_paren:
+ parseParens();
+ break;
+ case tok::l_brace:
+ {
+ parseBlock();
+ return;
+ }
+ case tok::raw_identifier:
+ {
+ StringRef Data(Sources.getCharacterData(FormatTok.Tok.getLocation()),
+ FormatTok.Tok.getLength());
+ if (Data == "if") {
+ parseIfThenElse();
+ return;
+ }
+ }
+ default:
+ nextToken();
+ break;
+ }
+ } while (!eof());
+}
+
+void ContinuationParser::parseParens() {
+ if (FormatTok.Tok.getKind() != tok::l_paren) abort();
+ nextToken();
+ do {
+ switch (FormatTok.Tok.getKind()) {
+ case tok::l_paren:
+ parseParens();
+ break;
+ case tok::r_paren:
+ nextToken();
+ return;
+ default:
+ nextToken();
+ break;
+ }
+ } while (!eof());
+}
+
+void ContinuationParser::parseIfThenElse() {
+ if (FormatTok.Tok.getKind() != tok::raw_identifier) abort();
+ nextToken();
+ parseParens();
+ if (FormatTok.Tok.getKind() == tok::l_brace) {
+ parseBlock();
+ } else {
+ addContinuation();
+ ++Cont.Level;
+ parseStatement();
+ --Cont.Level;
+ }
+ if (FormatTok.Tok.getKind() == tok::raw_identifier) {
+ StringRef Data(Sources.getCharacterData(FormatTok.Tok.getLocation()),
+ FormatTok.Tok.getLength());
+ if (Data == "else") {
+ nextToken();
+ parseStatement();
+ }
+ }
+}
+
+void ContinuationParser::addContinuation() {
+ Callback.formatContinuation(Cont);
+ Cont.Tokens.clear();
+}
+
+bool ContinuationParser::eof() const {
+ return FormatTok.Tok.getKind() == tok::eof;
+}
+
+void ContinuationParser::nextToken() {
+ if (eof()) return;
+ Cont.Tokens.push_back(FormatTok);
+ return parseToken();
+}
+
+void ContinuationParser::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(Sources.getCharacterData(FormatTok.Tok.getLocation()),
+ FormatTok.Tok.getLength());
+ if (std::find(Data.begin(), Data.end(), '\n') != Data.end())
+ ++FormatTok.NewlinesBefore;
+ FormatTok.WhiteSpaceLength += FormatTok.Tok.getLength();
+
+ if (eof()) return;
+ Lex.LexFromRawLexer(FormatTok.Tok);
+ }
+}
+
+} // end namespace format
+} // end namespace clang
Index: lib/Format/ContinuationParser.h
===================================================================
--- /dev/null
+++ lib/Format/ContinuationParser.h
@@ -0,0 +1,76 @@
+//===--- ContinuationParser.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.
+//
+//===----------------------------------------------------------------------===//
+//
+// 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_CONTINUATION_PARSER_H
+#define LLVM_CLANG_FORMAT_CONTINUATION_PARSER_H
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace format {
+
+struct FormatToken {
+ FormatToken() : NewlinesBefore(0), WhiteSpaceLength(0) {}
+
+ Token Tok;
+ unsigned NewlinesBefore;
+ unsigned WhiteSpaceLength;
+ SourceLocation WhiteSpaceStart;
+};
+
+struct Continuation {
+ Continuation() : Level(0) {}
+
+ std::vector<FormatToken> Tokens;
+ unsigned Level;
+};
+
+class ContinuationConsumer {
+public:
+ virtual void formatContinuation(const Continuation &Cont) = 0;
+};
+
+class ContinuationParser {
+public:
+ ContinuationParser(Lexer &Lex, SourceManager &Sources,
+ ContinuationConsumer &Callback);
+
+ void parse();
+
+private:
+ void parseLevel();
+ void parseBlock();
+ void parsePPDirective();
+ void parseComment();
+ void parseStatement();
+ void parseParens();
+ void parseIfThenElse();
+ void addContinuation();
+ bool eof() const;
+ void nextToken();
+ void parseToken();
+
+ Continuation Cont;
+ FormatToken FormatTok;
+
+ Lexer &Lex;
+ SourceManager &Sources;
+ ContinuationConsumer &Callback;
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif // LLVM_CLANG_FORMAT_CONTINUATION_PARSER_H
Index: lib/Format/Format.cpp
===================================================================
--- /dev/null
+++ lib/Format/Format.cpp
@@ -0,0 +1,222 @@
+//===--- 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.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is EXPERIMENTAL code under heavy development. It is not in a state yet,
+// where it can be used to format real code.
+//
+// Implements Format.h.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Format/Format.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+
+#include "ContinuationParser.h"
+
+namespace clang {
+namespace format {
+
+using llvm::MutableArrayRef;
+
+class ContinuationFormatter {
+public:
+ ContinuationFormatter(SourceManager &Sources,
+ const Continuation &Cont,
+ tooling::Replacements &Replaces)
+ : Sources(Sources), Cont(Cont), Replaces(Replaces) {}
+
+ void format() {
+ addNewline(Cont.Tokens[0], Cont.Level);
+ count = 0;
+ IndentState State;
+ State.ParenLevel = 0;
+ State.Column = Cont.Level * 2 + Cont.Tokens[0].Tok.getLength();
+
+ State.UsedIndent.push_back(Cont.Level * 2);
+ State.Indent.push_back(Cont.Level * 2 + 4);
+ for (unsigned i = 1; i < Cont.Tokens.size(); ++i) {
+ bool InsertNewLine = Cont.Tokens[i].NewlinesBefore > 0;
+ if (!InsertNewLine) {
+ int NoBreak = numLines(State, false, i + 1,
+ Cont.Tokens.size()-1, 100000);
+ int Break = numLines(State, true, i + 1, Cont.Tokens.size()-1, 100000);
+ InsertNewLine = Break < NoBreak;
+ }
+ addToken(i, InsertNewLine, false, State);
+ }
+ }
+
+private:
+ // The current state when indenting a continuation.
+ struct IndentState {
+ unsigned ParenLevel;
+ unsigned Column;
+ std::vector<unsigned> Indent;
+ std::vector<unsigned> UsedIndent;
+ };
+
+ // Append the token at 'Index' to the IndentState 'State'.
+ void addToken(unsigned Index, bool Newline, bool DryRun, IndentState &State) {
+ if (Cont.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)
+ setWhitespace(Cont.Tokens[Index], 1, State.Indent[State.ParenLevel]);
+ State.Column = State.Indent[State.ParenLevel] +
+ Cont.Tokens[Index].Tok.getLength();
+ State.UsedIndent[State.ParenLevel] = State.Indent[State.ParenLevel];
+ } else {
+ bool Space = spaceRequiredBetween(Cont.Tokens[Index - 1].Tok,
+ Cont.Tokens[Index].Tok);
+ //if (Cont.Tokens[Index].NewlinesBefore == 0)
+ // Space = Cont.Tokens[Index].WhiteSpaceLength > 0;
+ if (!DryRun)
+ setWhitespace(Cont.Tokens[Index], 0, Space ? 1 : 0);
+ if (Cont.Tokens[Index - 1].Tok.getKind() == tok::l_paren)
+ State.Indent[State.ParenLevel] = State.Column;
+ State.Column += Cont.Tokens[Index].Tok.getLength() + (Space ? 1 : 0);
+ }
+
+ if (Cont.Tokens[Index].Tok.getKind() == tok::r_paren) {
+ if (State.ParenLevel == 0) {
+ llvm::outs() << "ParenLevel is 0!!!\n";
+ abort();
+ }
+ --State.ParenLevel;
+ State.Indent.pop_back();
+ }
+ }
+
+ bool canBreakAfter(Token tok) {
+ return tok.getKind() == tok::comma || tok.getKind() == tok::semi ||
+ tok.getKind() == tok::l_paren;
+ }
+
+ // Calculate the number of lines needed to format the remaining part of the
+ // continuation starting in the state 'State'. If 'NewLine' is set, a new line
+ // will be added after the previous token.
+ // 'EndIndex' is the last token belonging to the continuation.
+ // 'StopAt' is used for optimization. If we can determine that we'll
+ // definitely need more than 'StopAt' additional lines, we already know of a
+ // better solution.
+ int numLines(IndentState State, bool NewLine, unsigned Index,
+ unsigned EndIndex, int StopAt) {
+ count++;
+
+ // We are at the end of the continuation, 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;
+
+ int NoBreak = numLines(State, false, Index + 1, EndIndex, StopAt);
+ if (!canBreakAfter(Cont.Tokens[Index - 1].Tok))
+ return NoBreak + (NewLine ? 1 : 0);
+ int Break = numLines(State, true, Index + 1, EndIndex,
+ std::min(StopAt, NoBreak));
+ return std::min(NoBreak, Break) + (NewLine ? 1 : 0);
+ }
+
+ void setWhitespace(const FormatToken& Tok, unsigned NewLines,
+ unsigned Spaces) {
+ Replaces.insert(tooling::Replacement(Sources, 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(Sources.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 before token \c Index.
+ void addNewline(const FormatToken &Token, unsigned Level) {
+ //unsigned Index, unsigned Level) {
+ if (Token.WhiteSpaceStart.isValid()) {
+ unsigned Newlines = Token.NewlinesBefore;
+ unsigned Offset = Sources.getFileOffset(Token.WhiteSpaceStart);
+ if (Newlines == 0 && Offset != 0)
+ Newlines = 1;
+ setWhitespace(Token, Newlines, Level * 2);
+ }
+ }
+
+ SourceManager &Sources;
+ const Continuation &Cont;
+ tooling::Replacements &Replaces;
+ unsigned int count;
+};
+
+class Formatter : public ContinuationConsumer {
+public:
+ Formatter(Lexer &Lex, SourceManager &Sources,
+ const std::vector<CodeRange> &Ranges)
+ : Lex(Lex), Sources(Sources) {}
+
+ tooling::Replacements format() {
+ ContinuationParser Parser(Lex, Sources, *this);
+ Parser.parse();
+ return Replaces;
+ }
+
+private:
+ virtual void formatContinuation(const Continuation &TheCont) {
+ ContinuationFormatter Formatter(Sources, TheCont, Replaces);
+ Formatter.format();
+ }
+
+ Lexer &Lex;
+ SourceManager &Sources;
+ tooling::Replacements Replaces;
+};
+
+tooling::Replacements reformat(Lexer &Lex, SourceManager &Sources,
+ std::vector<CodeRange> Ranges) {
+ Formatter formatter(Lex, Sources, 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/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,130 @@
+//===- 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, FormatsContinuationsAtFirstFormat) {
+ 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, 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