aboutsummaryrefslogtreecommitdiff
path: root/unittests/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2012-12-02 13:20:44 +0000
committerDimitry Andric <dim@FreeBSD.org>2012-12-02 13:20:44 +0000
commit13cc256e404620c1de0cbcc4e43ce1e2dbbc4898 (patch)
tree2732d02d7d51218d6eed98ac7fcfc5b8794896b5 /unittests/Tooling
parent657bc3d9848e3be92029b2416031340988cd0111 (diff)
downloadsrc-13cc256e404620c1de0cbcc4e43ce1e2dbbc4898.tar.gz
src-13cc256e404620c1de0cbcc4e43ce1e2dbbc4898.zip
Vendor import of clang release_32 branch r168974 (effectively, 3.2 RC2):vendor/clang/clang-release_32-r168974
Notes
Notes: svn path=/vendor/clang/dist/; revision=243791 svn path=/vendor/clang/clang-release_32-r168974/; revision=243792; tag=vendor/clang/clang-release_32-r168974
Diffstat (limited to 'unittests/Tooling')
-rw-r--r--unittests/Tooling/CMakeLists.txt2
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp123
-rw-r--r--unittests/Tooling/Makefile3
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp68
-rw-r--r--unittests/Tooling/RefactoringCallbacksTest.cpp16
-rw-r--r--unittests/Tooling/RefactoringTest.cpp4
-rw-r--r--unittests/Tooling/RewriterTestContext.h11
-rw-r--r--unittests/Tooling/TestVisitor.h161
-rw-r--r--unittests/Tooling/ToolingTest.cpp32
9 files changed, 340 insertions, 80 deletions
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
index 4eaf33956f3b..bd7317fe4ae8 100644
--- a/unittests/Tooling/CMakeLists.txt
+++ b/unittests/Tooling/CMakeLists.txt
@@ -18,5 +18,5 @@ add_clang_unittest(ToolingTests
target_link_libraries(ToolingTests
clangAST
clangTooling
- clangRewrite
+ clangRewriteCore
)
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 591d48dbbd61..5ed4240c1ee0 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -11,8 +11,10 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Frontend/FrontendAction.h"
-#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/FileMatchTrie.h"
+#include "clang/Tooling/JSONCompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/PathV2.h"
#include "gtest/gtest.h"
namespace clang {
@@ -55,13 +57,16 @@ TEST(JSONCompilationDatabase, GetAllFiles) {
getAllFiles("[]", ErrorMessage)) << ErrorMessage;
std::vector<std::string> expected_files;
- expected_files.push_back("file1");
- expected_files.push_back("file2");
+ SmallString<16> PathStorage;
+ llvm::sys::path::native("//net/dir/file1", PathStorage);
+ expected_files.push_back(PathStorage.str());
+ llvm::sys::path::native("//net/dir/file2", PathStorage);
+ expected_files.push_back(PathStorage.str());
EXPECT_EQ(expected_files, getAllFiles(
- "[{\"directory\":\"dir\","
+ "[{\"directory\":\"//net/dir\","
"\"command\":\"command\","
"\"file\":\"file1\"},"
- " {\"directory\":\"dir\","
+ " {\"directory\":\"//net/dir\","
"\"command\":\"command\","
"\"file\":\"file2\"}]",
ErrorMessage)) << ErrorMessage;
@@ -81,6 +86,82 @@ static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
return Commands[0];
}
+struct FakeComparator : public PathComparator {
+ virtual ~FakeComparator() {}
+ virtual bool equivalent(StringRef FileA, StringRef FileB) const {
+ return FileA.equals_lower(FileB);
+ }
+};
+
+class FileMatchTrieTest : public ::testing::Test {
+protected:
+ FileMatchTrieTest() : Trie(new FakeComparator()) {}
+
+ StringRef find(StringRef Path) {
+ llvm::raw_string_ostream ES(Error);
+ return Trie.findEquivalent(Path, ES);
+ }
+
+ FileMatchTrie Trie;
+ std::string Error;
+};
+
+TEST_F(FileMatchTrieTest, InsertingRelativePath) {
+ Trie.insert("//net/path/file.cc");
+ Trie.insert("file.cc");
+ EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
+}
+
+TEST_F(FileMatchTrieTest, MatchingRelativePath) {
+ EXPECT_EQ("", find("file.cc"));
+}
+
+TEST_F(FileMatchTrieTest, ReturnsBestResults) {
+ Trie.insert("//net/d/c/b.cc");
+ Trie.insert("//net/d/b/b.cc");
+ EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
+}
+
+TEST_F(FileMatchTrieTest, HandlesSymlinks) {
+ Trie.insert("//net/AA/file.cc");
+ EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
+}
+
+TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
+ Trie.insert("//net/Aa/file.cc");
+ Trie.insert("//net/aA/file.cc");
+ EXPECT_TRUE(find("//net/aa/file.cc").empty());
+ EXPECT_EQ("Path is ambiguous", Error);
+}
+
+TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
+ Trie.insert("//net/src/Aa/file.cc");
+ Trie.insert("//net/src/aA/file.cc");
+ Trie.insert("//net/SRC/aa/file.cc");
+ EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
+}
+
+TEST_F(FileMatchTrieTest, EmptyTrie) {
+ EXPECT_TRUE(find("//net/some/path").empty());
+}
+
+TEST_F(FileMatchTrieTest, NoResult) {
+ Trie.insert("//net/somepath/otherfile.cc");
+ Trie.insert("//net/otherpath/somefile.cc");
+ EXPECT_EQ("", find("//net/somepath/somefile.cc"));
+}
+
+TEST_F(FileMatchTrieTest, RootElementDifferent) {
+ Trie.insert("//net/path/file.cc");
+ Trie.insert("//net/otherpath/file.cc");
+ EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
+}
+
+TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
+ EXPECT_EQ("", find("relative-path.cc"));
+ EXPECT_EQ("Cannot resolve relative paths", Error);
+}
+
TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
std::string ErrorMessage;
CompileCommand NotFound = findCompileArgsInJsonDatabase(
@@ -90,9 +171,9 @@ TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
}
TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
- StringRef Directory("/some/directory");
- StringRef FileName("/path/to/a-file.cpp");
- StringRef Command("/path/to/compiler and some arguments");
+ StringRef Directory("//net/some/directory");
+ StringRef FileName("//net/path/to/a-file.cpp");
+ StringRef Command("//net/path/to/compiler and some arguments");
std::string ErrorMessage;
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
FileName,
@@ -102,7 +183,8 @@ TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
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("//net/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;
@@ -118,9 +200,9 @@ TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
}
TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
- StringRef Directory("/some/directory");
- StringRef FileName("/path/to/a-file.cpp");
- StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
+ StringRef Directory("//net/some/directory");
+ StringRef FileName("//net/path/to/a-file.cpp");
+ StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
std::string ErrorMessage;
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
FileName,
@@ -129,13 +211,14 @@ TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
"\"file\":\"" + FileName + "\"}]").str(),
ErrorMessage);
ASSERT_EQ(2u, FoundCommand.CommandLine.size());
- EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
+ EXPECT_EQ("//net/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 Directory("//net/some directory / with spaces");
+ StringRef FileName("//net/path/to/a-file.cpp");
StringRef Command("a command");
std::string ErrorMessage;
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
@@ -148,7 +231,7 @@ TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
}
TEST(findCompileArgsInJsonDatabase, FindsEntry) {
- StringRef Directory("directory");
+ StringRef Directory("//net/directory");
StringRef FileName("file");
StringRef Command("command");
std::string JsonDatabase = "[";
@@ -162,19 +245,19 @@ TEST(findCompileArgsInJsonDatabase, FindsEntry) {
JsonDatabase += "]";
std::string ErrorMessage;
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
- "file4", JsonDatabase, ErrorMessage);
- EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage;
+ "//net/directory4/file4", JsonDatabase, ErrorMessage);
+ EXPECT_EQ("//net/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\": \"" +
+ ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
Command + "\"}]").str();
std::string ErrorMessage;
CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
- "test", JsonDatabase, ErrorMessage);
+ "//net/root/test", JsonDatabase, ErrorMessage);
EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
return FoundCommand.CommandLine;
}
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
index 5d2224d40e15..5ed99fcc430b 100644
--- a/unittests/Tooling/Makefile
+++ b/unittests/Tooling/Makefile
@@ -12,7 +12,8 @@ TESTNAME = Tooling
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
- clangParse.a clangRewrite.a clangSema.a clangAnalysis.a clangEdit.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
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
index 4b539067b136..a68a869bf55d 100644
--- a/unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -385,6 +385,66 @@ TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) {
"int main() { Simple s; Simple t(s); }\n"));
}
+/// \brief A visitor that optionally includes implicit code and matches
+/// CXXConstructExpr.
+///
+/// The name recorded for the match is the name of the class whose constructor
+/// is invoked by the CXXConstructExpr, not the name of the class whose
+/// constructor the CXXConstructExpr is contained in.
+class ConstructExprVisitor
+ : public ExpectedLocationVisitor<ConstructExprVisitor> {
+public:
+ ConstructExprVisitor() : ShouldVisitImplicitCode(false) {}
+
+ bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
+
+ void setShouldVisitImplicitCode(bool NewValue) {
+ ShouldVisitImplicitCode = NewValue;
+ }
+
+ bool VisitCXXConstructExpr(CXXConstructExpr* Expr) {
+ if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) {
+ if (const CXXRecordDecl* Class = Ctor->getParent()) {
+ Match(Class->getName(), Expr->getLocation());
+ }
+ }
+ return true;
+ }
+
+ private:
+ bool ShouldVisitImplicitCode;
+};
+
+TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) {
+ ConstructExprVisitor Visitor;
+ Visitor.setShouldVisitImplicitCode(true);
+ Visitor.ExpectMatch("WithCtor", 2, 8);
+ // Simple has a constructor that implicitly initializes 'w'. Test
+ // that a visitor that visits implicit code visits that initialization.
+ // Note: Clang lazily instantiates implicit declarations, so we need
+ // to use them in order to force them to appear in the AST.
+ EXPECT_TRUE(Visitor.runOver(
+ "struct WithCtor { WithCtor(); }; \n"
+ "struct Simple { WithCtor w; }; \n"
+ "int main() { Simple s; }\n"));
+}
+
+// The same as CanVisitImplicitMemberInitializations, but checking that the
+// visits are omitted when the visitor does not include implicit code.
+TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
+ ConstructExprVisitor Visitor;
+ Visitor.setShouldVisitImplicitCode(false);
+ Visitor.DisallowMatch("WithCtor", 2, 8);
+ // Simple has a constructor that implicitly initializes 'w'. Test
+ // that a visitor that skips implicit code skips that initialization.
+ // Note: Clang lazily instantiates implicit declarations, so we need
+ // to use them in order to force them to appear in the AST.
+ EXPECT_TRUE(Visitor.runOver(
+ "struct WithCtor { WithCtor(); }; \n"
+ "struct Simple { WithCtor w; }; \n"
+ "int main() { Simple s; }\n"));
+}
+
TEST(RecursiveASTVisitor, VisitsExtension) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("s", 1, 24);
@@ -392,4 +452,12 @@ TEST(RecursiveASTVisitor, VisitsExtension) {
"int s = __extension__ (s);\n"));
}
+TEST(RecursiveASTVisitor, VisitsCompoundLiteralType) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("struct S", 1, 26);
+ EXPECT_TRUE(Visitor.runOver(
+ "int f() { return (struct S { int a; }){.a = 0}.a; }",
+ TypeLocVisitor::Lang_C));
+}
+
} // end namespace clang
diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp
index 00eb193d74c0..4e30cfde26ab 100644
--- a/unittests/Tooling/RefactoringCallbacksTest.cpp
+++ b/unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -10,8 +10,8 @@
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/RefactoringCallbacks.h"
-#include "gtest/gtest.h"
#include "RewriterTestContext.h"
+#include "gtest/gtest.h"
namespace clang {
namespace tooling {
@@ -40,28 +40,28 @@ TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { ; }";
ReplaceStmtWithText Callback("id", ";");
- expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+ expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
std::string Code = "#define A void f() { int i = 1; }\nA";
std::string Expected = "#define A void f() { ; }\nA";
ReplaceStmtWithText Callback("id", ";");
- expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+ expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
std::string Code = "#define A void f() { int i = 1; }";
std::string Expected = "#define A void f() { int i = 1; }";
ReplaceStmtWithText Callback("id", ";");
- expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+ expectRewritten(Code, Expected, id("id", declStmt()), Callback);
}
TEST(RefactoringCallbacksTest, ReplacesInteger) {
std::string Code = "void f() { int i = 1; }";
std::string Expected = "void f() { int i = 2; }";
ReplaceStmtWithText Callback("id", "2");
- expectRewritten(Code, Expected, id("id", expression(integerLiteral())),
+ expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
Callback);
}
@@ -72,7 +72,7 @@ TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
expectRewritten(Code, Expected,
id("always-false", conditionalOperator(
hasCondition(boolLiteral(equals(false))),
- hasFalseExpression(id("should-be", expression())))),
+ hasFalseExpression(id("should-be", expr())))),
Callback);
}
@@ -82,8 +82,8 @@ TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
ReplaceIfStmtWithItsBody Callback("id", true);
expectRewritten(Code, Expected,
id("id", ifStmt(
- hasCondition(implicitCast(hasSourceExpression(
- declarationReference(to(variable(hasName("a"))))))))),
+ hasCondition(implicitCastExpr(hasSourceExpression(
+ declRefExpr(to(varDecl(hasName("a"))))))))),
Callback);
}
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
index 8d9695590af0..ff278bfd52d5 100644
--- a/unittests/Tooling/RefactoringTest.cpp
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -15,14 +15,14 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
-#include "clang/Rewrite/Rewriter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Path.h"
diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h
index f68be6b4576a..d790ac103514 100644
--- a/unittests/Tooling/RewriterTestContext.h
+++ b/unittests/Tooling/RewriterTestContext.h
@@ -15,12 +15,12 @@
#define LLVM_CLANG_REWRITER_TEST_CONTEXT_H
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
-#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
-#include "clang/Rewrite/Rewriter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -35,8 +35,10 @@ namespace clang {
class RewriterTestContext {
public:
RewriterTestContext()
- : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()),
- DiagnosticPrinter(llvm::outs(), DiagnosticOptions()),
+ : DiagOpts(new DiagnosticOptions()),
+ Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+ &*DiagOpts),
+ DiagnosticPrinter(llvm::outs(), &*DiagOpts),
Files((FileSystemOptions())),
Sources(Diagnostics, Files),
Rewrite(Sources, Options) {
@@ -109,6 +111,7 @@ class RewriterTestContext {
return Files.getBufferForFile(Path, NULL)->getBuffer();
}
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
DiagnosticsEngine Diagnostics;
TextDiagnosticPrinter DiagnosticPrinter;
FileManager Files;
diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h
index d439d81d89ef..8333c24a6880 100644
--- a/unittests/Tooling/TestVisitor.h
+++ b/unittests/Tooling/TestVisitor.h
@@ -6,14 +6,17 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines a utility class for RecursiveASTVisitor related tests.
-//
+///
+/// \file
+/// \brief Defines utility templates for RecursiveASTVisitor related tests.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TEST_VISITOR_H
#define LLVM_CLANG_TEST_VISITOR_H
+#include <vector>
+
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
@@ -29,7 +32,7 @@ namespace clang {
/// This is a drop-in replacement for RecursiveASTVisitor itself, with the
/// additional capability of running it over a snippet of code.
///
-/// Visits template instantiations by default.
+/// Visits template instantiations (but not implicit code) by default.
template <typename T>
class TestVisitor : public RecursiveASTVisitor<T> {
public:
@@ -37,9 +40,16 @@ public:
virtual ~TestVisitor() { }
+ enum Language { Lang_C, Lang_CXX };
+
/// \brief Runs the current AST visitor over the given code.
- bool runOver(StringRef Code) {
- return tooling::runToolOnCode(CreateTestAction(), Code);
+ bool runOver(StringRef Code, Language L = Lang_CXX) {
+ std::vector<std::string> Args;
+ switch (L) {
+ case Lang_C: Args.push_back("-std=c99"); break;
+ case Lang_CXX: Args.push_back("-std=c++98"); break;
+ }
+ return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
}
bool shouldVisitTemplateInstantiations() const {
@@ -81,63 +91,126 @@ protected:
ASTContext *Context;
};
-
-/// \brief A RecursiveASTVisitor for testing the RecursiveASTVisitor itself.
+/// \brief A RecursiveASTVisitor to check that certain matches are (or are
+/// not) observed during visitation.
///
-/// Allows simple creation of test visitors running matches on only a small
+/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
+/// and allows simple creation of test visitors running matches on only a small
/// subset of the Visit* methods.
template <typename T, template <typename> class Visitor = TestVisitor>
class ExpectedLocationVisitor : public Visitor<T> {
public:
- ExpectedLocationVisitor()
- : ExpectedLine(0), ExpectedColumn(0), Found(false) {}
-
- virtual ~ExpectedLocationVisitor() {
- EXPECT_TRUE(Found)
- << "Expected \"" << ExpectedMatch << "\" at " << ExpectedLine
- << ":" << ExpectedColumn << PartialMatches;
+ /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
+ ///
+ /// Any number of matches can be disallowed.
+ void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
+ DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
}
/// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
+ ///
+ /// Any number of expected matches can be set by calling this repeatedly.
+ /// Each is expected to be matched exactly once.
void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
- ExpectedMatch = Match.str();
- ExpectedLine = Line;
- ExpectedColumn = Column;
+ ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
+ }
+
+ /// \brief Checks that all expected matches have been found.
+ virtual ~ExpectedLocationVisitor() {
+ for (typename std::vector<ExpectedMatch>::const_iterator
+ It = ExpectedMatches.begin(), End = ExpectedMatches.end();
+ It != End; ++It) {
+ It->ExpectFound();
+ }
}
protected:
- /// \brief Convenience method to simplify writing test visitors.
- ///
- /// Sets 'Found' to true if 'Name' and 'Location' match the expected
- /// values. If only a partial match is found, record the information
- /// to produce nice error output when a test fails.
+ /// \brief Checks an actual match against expected and disallowed matches.
///
/// Implementations are required to call this with appropriate values
/// for 'Name' during visitation.
void Match(StringRef Name, SourceLocation Location) {
- FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
- if (Name == ExpectedMatch &&
- FullLocation.isValid() &&
- FullLocation.getSpellingLineNumber() == ExpectedLine &&
- FullLocation.getSpellingColumnNumber() == ExpectedColumn) {
- EXPECT_TRUE(!Found);
- Found = true;
- } else if (Name == ExpectedMatch ||
- (FullLocation.isValid() &&
- FullLocation.getSpellingLineNumber() == ExpectedLine &&
- FullLocation.getSpellingColumnNumber() == ExpectedColumn)) {
- // If we did not match, record information about partial matches.
- llvm::raw_string_ostream Stream(PartialMatches);
- Stream << ", partial match: \"" << Name << "\" at ";
- Location.print(Stream, this->Context->getSourceManager());
+ const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
+
+ for (typename std::vector<MatchCandidate>::const_iterator
+ It = DisallowedMatches.begin(), End = DisallowedMatches.end();
+ It != End; ++It) {
+ EXPECT_FALSE(It->Matches(Name, FullLocation))
+ << "Matched disallowed " << *It;
+ }
+
+ for (typename std::vector<ExpectedMatch>::iterator
+ It = ExpectedMatches.begin(), End = ExpectedMatches.end();
+ It != End; ++It) {
+ It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
}
}
- std::string ExpectedMatch;
- unsigned ExpectedLine;
- unsigned ExpectedColumn;
- std::string PartialMatches;
- bool Found;
+ private:
+ struct MatchCandidate {
+ std::string ExpectedName;
+ unsigned LineNumber;
+ unsigned ColumnNumber;
+
+ MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
+ : ExpectedName(Name.str()), LineNumber(LineNumber),
+ ColumnNumber(ColumnNumber) {
+ }
+
+ bool Matches(StringRef Name, FullSourceLoc const &Location) const {
+ return MatchesName(Name) && MatchesLocation(Location);
+ }
+
+ bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
+ return MatchesName(Name) || MatchesLocation(Location);
+ }
+
+ bool MatchesName(StringRef Name) const {
+ return Name == ExpectedName;
+ }
+
+ bool MatchesLocation(FullSourceLoc const &Location) const {
+ return Location.isValid() &&
+ Location.getSpellingLineNumber() == LineNumber &&
+ Location.getSpellingColumnNumber() == ColumnNumber;
+ }
+
+ friend std::ostream &operator<<(std::ostream &Stream,
+ MatchCandidate const &Match) {
+ return Stream << Match.ExpectedName
+ << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
+ }
+ };
+
+ struct ExpectedMatch {
+ ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
+ : Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
+
+ void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
+ if (Candidate.Matches(Name, Location)) {
+ EXPECT_TRUE(!Found);
+ Found = true;
+ } else if (!Found && Candidate.PartiallyMatches(Name, Location)) {
+ llvm::raw_string_ostream Stream(PartialMatches);
+ Stream << ", partial match: \"" << Name << "\" at ";
+ Location.print(Stream, SM);
+ }
+ }
+
+ void ExpectFound() const {
+ EXPECT_TRUE(Found)
+ << "Expected \"" << Candidate.ExpectedName
+ << "\" at " << Candidate.LineNumber
+ << ":" << Candidate.ColumnNumber << PartialMatches;
+ }
+
+ MatchCandidate Candidate;
+ std::string PartialMatches;
+ bool Found;
+ };
+
+ std::vector<MatchCandidate> DisallowedMatches;
+ std::vector<ExpectedMatch> ExpectedMatches;
};
}
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
index fb3af2678c5f..d40c613dd059 100644
--- a/unittests/Tooling/ToolingTest.cpp
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -130,5 +130,37 @@ TEST(ToolInvocation, TestMapVirtualFile) {
EXPECT_TRUE(Invocation.run());
}
+struct VerifyEndCallback : public EndOfSourceFileCallback {
+ VerifyEndCallback() : Called(0), Matched(false) {}
+ virtual void run() {
+ ++Called;
+ }
+ ASTConsumer *newASTConsumer() {
+ return new FindTopLevelDeclConsumer(&Matched);
+ }
+ unsigned Called;
+ bool Matched;
+};
+
+#if !defined(_WIN32)
+TEST(newFrontendActionFactory, InjectsEndOfSourceFileCallback) {
+ VerifyEndCallback EndCallback;
+
+ FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+ std::vector<std::string> Sources;
+ Sources.push_back("/a.cc");
+ Sources.push_back("/b.cc");
+ ClangTool Tool(Compilations, Sources);
+
+ Tool.mapVirtualFile("/a.cc", "void a() {}");
+ Tool.mapVirtualFile("/b.cc", "void b() {}");
+
+ Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback));
+
+ EXPECT_TRUE(EndCallback.Matched);
+ EXPECT_EQ(2u, EndCallback.Called);
+}
+#endif
+
} // end namespace tooling
} // end namespace clang