summaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'unittests')
-rw-r--r--unittests/AST/APValueTest.cpp83
-rw-r--r--unittests/Basic/Makefile2
-rw-r--r--unittests/Basic/SourceManagerTest.cpp296
-rw-r--r--unittests/CMakeLists.txt19
-rw-r--r--unittests/Frontend/FrontendActionTest.cpp4
-rw-r--r--unittests/Frontend/Makefile2
-rw-r--r--unittests/Lex/LexerTest.cpp177
-rw-r--r--unittests/Lex/Makefile (renamed from unittests/AST/Makefile)6
-rw-r--r--unittests/Lex/PreprocessingRecordTest.cpp139
-rw-r--r--unittests/Makefile2
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp223
-rw-r--r--unittests/Tooling/Makefile17
-rw-r--r--unittests/Tooling/ToolingTest.cpp113
13 files changed, 986 insertions, 97 deletions
diff --git a/unittests/AST/APValueTest.cpp b/unittests/AST/APValueTest.cpp
deleted file mode 100644
index 5ac454de5f13f..0000000000000
--- a/unittests/AST/APValueTest.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-//===- unittests/AST/APValueTest.cpp - APValue tests ---===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/APValue.h"
-
-#include "clang/Basic/Diagnostic.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
-
-#include "gtest/gtest.h"
-
-using namespace llvm;
-using namespace clang;
-
-namespace {
-
-class DiagnosticOutputGetter {
- class LastDiagnosticString : public DiagnosticConsumer {
- SmallString<64> LastDiagnostic;
- public:
- virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
- const Diagnostic &Info) {
- LastDiagnostic.clear();
- Info.FormatDiagnostic(LastDiagnostic);
- }
-
- StringRef get() const { return LastDiagnostic; }
-
- virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
- return new LastDiagnosticString();
- }
- };
-
- const IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs;
- const unsigned diag_just_format;
- LastDiagnosticString LastDiagnostic;
- DiagnosticsEngine Diag;
-
-public:
- DiagnosticOutputGetter()
- : DiagIDs(new DiagnosticIDs),
- diag_just_format(DiagIDs->getCustomDiagID(DiagnosticIDs::Error, "%0")),
- Diag(DiagIDs, &LastDiagnostic, false) {
- }
-
- template<typename T>
- std::string operator()(const T& value) {
- Diag.Report(diag_just_format) << value;
- return LastDiagnostic.get().str();
- }
-};
-
-TEST(APValue, Diagnostics) {
- DiagnosticOutputGetter GetDiagnosticOutput;
-
- EXPECT_EQ("Uninitialized", GetDiagnosticOutput(APValue()));
- EXPECT_EQ("5", GetDiagnosticOutput(APValue(APSInt(APInt(16, 5)))));
- EXPECT_EQ("3.141590e+00",
- GetDiagnosticOutput(APValue(APFloat(APFloat::IEEEdouble,
- "3.14159"))));
- EXPECT_EQ("3+4i",
- GetDiagnosticOutput(APValue(APSInt(APInt(16, 3)),
- APSInt(APInt(16, 4)))));
- EXPECT_EQ("3.200000e+00+5.700000e+00i",
- GetDiagnosticOutput(APValue(
- APFloat(APFloat::IEEEdouble, "3.2"),
- APFloat(APFloat::IEEEdouble, "5.7"))));
- APValue V[] = {
- APValue(APSInt(APInt(16, 3))),
- APValue(APSInt(APInt(16, 4))),
- APValue(APSInt(APInt(16, 5)))
- };
- EXPECT_EQ("[3, 4, 5]",
- GetDiagnosticOutput(APValue(V, array_lengthof(V))));
-}
-
-} // anonymous namespace
diff --git a/unittests/Basic/Makefile b/unittests/Basic/Makefile
index 4bac50c12ab30..82de790598f69 100644
--- a/unittests/Basic/Makefile
+++ b/unittests/Basic/Makefile
@@ -10,6 +10,6 @@
CLANG_LEVEL = ../..
TESTNAME = Basic
LINK_COMPONENTS := support mc
-USEDLIBS = clangBasic.a
+USEDLIBS = clangLex.a clangBasic.a
include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp
new file mode 100644
index 0000000000000..429b58d7ea452
--- /dev/null
+++ b/unittests/Basic/SourceManagerTest.cpp
@@ -0,0 +1,296 @@
+//===- unittests/Basic/SourceManagerTest.cpp ------ SourceManager tests ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Config/config.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+// The test fixture.
+class SourceManagerTest : public ::testing::Test {
+protected:
+ SourceManagerTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ TargetOpts.Triple = "x86_64-apple-darwin11.1.0";
+ Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ LangOptions LangOpts;
+ TargetOptions TargetOpts;
+ IntrusiveRefCntPtr<TargetInfo> Target;
+};
+
+class VoidModuleLoader : public ModuleLoader {
+ virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
+ Module::NameVisibilityKind Visibility,
+ bool IsInclusionDirective) {
+ return 0;
+ }
+};
+
+TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
+ const char *source =
+ "#define M(x) [x]\n"
+ "M(foo)";
+ MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source);
+ FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(buf);
+
+ VoidModuleLoader ModLoader;
+ HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target);
+ Preprocessor PP(Diags, LangOpts,
+ Target.getPtr(),
+ SourceMgr, HeaderInfo, ModLoader,
+ /*IILookup =*/ 0,
+ /*OwnsHeaderSearch =*/false,
+ /*DelayInitialization =*/ false);
+ PP.EnterMainSourceFile();
+
+ std::vector<Token> toks;
+ while (1) {
+ Token tok;
+ PP.Lex(tok);
+ if (tok.is(tok::eof))
+ break;
+ toks.push_back(tok);
+ }
+
+ // Make sure we got the tokens that we expected.
+ ASSERT_EQ(3U, toks.size());
+ ASSERT_EQ(tok::l_square, toks[0].getKind());
+ ASSERT_EQ(tok::identifier, toks[1].getKind());
+ ASSERT_EQ(tok::r_square, toks[2].getKind());
+
+ SourceLocation lsqrLoc = toks[0].getLocation();
+ SourceLocation idLoc = toks[1].getLocation();
+ SourceLocation rsqrLoc = toks[2].getLocation();
+
+ SourceLocation macroExpStartLoc = SourceMgr.translateLineCol(mainFileID, 2, 1);
+ SourceLocation macroExpEndLoc = SourceMgr.translateLineCol(mainFileID, 2, 6);
+ ASSERT_TRUE(macroExpStartLoc.isFileID());
+ ASSERT_TRUE(macroExpEndLoc.isFileID());
+
+ SmallString<32> str;
+ ASSERT_EQ("M", PP.getSpelling(macroExpStartLoc, str));
+ ASSERT_EQ(")", PP.getSpelling(macroExpEndLoc, str));
+
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(lsqrLoc, idLoc));
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, rsqrLoc));
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(macroExpStartLoc, idLoc));
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc));
+}
+
+#if defined(LLVM_ON_UNIX)
+
+TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
+ const char *header =
+ "#define FM(x,y) x\n";
+
+ const char *main =
+ "#include \"/test-header.h\"\n"
+ "#define VAL 0\n"
+ "FM(VAL,0)\n"
+ "FM(0,VAL)\n"
+ "FM(FM(0,VAL),0)\n"
+ "#define CONCAT(X, Y) X##Y\n"
+ "CONCAT(1,1)\n";
+
+ MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header);
+ MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main);
+ FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(mainBuf);
+
+ const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h",
+ headerBuf->getBufferSize(), 0);
+ SourceMgr.overrideFileContents(headerFile, headerBuf);
+
+ VoidModuleLoader ModLoader;
+ HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target);
+ Preprocessor PP(Diags, LangOpts,
+ Target.getPtr(),
+ SourceMgr, HeaderInfo, ModLoader,
+ /*IILookup =*/ 0,
+ /*OwnsHeaderSearch =*/false,
+ /*DelayInitialization =*/ false);
+ PP.EnterMainSourceFile();
+
+ std::vector<Token> toks;
+ while (1) {
+ Token tok;
+ PP.Lex(tok);
+ if (tok.is(tok::eof))
+ break;
+ toks.push_back(tok);
+ }
+
+ // Make sure we got the tokens that we expected.
+ ASSERT_EQ(4U, toks.size());
+ ASSERT_EQ(tok::numeric_constant, toks[0].getKind());
+ ASSERT_EQ(tok::numeric_constant, toks[1].getKind());
+ ASSERT_EQ(tok::numeric_constant, toks[2].getKind());
+ ASSERT_EQ(tok::numeric_constant, toks[3].getKind());
+
+ SourceLocation defLoc = SourceMgr.translateLineCol(mainFileID, 2, 13);
+ SourceLocation loc1 = SourceMgr.translateLineCol(mainFileID, 3, 8);
+ SourceLocation loc2 = SourceMgr.translateLineCol(mainFileID, 4, 4);
+ SourceLocation loc3 = SourceMgr.translateLineCol(mainFileID, 5, 7);
+ SourceLocation defLoc2 = SourceMgr.translateLineCol(mainFileID, 6, 22);
+ defLoc = SourceMgr.getMacroArgExpandedLocation(defLoc);
+ loc1 = SourceMgr.getMacroArgExpandedLocation(loc1);
+ loc2 = SourceMgr.getMacroArgExpandedLocation(loc2);
+ loc3 = SourceMgr.getMacroArgExpandedLocation(loc3);
+ defLoc2 = SourceMgr.getMacroArgExpandedLocation(defLoc2);
+
+ EXPECT_TRUE(defLoc.isFileID());
+ EXPECT_TRUE(loc1.isFileID());
+ EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc2));
+ EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc3));
+ EXPECT_EQ(loc2, toks[1].getLocation());
+ EXPECT_EQ(loc3, toks[2].getLocation());
+ EXPECT_TRUE(defLoc2.isFileID());
+}
+
+namespace {
+
+struct MacroAction {
+ SourceLocation Loc;
+ std::string Name;
+ bool isDefinition; // if false, it is expansion.
+
+ MacroAction(SourceLocation Loc, StringRef Name, bool isDefinition)
+ : Loc(Loc), Name(Name), isDefinition(isDefinition) { }
+};
+
+class MacroTracker : public PPCallbacks {
+ std::vector<MacroAction> &Macros;
+
+public:
+ explicit MacroTracker(std::vector<MacroAction> &Macros) : Macros(Macros) { }
+
+ virtual void MacroDefined(const Token &MacroNameTok, const MacroInfo *MI) {
+ Macros.push_back(MacroAction(MI->getDefinitionLoc(),
+ MacroNameTok.getIdentifierInfo()->getName(),
+ true));
+ }
+ virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo* MI,
+ SourceRange Range) {
+ Macros.push_back(MacroAction(MacroNameTok.getLocation(),
+ MacroNameTok.getIdentifierInfo()->getName(),
+ false));
+ }
+};
+
+}
+
+TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) {
+ const char *header =
+ "#define MACRO_IN_INCLUDE 0\n";
+
+ const char *main =
+ "#define M(x) x\n"
+ "#define INC \"/test-header.h\"\n"
+ "#include M(INC)\n"
+ "#define INC2 </test-header.h>\n"
+ "#include M(INC2)\n";
+
+ MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header);
+ MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main);
+ SourceMgr.createMainFileIDForMemBuffer(mainBuf);
+
+ const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h",
+ headerBuf->getBufferSize(), 0);
+ SourceMgr.overrideFileContents(headerFile, headerBuf);
+
+ VoidModuleLoader ModLoader;
+ HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target);
+ Preprocessor PP(Diags, LangOpts,
+ Target.getPtr(),
+ SourceMgr, HeaderInfo, ModLoader,
+ /*IILookup =*/ 0,
+ /*OwnsHeaderSearch =*/false,
+ /*DelayInitialization =*/ false);
+
+ std::vector<MacroAction> Macros;
+ PP.addPPCallbacks(new MacroTracker(Macros));
+
+ PP.EnterMainSourceFile();
+
+ std::vector<Token> toks;
+ while (1) {
+ Token tok;
+ PP.Lex(tok);
+ if (tok.is(tok::eof))
+ break;
+ toks.push_back(tok);
+ }
+
+ // Make sure we got the tokens that we expected.
+ ASSERT_EQ(0U, toks.size());
+
+ ASSERT_EQ(9U, Macros.size());
+ // #define M(x) x
+ ASSERT_TRUE(Macros[0].isDefinition);
+ ASSERT_EQ("M", Macros[0].Name);
+ // #define INC "/test-header.h"
+ ASSERT_TRUE(Macros[1].isDefinition);
+ ASSERT_EQ("INC", Macros[1].Name);
+ // M expansion in #include M(INC)
+ ASSERT_FALSE(Macros[2].isDefinition);
+ ASSERT_EQ("M", Macros[2].Name);
+ // INC expansion in #include M(INC)
+ ASSERT_FALSE(Macros[3].isDefinition);
+ ASSERT_EQ("INC", Macros[3].Name);
+ // #define MACRO_IN_INCLUDE 0
+ ASSERT_TRUE(Macros[4].isDefinition);
+ ASSERT_EQ("MACRO_IN_INCLUDE", Macros[4].Name);
+ // #define INC2 </test-header.h>
+ ASSERT_TRUE(Macros[5].isDefinition);
+ ASSERT_EQ("INC2", Macros[5].Name);
+ // M expansion in #include M(INC2)
+ ASSERT_FALSE(Macros[6].isDefinition);
+ ASSERT_EQ("M", Macros[6].Name);
+ // INC2 expansion in #include M(INC2)
+ ASSERT_FALSE(Macros[7].isDefinition);
+ ASSERT_EQ("INC2", Macros[7].Name);
+ // #define MACRO_IN_INCLUDE 0
+ ASSERT_TRUE(Macros[8].isDefinition);
+ ASSERT_EQ("MACRO_IN_INCLUDE", Macros[8].Name);
+
+ // The INC expansion in #include M(INC) comes before the first
+ // MACRO_IN_INCLUDE definition of the included file.
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[3].Loc, Macros[4].Loc));
+
+ // The INC2 expansion in #include M(INC2) comes before the second
+ // MACRO_IN_INCLUDE definition of the included file.
+ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[7].Loc, Macros[8].Loc));
+}
+
+#endif
+
+} // anonymous namespace
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 901f167f35636..0b3eac95d4ee5 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -50,17 +50,24 @@ if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
add_definitions("-Wno-variadic-macros")
endif()
-add_clang_unittest(AST
- AST/APValueTest.cpp
- USED_LIBS gtest gtest_main clangAST
- )
-
add_clang_unittest(Basic
Basic/FileManagerTest.cpp
- USED_LIBS gtest gtest_main clangBasic
+ Basic/SourceManagerTest.cpp
+ USED_LIBS gtest gtest_main clangLex
+ )
+
+add_clang_unittest(Lex
+ Lex/LexerTest.cpp
+ USED_LIBS gtest gtest_main clangLex
)
add_clang_unittest(Frontend
Frontend/FrontendActionTest.cpp
USED_LIBS gtest gtest_main clangFrontend
)
+
+add_clang_unittest(Tooling
+ Tooling/CompilationDatabaseTest.cpp
+ Tooling/ToolingTest.cpp
+ USED_LIBS gtest gtest_main clangTooling
+ )
diff --git a/unittests/Frontend/FrontendActionTest.cpp b/unittests/Frontend/FrontendActionTest.cpp
index a32388a062e96..2d4befc1f4d38 100644
--- a/unittests/Frontend/FrontendActionTest.cpp
+++ b/unittests/Frontend/FrontendActionTest.cpp
@@ -55,8 +55,8 @@ TEST(ASTFrontendAction, Sanity) {
CompilerInvocation *invocation = new CompilerInvocation;
invocation->getPreprocessorOpts().addRemappedFile(
"test.cc", MemoryBuffer::getMemBuffer("int main() { float x; }"));
- invocation->getFrontendOpts().Inputs.push_back(
- std::make_pair(IK_CXX, "test.cc"));
+ invocation->getFrontendOpts().Inputs.push_back(FrontendInputFile("test.cc",
+ IK_CXX));
invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
CompilerInstance compiler;
diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile
index 7ec2b894a04b8..7c282d6b47f1a 100644
--- a/unittests/Frontend/Makefile
+++ b/unittests/Frontend/Makefile
@@ -13,7 +13,7 @@ LINK_COMPONENTS := support mc
USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \
- clangIndex.a clangARCMigrate.a clangRewrite.a \
+ clangIndex.a clangARCMigrate.a clangRewrite.a clangEdit.a \
clangAnalysis.a clangAST.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp
new file mode 100644
index 0000000000000..e43ad86ff597d
--- /dev/null
+++ b/unittests/Lex/LexerTest.cpp
@@ -0,0 +1,177 @@
+//===- unittests/Basic/LexerTest.cpp ------ Lexer tests -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Config/config.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+// The test fixture.
+class LexerTest : public ::testing::Test {
+protected:
+ LexerTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ TargetOpts.Triple = "x86_64-apple-darwin11.1.0";
+ Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ LangOptions LangOpts;
+ TargetOptions TargetOpts;
+ IntrusiveRefCntPtr<TargetInfo> Target;
+};
+
+class VoidModuleLoader : public ModuleLoader {
+ virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
+ Module::NameVisibilityKind Visibility,
+ bool IsInclusionDirective) {
+ return 0;
+ }
+};
+
+TEST_F(LexerTest, LexAPI) {
+ const char *source =
+ "#define M(x) [x]\n"
+ "#define N(x) x\n"
+ "#define INN(x) x\n"
+ "#define NOF1 INN(val)\n"
+ "#define NOF2 val\n"
+ "M(foo) N([bar])\n"
+ "N(INN(val)) N(NOF1) N(NOF2) N(val)";
+
+ MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source);
+ (void)SourceMgr.createMainFileIDForMemBuffer(buf);
+
+ VoidModuleLoader ModLoader;
+ HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr());
+ Preprocessor PP(Diags, LangOpts,
+ Target.getPtr(),
+ SourceMgr, HeaderInfo, ModLoader,
+ /*IILookup =*/ 0,
+ /*OwnsHeaderSearch =*/false,
+ /*DelayInitialization =*/ false);
+ PP.EnterMainSourceFile();
+
+ std::vector<Token> toks;
+ while (1) {
+ Token tok;
+ PP.Lex(tok);
+ if (tok.is(tok::eof))
+ break;
+ toks.push_back(tok);
+ }
+
+ // Make sure we got the tokens that we expected.
+ ASSERT_EQ(10U, toks.size());
+ ASSERT_EQ(tok::l_square, toks[0].getKind());
+ ASSERT_EQ(tok::identifier, toks[1].getKind());
+ ASSERT_EQ(tok::r_square, toks[2].getKind());
+ ASSERT_EQ(tok::l_square, toks[3].getKind());
+ ASSERT_EQ(tok::identifier, toks[4].getKind());
+ ASSERT_EQ(tok::r_square, toks[5].getKind());
+ ASSERT_EQ(tok::identifier, toks[6].getKind());
+ ASSERT_EQ(tok::identifier, toks[7].getKind());
+ ASSERT_EQ(tok::identifier, toks[8].getKind());
+ ASSERT_EQ(tok::identifier, toks[9].getKind());
+
+ SourceLocation lsqrLoc = toks[0].getLocation();
+ SourceLocation idLoc = toks[1].getLocation();
+ SourceLocation rsqrLoc = toks[2].getLocation();
+ std::pair<SourceLocation,SourceLocation>
+ macroPair = SourceMgr.getExpansionRange(lsqrLoc);
+ SourceRange macroRange = SourceRange(macroPair.first, macroPair.second);
+
+ SourceLocation Loc;
+ EXPECT_TRUE(Lexer::isAtStartOfMacroExpansion(lsqrLoc, SourceMgr, LangOpts, &Loc));
+ EXPECT_EQ(Loc, macroRange.getBegin());
+ EXPECT_FALSE(Lexer::isAtStartOfMacroExpansion(idLoc, SourceMgr, LangOpts));
+ EXPECT_FALSE(Lexer::isAtEndOfMacroExpansion(idLoc, SourceMgr, LangOpts));
+ EXPECT_TRUE(Lexer::isAtEndOfMacroExpansion(rsqrLoc, SourceMgr, LangOpts, &Loc));
+ EXPECT_EQ(Loc, macroRange.getEnd());
+
+ CharSourceRange range = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(lsqrLoc, idLoc), SourceMgr, LangOpts);
+ EXPECT_TRUE(range.isInvalid());
+ range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(idLoc, rsqrLoc),
+ SourceMgr, LangOpts);
+ EXPECT_TRUE(range.isInvalid());
+ range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(lsqrLoc, rsqrLoc),
+ SourceMgr, LangOpts);
+ EXPECT_TRUE(!range.isTokenRange());
+ EXPECT_EQ(range.getAsRange(),
+ SourceRange(macroRange.getBegin(),
+ macroRange.getEnd().getLocWithOffset(1)));
+
+ StringRef text = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(lsqrLoc, rsqrLoc),
+ SourceMgr, LangOpts);
+ EXPECT_EQ(text, "M(foo)");
+
+ SourceLocation macroLsqrLoc = toks[3].getLocation();
+ SourceLocation macroIdLoc = toks[4].getLocation();
+ SourceLocation macroRsqrLoc = toks[5].getLocation();
+ SourceLocation fileLsqrLoc = SourceMgr.getSpellingLoc(macroLsqrLoc);
+ SourceLocation fileIdLoc = SourceMgr.getSpellingLoc(macroIdLoc);
+ SourceLocation fileRsqrLoc = SourceMgr.getSpellingLoc(macroRsqrLoc);
+
+ range = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(macroLsqrLoc, macroIdLoc),
+ SourceMgr, LangOpts);
+ EXPECT_EQ(SourceRange(fileLsqrLoc, fileIdLoc.getLocWithOffset(3)),
+ range.getAsRange());
+
+ range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(macroIdLoc, macroRsqrLoc),
+ SourceMgr, LangOpts);
+ EXPECT_EQ(SourceRange(fileIdLoc, fileRsqrLoc.getLocWithOffset(1)),
+ range.getAsRange());
+
+ macroPair = SourceMgr.getExpansionRange(macroLsqrLoc);
+ range = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(macroLsqrLoc, macroRsqrLoc),
+ SourceMgr, LangOpts);
+ EXPECT_EQ(SourceRange(macroPair.first, macroPair.second.getLocWithOffset(1)),
+ range.getAsRange());
+
+ text = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SourceRange(macroLsqrLoc, macroIdLoc)),
+ SourceMgr, LangOpts);
+ EXPECT_EQ(text, "[bar");
+
+
+ SourceLocation idLoc1 = toks[6].getLocation();
+ SourceLocation idLoc2 = toks[7].getLocation();
+ SourceLocation idLoc3 = toks[8].getLocation();
+ SourceLocation idLoc4 = toks[9].getLocation();
+ EXPECT_EQ("INN", Lexer::getImmediateMacroName(idLoc1, SourceMgr, LangOpts));
+ EXPECT_EQ("INN", Lexer::getImmediateMacroName(idLoc2, SourceMgr, LangOpts));
+ EXPECT_EQ("NOF2", Lexer::getImmediateMacroName(idLoc3, SourceMgr, LangOpts));
+ EXPECT_EQ("N", Lexer::getImmediateMacroName(idLoc4, SourceMgr, LangOpts));
+}
+
+} // anonymous namespace
diff --git a/unittests/AST/Makefile b/unittests/Lex/Makefile
index 74191d037f51f..bb9c6bc3901db 100644
--- a/unittests/AST/Makefile
+++ b/unittests/Lex/Makefile
@@ -1,4 +1,4 @@
-##===- unittests/Frontend/Makefile -------------------------*- Makefile -*-===##
+##===- unittests/Lex/Makefile ------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
@@ -8,8 +8,8 @@
##===----------------------------------------------------------------------===##
CLANG_LEVEL = ../..
-TESTNAME = AST
+TESTNAME = Lex
LINK_COMPONENTS := support mc
-USEDLIBS = clangAST.a clangBasic.a
+USEDLIBS = clangLex.a clangBasic.a
include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Lex/PreprocessingRecordTest.cpp b/unittests/Lex/PreprocessingRecordTest.cpp
new file mode 100644
index 0000000000000..5b5d933d1b1da
--- /dev/null
+++ b/unittests/Lex/PreprocessingRecordTest.cpp
@@ -0,0 +1,139 @@
+//===- unittests/Lex/PreprocessingRecordTest.cpp - PreprocessingRecord tests =//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessingRecord.h"
+#include "llvm/Config/config.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+// The test fixture.
+class PreprocessingRecordTest : public ::testing::Test {
+protected:
+ PreprocessingRecordTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ TargetOpts.Triple = "x86_64-apple-darwin11.1.0";
+ Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ LangOptions LangOpts;
+ TargetOptions TargetOpts;
+ IntrusiveRefCntPtr<TargetInfo> Target;
+};
+
+class VoidModuleLoader : public ModuleLoader {
+ virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
+ Module::NameVisibilityKind Visibility,
+ bool IsInclusionDirective) {
+ return 0;
+ }
+};
+
+TEST_F(PreprocessingRecordTest, PPRecAPI) {
+ const char *source =
+ "0 1\n"
+ "#if 1\n"
+ "2\n"
+ "#ifndef BB\n"
+ "3 4\n"
+ "#else\n"
+ "#endif\n"
+ "5\n"
+ "#endif\n"
+ "6\n"
+ "#if 1\n"
+ "7\n"
+ "#if 1\n"
+ "#endif\n"
+ "8\n"
+ "#endif\n"
+ "9\n";
+
+ MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source);
+ SourceMgr.createMainFileIDForMemBuffer(buf);
+
+ VoidModuleLoader ModLoader;
+ HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr());
+ Preprocessor PP(Diags, LangOpts,
+ Target.getPtr(),
+ SourceMgr, HeaderInfo, ModLoader,
+ /*IILookup =*/ 0,
+ /*OwnsHeaderSearch =*/false,
+ /*DelayInitialization =*/ false);
+ PP.createPreprocessingRecord(true);
+ PP.EnterMainSourceFile();
+
+ std::vector<Token> toks;
+ while (1) {
+ Token tok;
+ PP.Lex(tok);
+ if (tok.is(tok::eof))
+ break;
+ toks.push_back(tok);
+ }
+
+ // Make sure we got the tokens that we expected.
+ ASSERT_EQ(10U, toks.size());
+
+ PreprocessingRecord &PPRec = *PP.getPreprocessingRecord();
+ EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[0].getLocation(), toks[1].getLocation())));
+ EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[0].getLocation(), toks[2].getLocation())));
+ EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[3].getLocation(), toks[4].getLocation())));
+ EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[1].getLocation(), toks[5].getLocation())));
+ EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[2].getLocation(), toks[6].getLocation())));
+ EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[2].getLocation(), toks[5].getLocation())));
+ EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[0].getLocation(), toks[6].getLocation())));
+ EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[2].getLocation(), toks[8].getLocation())));
+ EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective(
+ SourceRange(toks[0].getLocation(), toks[9].getLocation())));
+
+ EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[0].getLocation(), toks[2].getLocation()));
+ EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[3].getLocation(), toks[4].getLocation()));
+ EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[1].getLocation(), toks[5].getLocation()));
+ EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[2].getLocation(), toks[0].getLocation()));
+ EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[4].getLocation(), toks[3].getLocation()));
+ EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion(
+ toks[5].getLocation(), toks[1].getLocation()));
+}
+
+} // anonymous namespace
diff --git a/unittests/Makefile b/unittests/Makefile
index f4ce6adaa7251..05449d8ccf157 100644
--- a/unittests/Makefile
+++ b/unittests/Makefile
@@ -14,7 +14,7 @@ ifndef CLANG_LEVEL
IS_UNITTEST_LEVEL := 1
CLANG_LEVEL := ..
-PARALLEL_DIRS = AST Basic Frontend
+PARALLEL_DIRS = Basic Frontend Lex Tooling
endif # CLANG_LEVEL
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
new file mode 100644
index 0000000000000..9747de25548c4
--- /dev/null
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -0,0 +1,223 @@
+//===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
+ StringRef JSONDatabase,
+ std::string &ErrorMessage) {
+ llvm::OwningPtr<CompilationDatabase> Database(
+ JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
+ if (!Database)
+ return CompileCommand();
+ std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
+ EXPECT_LE(Commands.size(), 1u);
+ if (Commands.empty())
+ return CompileCommand();
+ return Commands[0];
+}
+
+TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
+ std::string ErrorMessage;
+ CompileCommand NotFound = findCompileArgsInJsonDatabase(
+ "a-file.cpp", "", ErrorMessage);
+ EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
+ EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
+}
+
+TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
+ StringRef Directory("/some/directory");
+ StringRef FileName("/path/to/a-file.cpp");
+ StringRef Command("/path/to/compiler and some arguments");
+ std::string ErrorMessage;
+ CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+ FileName,
+ ("[{\"directory\":\"" + Directory + "\"," +
+ "\"command\":\"" + Command + "\","
+ "\"file\":\"" + FileName + "\"}]").str(),
+ ErrorMessage);
+ EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
+ ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
+ EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
+ EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
+ EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
+ EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
+
+ CompileCommand NotFound = findCompileArgsInJsonDatabase(
+ "a-file.cpp",
+ ("[{\"directory\":\"" + Directory + "\"," +
+ "\"command\":\"" + Command + "\","
+ "\"file\":\"" + FileName + "\"}]").str(),
+ ErrorMessage);
+ EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
+ EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
+}
+
+TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
+ StringRef Directory("/some/directory");
+ StringRef FileName("/path/to/a-file.cpp");
+ StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
+ std::string ErrorMessage;
+ CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+ FileName,
+ ("[{\"directory\":\"" + Directory + "\"," +
+ "\"command\":\"" + Command + "\","
+ "\"file\":\"" + FileName + "\"}]").str(),
+ ErrorMessage);
+ ASSERT_EQ(2u, FoundCommand.CommandLine.size());
+ EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
+ EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
+}
+
+TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
+ StringRef Directory("/some directory / with spaces");
+ StringRef FileName("/path/to/a-file.cpp");
+ StringRef Command("a command");
+ std::string ErrorMessage;
+ CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+ FileName,
+ ("[{\"directory\":\"" + Directory + "\"," +
+ "\"command\":\"" + Command + "\","
+ "\"file\":\"" + FileName + "\"}]").str(),
+ ErrorMessage);
+ EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
+}
+
+TEST(findCompileArgsInJsonDatabase, FindsEntry) {
+ StringRef Directory("directory");
+ StringRef FileName("file");
+ StringRef Command("command");
+ std::string JsonDatabase = "[";
+ for (int I = 0; I < 10; ++I) {
+ if (I > 0) JsonDatabase += ",";
+ JsonDatabase +=
+ ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
+ "\"command\":\"" + Command + Twine(I) + "\","
+ "\"file\":\"" + FileName + Twine(I) + "\"}").str();
+ }
+ JsonDatabase += "]";
+ std::string ErrorMessage;
+ CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+ "file4", JsonDatabase, ErrorMessage);
+ EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage;
+ ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
+ EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
+}
+
+static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
+ std::string JsonDatabase =
+ ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" +
+ Command + "\"}]").str();
+ std::string ErrorMessage;
+ CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
+ "test", JsonDatabase, ErrorMessage);
+ EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
+ return FoundCommand.CommandLine;
+}
+
+TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
+ std::vector<std::string> Result = unescapeJsonCommandLine("");
+ EXPECT_TRUE(Result.empty());
+}
+
+TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
+ std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
+ ASSERT_EQ(3ul, Result.size());
+ EXPECT_EQ("a", Result[0]);
+ EXPECT_EQ("b", Result[1]);
+ EXPECT_EQ("c", Result[2]);
+}
+
+TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
+ std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
+ ASSERT_EQ(2ul, Result.size());
+ EXPECT_EQ("a", Result[0]);
+ EXPECT_EQ("b", Result[1]);
+}
+
+TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
+ std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
+ ASSERT_EQ(1ul, Backslash.size());
+ EXPECT_EQ("a\\", Backslash[0]);
+ std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
+ ASSERT_EQ(1ul, Quote.size());
+ EXPECT_EQ("a\"", Quote[0]);
+}
+
+TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
+ std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
+ ASSERT_EQ(1ul, Result.size());
+ EXPECT_EQ(" a b ", Result[0]);
+}
+
+TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
+ std::vector<std::string> Result = unescapeJsonCommandLine(
+ " \\\" a \\\" \\\" b \\\" ");
+ ASSERT_EQ(2ul, Result.size());
+ EXPECT_EQ(" a ", Result[0]);
+ EXPECT_EQ(" b ", Result[1]);
+}
+
+TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
+ std::vector<std::string> Result = unescapeJsonCommandLine(
+ "\\\"\\\"\\\"\\\"");
+ ASSERT_EQ(1ul, Result.size());
+ EXPECT_TRUE(Result[0].empty()) << Result[0];
+}
+
+TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
+ std::vector<std::string> Result = unescapeJsonCommandLine(
+ "\\\"\\\\\\\"\\\"");
+ ASSERT_EQ(1ul, Result.size());
+ EXPECT_EQ("\"", Result[0]);
+}
+
+TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
+ std::vector<std::string> Result = unescapeJsonCommandLine(
+ " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
+ ASSERT_EQ(4ul, Result.size());
+ EXPECT_EQ("\"", Result[0]);
+ EXPECT_EQ("a \" b ", Result[1]);
+ EXPECT_EQ("and\\c", Result[2]);
+ EXPECT_EQ("\"", Result[3]);
+}
+
+TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
+ std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
+ "\\\"a\\\"\\\"b\\\"");
+ ASSERT_EQ(1ul, QuotedNoSpaces.size());
+ EXPECT_EQ("ab", QuotedNoSpaces[0]);
+
+ std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
+ "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
+ ASSERT_EQ(1ul, MixedNoSpaces.size());
+ EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
+}
+
+TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
+ std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
+ ASSERT_EQ(1ul, Unclosed.size());
+ EXPECT_EQ("abc", Unclosed[0]);
+
+ std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
+ ASSERT_EQ(1ul, Empty.size());
+ EXPECT_EQ("", Empty[0]);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
new file mode 100644
index 0000000000000..0829da543d0b0
--- /dev/null
+++ b/unittests/Tooling/Makefile
@@ -0,0 +1,17 @@
+##===- unittests/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 = ../..
+TESTNAME = Tooling
+LINK_COMPONENTS := support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+ clangParse.a clangSema.a clangAnalysis.a clangEdit.a clangAST.a \
+ clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
new file mode 100644
index 0000000000000..c7b2210a754ea
--- /dev/null
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -0,0 +1,113 @@
+//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+namespace {
+/// Takes an ast consumer and returns it from CreateASTConsumer. This only
+/// works with single translation unit compilations.
+class TestAction : public clang::ASTFrontendAction {
+ public:
+ /// Takes ownership of TestConsumer.
+ explicit TestAction(clang::ASTConsumer *TestConsumer)
+ : TestConsumer(TestConsumer) {}
+
+ protected:
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ clang::CompilerInstance& compiler, StringRef dummy) {
+ /// TestConsumer will be deleted by the framework calling us.
+ return TestConsumer;
+ }
+
+ private:
+ clang::ASTConsumer * const TestConsumer;
+};
+
+class FindTopLevelDeclConsumer : public clang::ASTConsumer {
+ public:
+ explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl)
+ : FoundTopLevelDecl(FoundTopLevelDecl) {}
+ virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) {
+ *FoundTopLevelDecl = true;
+ return true;
+ }
+ private:
+ bool * const FoundTopLevelDecl;
+};
+} // end namespace
+
+TEST(runToolOnCode, FindsTopLevelDeclOnEmptyCode) {
+ bool FoundTopLevelDecl = false;
+ EXPECT_TRUE(runToolOnCode(
+ new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
+ EXPECT_TRUE(FoundTopLevelDecl);
+}
+
+namespace {
+class FindClassDeclXConsumer : public clang::ASTConsumer {
+ public:
+ FindClassDeclXConsumer(bool *FoundClassDeclX)
+ : FoundClassDeclX(FoundClassDeclX) {}
+ virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
+ if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(
+ *GroupRef.begin())) {
+ if (Record->getName() == "X") {
+ *FoundClassDeclX = true;
+ }
+ }
+ return true;
+ }
+ private:
+ bool *FoundClassDeclX;
+};
+} // end namespace
+
+TEST(runToolOnCode, FindsClassDecl) {
+ bool FoundClassDeclX = false;
+ EXPECT_TRUE(runToolOnCode(new TestAction(
+ new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;"));
+ EXPECT_TRUE(FoundClassDeclX);
+
+ FoundClassDeclX = false;
+ EXPECT_TRUE(runToolOnCode(new TestAction(
+ new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;"));
+ EXPECT_FALSE(FoundClassDeclX);
+}
+
+TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
+ llvm::OwningPtr<FrontendActionFactory> Factory(
+ newFrontendActionFactory<SyntaxOnlyAction>());
+ llvm::OwningPtr<FrontendAction> Action(Factory->create());
+ EXPECT_TRUE(Action.get() != NULL);
+}
+
+struct IndependentFrontendActionCreator {
+ FrontendAction *newFrontendAction() { return new SyntaxOnlyAction; }
+};
+
+TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
+ IndependentFrontendActionCreator Creator;
+ llvm::OwningPtr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Creator));
+ llvm::OwningPtr<FrontendAction> Action(Factory->create());
+ EXPECT_TRUE(Action.get() != NULL);
+}
+
+} // end namespace tooling
+} // end namespace clang