diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2012-12-02 13:20:44 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2012-12-02 13:20:44 +0000 |
commit | 13cc256e404620c1de0cbcc4e43ce1e2dbbc4898 (patch) | |
tree | 2732d02d7d51218d6eed98ac7fcfc5b8794896b5 /unittests/Tooling | |
parent | 657bc3d9848e3be92029b2416031340988cd0111 (diff) | |
download | src-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.txt | 2 | ||||
-rw-r--r-- | unittests/Tooling/CompilationDatabaseTest.cpp | 123 | ||||
-rw-r--r-- | unittests/Tooling/Makefile | 3 | ||||
-rw-r--r-- | unittests/Tooling/RecursiveASTVisitorTest.cpp | 68 | ||||
-rw-r--r-- | unittests/Tooling/RefactoringCallbacksTest.cpp | 16 | ||||
-rw-r--r-- | unittests/Tooling/RefactoringTest.cpp | 4 | ||||
-rw-r--r-- | unittests/Tooling/RewriterTestContext.h | 11 | ||||
-rw-r--r-- | unittests/Tooling/TestVisitor.h | 161 | ||||
-rw-r--r-- | unittests/Tooling/ToolingTest.cpp | 32 |
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 |