gribozavr created this revision. Herald added subscribers: cfe-commits, sstefan1, jfb. Herald added a reviewer: jdoerfert. Herald added a project: clang. gribozavr2 added a reviewer: ymandel. gribozavr2 edited the summary of this revision.
Previously, AST Matchers tests were using a custom way to run a test with a specific C++ standard version. I'm migrating them to a shared infrastructure to specify a Clang target from libClangTesting. I'm also changing tests for AST Matchers to run in multiple language standards versions, and under multiple triples that have different behavior with regards to templates. To keep the size of the patch manageable, in this patch I'm only migrating one file to get the process started and get feedback on this approach. One caveat is that increasing the number of test configuration does significantly increase the runtime of AST Matchers tests. On my machine, the test runtime increases from 2.0 to 6.0s. I think it is worth the improved test coverage. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D82179 Files: clang/include/clang/Testing/TestClangConfig.h clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp clang/unittests/ASTMatchers/ASTMatchersTest.h clang/unittests/Tooling/Syntax/TreeTest.cpp
Index: clang/unittests/Tooling/Syntax/TreeTest.cpp =================================================================== --- clang/unittests/Tooling/Syntax/TreeTest.cpp +++ clang/unittests/Tooling/Syntax/TreeTest.cpp @@ -18,6 +18,7 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Testing/CommandLineArgs.h" +#include "clang/Testing/TestClangConfig.h" #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Syntax/BuildTree.h" #include "clang/Tooling/Syntax/Mutations.h" @@ -47,77 +48,6 @@ T->lastLeaf()->token() + 1); } -struct TestClangConfig { - TestLanguage Language; - std::string Target; - - bool isC99OrLater() const { return Language == Lang_C99; } - - bool isCXX() const { - return Language == Lang_CXX03 || Language == Lang_CXX11 || - Language == Lang_CXX14 || Language == Lang_CXX17 || - Language == Lang_CXX20; - } - - bool isCXX11OrLater() const { - return Language == Lang_CXX11 || Language == Lang_CXX14 || - Language == Lang_CXX17 || Language == Lang_CXX20; - } - - bool isCXX14OrLater() const { - return Language == Lang_CXX14 || Language == Lang_CXX17 || - Language == Lang_CXX20; - } - - bool supportsCXXDynamicExceptionSpecification() const { - return Language == Lang_CXX03 || Language == Lang_CXX11 || - Language == Lang_CXX14; - } - - bool hasDelayedTemplateParsing() const { - return Target == "x86_64-pc-win32-msvc"; - } - - std::vector<std::string> getCommandLineArgs() const { - std::vector<std::string> Result = getCommandLineArgsForTesting(Language); - Result.push_back("-target"); - Result.push_back(Target); - return Result; - } - - std::string toString() const { - std::string Result; - llvm::raw_string_ostream OS(Result); - OS << "{ Language=" << Language << ", Target=" << Target << " }"; - return OS.str(); - } - - friend std::ostream &operator<<(std::ostream &OS, - const TestClangConfig &ClangConfig) { - return OS << ClangConfig.toString(); - } - - static std::vector<TestClangConfig> &allConfigs() { - static std::vector<TestClangConfig> all_configs = []() { - std::vector<TestClangConfig> all_configs; - for (TestLanguage lang : {Lang_C89, Lang_C99, Lang_CXX03, Lang_CXX11, - Lang_CXX14, Lang_CXX17, Lang_CXX20}) { - TestClangConfig config; - config.Language = lang; - config.Target = "x86_64-pc-linux-gnu"; - all_configs.push_back(config); - - // Windows target is interesting to test because it enables - // `-fdelayed-template-parsing`. - config.Target = "x86_64-pc-win32-msvc"; - all_configs.push_back(config); - } - return all_configs; - }(); - return all_configs; - } -}; - class SyntaxTreeTest : public ::testing::Test, public ::testing::WithParamInterface<TestClangConfig> { protected: @@ -3536,7 +3466,24 @@ EXPECT_TRUE(S->isDetached()); } +static std::vector<TestClangConfig> allTestClangConfigs() { + std::vector<TestClangConfig> all_configs; + for (TestLanguage lang : {Lang_C89, Lang_C99, Lang_CXX03, Lang_CXX11, + Lang_CXX14, Lang_CXX17, Lang_CXX20}) { + TestClangConfig config; + config.Language = lang; + config.Target = "x86_64-pc-linux-gnu"; + all_configs.push_back(config); + + // Windows target is interesting to test because it enables + // `-fdelayed-template-parsing`. + config.Target = "x86_64-pc-win32-msvc"; + all_configs.push_back(config); + } + return all_configs; +} + INSTANTIATE_TEST_CASE_P(SyntaxTreeTests, SyntaxTreeTest, - testing::ValuesIn(TestClangConfig::allConfigs()), ); + testing::ValuesIn(allTestClangConfigs()), ); } // namespace Index: clang/unittests/ASTMatchers/ASTMatchersTest.h =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTest.h +++ clang/unittests/ASTMatchers/ASTMatchersTest.h @@ -12,6 +12,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Testing/CommandLineArgs.h" +#include "clang/Testing/TestClangConfig.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" @@ -94,17 +95,29 @@ return testing::AssertionFailure() << "Could not add dynamic matcher"; std::unique_ptr<FrontendActionFactory> Factory( newFrontendActionFactory(&Finder)); - // Some tests need rtti/exceptions on. Use an unknown-unknown triple so we - // don't instantiate the full system toolchain. On Linux, instantiating the - // toolchain involves stat'ing large portions of /usr/lib, and this slows down - // not only this test, but all other tests, via contention in the kernel. - // - // FIXME: This is a hack to work around the fact that there's no way to do the - // equivalent of runToolOnCodeWithArgs without instantiating a full Driver. - // We should consider having a function, at least for tests, that invokes cc1. - std::vector<std::string> Args(CompileArgs.begin(), CompileArgs.end()); - Args.insert(Args.end(), {"-frtti", "-fexceptions", - "-target", "i386-unknown-unknown"}); + std::vector<std::string> Args = { + // Some tests need rtti/exceptions on. + "-frtti", "-fexceptions", + // Ensure that tests specify the C++ standard version that they need. + "-Werror=c++14-extensions", "-Werror=c++17-extensions", + "-Werror=c++20-extensions"}; + // Append additional arguments at the end to allow overriding the default + // choices that we made above. + llvm::copy(CompileArgs, std::back_inserter(Args)); + if (llvm::find(Args, "-target") == Args.end()) { + // Use an unknown-unknown triple so we don't instantiate the full system + // toolchain. On Linux, instantiating the toolchain involves stat'ing + // large portions of /usr/lib, and this slows down not only this test, but + // all other tests, via contention in the kernel. + // + // FIXME: This is a hack to work around the fact that there's no way to do + // the equivalent of runToolOnCodeWithArgs without instantiating a full + // Driver. We should consider having a function, at least for tests, that + // invokes cc1. + Args.push_back("-target"); + Args.push_back("i386-unknown-unknown"); + } + if (!runToolOnCodeWithArgs( Factory->create(), Code, Args, Filename, "clang-tool", std::make_shared<PCHContainerOperations>(), VirtualMappedFiles)) { @@ -131,13 +144,9 @@ matchesConditionally(const Twine &Code, const T &AMatcher, bool ExpectMatch, ArrayRef<TestLanguage> TestLanguages) { for (auto Lang : TestLanguages) { - std::vector<std::string> Args = getCommandLineArgsForTesting(Lang); - Args.insert(Args.end(), - {"-Werror=c++14-extensions", "-Werror=c++17-extensions", - "-Werror=c++20-extensions"}); - auto Result = matchesConditionally(Code, AMatcher, ExpectMatch, Args, - FileContentMappings(), - getFilenameForTesting(Lang)); + auto Result = matchesConditionally( + Code, AMatcher, ExpectMatch, getCommandLineArgsForTesting(Lang), + FileContentMappings(), getFilenameForTesting(Lang)); if (!Result) return Result; } @@ -174,11 +183,6 @@ "input.c"); } -template <typename T> -testing::AssertionResult matchesC99(const Twine &Code, const T &AMatcher) { - return matchesConditionally(Code, AMatcher, true, {Lang_C99}); -} - template <typename T> testing::AssertionResult notMatchesC(const Twine &Code, const T &AMatcher) { return matchesConditionally(Code, AMatcher, false, {Lang_C89}); @@ -410,6 +414,26 @@ std::string Name; }; +class ASTMatchersTest : public ::testing::Test, + public ::testing::WithParamInterface<TestClangConfig> { +protected: + template <typename T> + testing::AssertionResult matches(const Twine &Code, const T &AMatcher) { + const TestClangConfig &TestConfig = GetParam(); + return clang::ast_matchers::matchesConditionally( + Code, AMatcher, /*ExpectMatch=*/true, TestConfig.getCommandLineArgs(), + FileContentMappings(), getFilenameForTesting(TestConfig.Language)); + } + + template <typename T> + testing::AssertionResult notMatches(const Twine &Code, const T &AMatcher) { + const TestClangConfig &TestConfig = GetParam(); + return clang::ast_matchers::matchesConditionally( + Code, AMatcher, /*ExpectMatch=*/false, TestConfig.getCommandLineArgs(), + FileContentMappings(), getFilenameForTesting(TestConfig.Language)); + } +}; + } // namespace ast_matchers } // namespace clang Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -18,52 +18,47 @@ namespace clang { namespace ast_matchers { -TEST(Finder, DynamicOnlyAcceptsSomeMatchers) { - MatchFinder Finder; - EXPECT_TRUE(Finder.addDynamicMatcher(decl(), nullptr)); - EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), nullptr)); - EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), - nullptr)); - - // Do not accept non-toplevel matchers. - EXPECT_FALSE(Finder.addDynamicMatcher(isMain(), nullptr)); - EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr)); -} - -TEST(Decl, MatchesDeclarations) { +TEST_P(ASTMatchersTest, Decl_CXX) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `decl()` that does not depend on C++. + return; + } EXPECT_TRUE(notMatches("", decl(usingDecl()))); - EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;", - decl(usingDecl()))); + EXPECT_TRUE( + matches("namespace x { class X {}; } using x::X;", decl(usingDecl()))); } -TEST(NameableDeclaration, MatchesVariousDecls) { +TEST_P(ASTMatchersTest, NameableDeclaration_MatchesVariousDecls) { DeclarationMatcher NamedX = namedDecl(hasName("X")); EXPECT_TRUE(matches("typedef int X;", NamedX)); EXPECT_TRUE(matches("int X;", NamedX)); - EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX)); - EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", NamedX)); EXPECT_TRUE(matches("void foo() { int X; }", NamedX)); - EXPECT_TRUE(matches("namespace X { }", NamedX)); EXPECT_TRUE(matches("enum X { A, B, C };", NamedX)); EXPECT_TRUE(notMatches("#define X 1", NamedX)); } -TEST(NameableDeclaration, REMatchesVariousDecls) { +TEST_P(ASTMatchersTest, NamedDecl_CXX) { + if (!GetParam().isCXX()) { + return; + } + DeclarationMatcher NamedX = namedDecl(hasName("X")); + EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX)); + EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", NamedX)); + EXPECT_TRUE(matches("namespace X { }", NamedX)); +} + +TEST_P(ASTMatchersTest, MatchesNameRE) { DeclarationMatcher NamedX = namedDecl(matchesName("::X")); EXPECT_TRUE(matches("typedef int Xa;", NamedX)); EXPECT_TRUE(matches("int Xb;", NamedX)); - EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX)); - EXPECT_TRUE(matches("void foo() try { } catch(int Xdef) { }", NamedX)); EXPECT_TRUE(matches("void foo() { int Xgh; }", NamedX)); - EXPECT_TRUE(matches("namespace Xij { }", NamedX)); EXPECT_TRUE(matches("enum X { A, B, C };", NamedX)); EXPECT_TRUE(notMatches("#define Xkl 1", NamedX)); DeclarationMatcher StartsWithNo = namedDecl(matchesName("::no")); EXPECT_TRUE(matches("int no_foo;", StartsWithNo)); - EXPECT_TRUE(matches("class foo { virtual void nobody(); };", StartsWithNo)); DeclarationMatcher Abc = namedDecl(matchesName("a.*b.*c")); EXPECT_TRUE(matches("int abc;", Abc)); @@ -74,18 +69,30 @@ DeclarationMatcher StartsWithK = namedDecl(matchesName(":k[^:]*$")); EXPECT_TRUE(matches("int k;", StartsWithK)); EXPECT_TRUE(matches("int kAbc;", StartsWithK)); +} + +TEST_P(ASTMatchersTest, MatchesNameRE_CXX) { + if (!GetParam().isCXX()) { + return; + } + DeclarationMatcher NamedX = namedDecl(matchesName("::X")); + EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX)); + EXPECT_TRUE(matches("void foo() try { } catch(int Xdef) { }", NamedX)); + EXPECT_TRUE(matches("namespace Xij { }", NamedX)); + + DeclarationMatcher StartsWithNo = namedDecl(matchesName("::no")); + EXPECT_TRUE(matches("class foo { virtual void nobody(); };", StartsWithNo)); + + DeclarationMatcher StartsWithK = namedDecl(matchesName(":k[^:]*$")); EXPECT_TRUE(matches("namespace x { int kTest; }", StartsWithK)); EXPECT_TRUE(matches("class C { int k; };", StartsWithK)); EXPECT_TRUE(notMatches("class C { int ckc; };", StartsWithK)); } -TEST(DeclarationMatcher, MatchClass) { - DeclarationMatcher ClassMatcher(recordDecl()); - - // This passes on Windows only because we explicitly pass -target - // i386-unknown-unknown. If we were to compile with the default target - // triple, we'd want to EXPECT_TRUE if it's Win32 or MSVC. - EXPECT_FALSE(matches("", ClassMatcher)); +TEST_P(ASTMatchersTest, DeclarationMatcher_MatchClass) { + if (!GetParam().isCXX()) { + return; + } DeclarationMatcher ClassX = recordDecl(recordDecl(hasName("X"))); EXPECT_TRUE(matches("class X;", ClassX)); @@ -94,7 +101,12 @@ EXPECT_TRUE(notMatches("", ClassX)); } -TEST(DeclarationMatcher, translationUnitDecl) { +TEST_P(ASTMatchersTest, TranslationUnitDecl) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `translationUnitDecl()` that does not depend on + // C++. + return; + } StringRef Code = "int MyVar1;\n" "namespace NameSpace {\n" "int MyVar2;\n" @@ -109,57 +121,84 @@ hasDeclContext(decl(hasDeclContext(translationUnitDecl())))))); } -TEST(DeclarationMatcher, LinkageSpecification) { +TEST_P(ASTMatchersTest, LinkageSpecDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("extern \"C\" { void foo() {}; }", linkageSpecDecl())); EXPECT_TRUE(notMatches("void foo() {};", linkageSpecDecl())); } -TEST(ClassTemplate, DoesNotMatchClass) { +TEST_P(ASTMatchersTest, ClassTemplateDecl_DoesNotMatchClass) { + if (!GetParam().isCXX()) { + return; + } DeclarationMatcher ClassX = classTemplateDecl(hasName("X")); EXPECT_TRUE(notMatches("class X;", ClassX)); EXPECT_TRUE(notMatches("class X {};", ClassX)); } -TEST(ClassTemplate, MatchesClassTemplate) { +TEST_P(ASTMatchersTest, ClassTemplateDecl_MatchesClassTemplate) { + if (!GetParam().isCXX()) { + return; + } DeclarationMatcher ClassX = classTemplateDecl(hasName("X")); EXPECT_TRUE(matches("template<typename T> class X {};", ClassX)); EXPECT_TRUE(matches("class Z { template<class T> class X {}; };", ClassX)); } -TEST(ClassTemplate, DoesNotMatchClassTemplateExplicitSpecialization) { +TEST_P(ASTMatchersTest, + ClassTemplateDecl_DoesNotMatchClassTemplateExplicitSpecialization) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("template<typename T> class X { };" "template<> class X<int> { int a; };", classTemplateDecl(hasName("X"), hasDescendant(fieldDecl(hasName("a")))))); } -TEST(ClassTemplate, DoesNotMatchClassTemplatePartialSpecialization) { +TEST_P(ASTMatchersTest, + ClassTemplateDecl_DoesNotMatchClassTemplatePartialSpecialization) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("template<typename T, typename U> class X { };" "template<typename T> class X<T, int> { int a; };", classTemplateDecl(hasName("X"), hasDescendant(fieldDecl(hasName("a")))))); } -TEST(DeclarationMatcher, MatchCudaDecl) { +TEST(ASTMatchersTestCUDA, CUDAKernelCallExpr) { EXPECT_TRUE(matchesWithCuda("__global__ void f() { }" "void g() { f<<<1, 2>>>(); }", cudaKernelCallExpr())); - EXPECT_TRUE(matchesWithCuda("__attribute__((device)) void f() {}", - hasAttr(clang::attr::CUDADevice))); EXPECT_TRUE(notMatchesWithCuda("void f() {}", cudaKernelCallExpr())); +} + +TEST(ASTMatchersTestCUDA, HasAttrCUDA) { + EXPECT_TRUE(matchesWithCuda("__attribute__((device)) void f() {}", + hasAttr(clang::attr::CUDADevice))); EXPECT_FALSE(notMatchesWithCuda("__attribute__((global)) void f() {}", hasAttr(clang::attr::CUDAGlobal))); } -TEST(ValueDecl, Matches) { +TEST_P(ASTMatchersTest, ValueDecl) { + if (!GetParam().isCXX()) { + // FIXME: Fix this test in non-C++ language modes. + return; + } EXPECT_TRUE(matches("enum EnumType { EnumValue };", valueDecl(hasType(asString("enum EnumType"))))); EXPECT_TRUE(matches("void FunctionDecl();", valueDecl(hasType(asString("void (void)"))))); } -TEST(FriendDecl, Matches) { +TEST_P(ASTMatchersTest, FriendDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("class Y { friend class X; };", friendDecl(hasType(asString("class X"))))); EXPECT_TRUE(matches("class Y { friend class X; };", @@ -169,43 +208,69 @@ functionDecl(hasName("f"), hasParent(friendDecl())))); } -TEST(Enum, DoesNotMatchClasses) { +TEST_P(ASTMatchersTest, EnumDecl_DoesNotMatchClasses) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X")))); } -TEST(Enum, MatchesEnums) { +TEST_P(ASTMatchersTest, EnumDecl_MatchesEnums) { + if (!GetParam().isCXX()) { + // FIXME: Fix this test in non-C++ language modes. + return; + } EXPECT_TRUE(matches("enum X {};", enumDecl(hasName("X")))); } -TEST(EnumConstant, Matches) { +TEST_P(ASTMatchersTest, EnumConstantDecl) { + if (!GetParam().isCXX()) { + // FIXME: Fix this test in non-C++ language modes. + return; + } DeclarationMatcher Matcher = enumConstantDecl(hasName("A")); EXPECT_TRUE(matches("enum X{ A };", Matcher)); EXPECT_TRUE(notMatches("enum X{ B };", Matcher)); EXPECT_TRUE(notMatches("enum X {};", Matcher)); } -TEST(TagDecl, MatchesTagDecls) { +TEST_P(ASTMatchersTest, TagDecl) { + if (!GetParam().isCXX()) { + // FIXME: Fix this test in non-C++ language modes. + return; + } EXPECT_TRUE(matches("struct X {};", tagDecl(hasName("X")))); - EXPECT_TRUE(matches("class C {};", tagDecl(hasName("C")))); EXPECT_TRUE(matches("union U {};", tagDecl(hasName("U")))); EXPECT_TRUE(matches("enum E {};", tagDecl(hasName("E")))); } -TEST(Matcher, UnresolvedLookupExpr) { - // FIXME: The test is known to be broken on Windows with delayed template - // parsing. - EXPECT_TRUE(matchesConditionally("template<typename T>" - "T foo() { T a; return a; }" - "template<typename T>" - "void bar() {" - " foo<T>();" - "}", - unresolvedLookupExpr(), - /*ExpectMatch=*/true, - {"-fno-delayed-template-parsing"})); +TEST_P(ASTMatchersTest, TagDecl_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(matches("class C {};", tagDecl(hasName("C")))); +} + +TEST_P(ASTMatchersTest, UnresolvedLookupExpr) { + if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { + // FIXME: Fix this test to work with delayed template parsing. + return; + } + + EXPECT_TRUE(matches("template<typename T>" + "T foo() { T a; return a; }" + "template<typename T>" + "void bar() {" + " foo<T>();" + "}", + unresolvedLookupExpr())); } -TEST(Matcher, ADLCall) { +TEST_P(ASTMatchersTest, UsesADL) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher ADLMatch = callExpr(usesADL()); StatementMatcher ADLMatchOper = cxxOperatorCallExpr(usesADL()); StringRef NS_Str = R"cpp( @@ -237,7 +302,11 @@ EXPECT_TRUE(notMatches(MkStr("NS::X x; NS::operator+(x, x);"), ADLMatch)); } -TEST(Matcher, Call) { +TEST_P(ASTMatchersTest, CallExpr_CXX) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `callExpr()` that does not depend on C++. + return; + } // FIXME: Do we want to overload Call() to directly take // Matcher<Decl>, too? StatementMatcher MethodX = @@ -284,20 +353,33 @@ notMatches("class Y { public: void x(); }; void z(Y &y) { y.x(); }", MethodOnYPointer)); } -TEST(Matcher, Lambda) { + +TEST_P(ASTMatchersTest, LambdaExpr) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("auto f = [] (int i) { return i; };", lambdaExpr())); } -TEST(Matcher, ForRange) { +TEST_P(ASTMatchersTest, CXXForRangeStmt) { + EXPECT_TRUE( + notMatches("void f() { for (int i; i<5; ++i); }", cxxForRangeStmt())); +} + +TEST_P(ASTMatchersTest, CXXForRangeStmt_CXX11) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("int as[] = { 1, 2, 3 };" "void f() { for (auto &a : as); }", cxxForRangeStmt())); - EXPECT_TRUE(notMatches("void f() { for (int i; i<5; ++i); }", - cxxForRangeStmt())); } -TEST(Matcher, SubstNonTypeTemplateParm) { +TEST_P(ASTMatchersTest, SubstNonTypeTemplateParmExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_FALSE(matches("template<int N>\n" "struct A { static const int n = 0; };\n" "struct B : public A<42> {};", @@ -310,21 +392,30 @@ substNonTypeTemplateParmExpr()))); } -TEST(Matcher, NonTypeTemplateParmDecl) { +TEST_P(ASTMatchersTest, NonTypeTemplateParmDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("template <int N> void f();", nonTypeTemplateParmDecl(hasName("N")))); EXPECT_TRUE( notMatches("template <typename T> void f();", nonTypeTemplateParmDecl())); } -TEST(Matcher, templateTypeParmDecl) { +TEST_P(ASTMatchersTest, TemplateTypeParmDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("template <typename T> void f();", templateTypeParmDecl(hasName("T")))); EXPECT_TRUE( notMatches("template <int N> void f();", templateTypeParmDecl())); } -TEST(Matcher, UserDefinedLiteral) { +TEST_P(ASTMatchersTest, UserDefinedLiteral) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("constexpr char operator \"\" _inc (const char i) {" " return i + 1;" "}" @@ -332,10 +423,9 @@ userDefinedLiteral())); } -TEST(Matcher, FlowControl) { - EXPECT_TRUE(matches("void f() { while(true) { break; } }", breakStmt())); - EXPECT_TRUE(matches("void f() { while(true) { continue; } }", - continueStmt())); +TEST_P(ASTMatchersTest, FlowControl) { + EXPECT_TRUE(matches("void f() { while(1) { break; } }", breakStmt())); + EXPECT_TRUE(matches("void f() { while(1) { continue; } }", continueStmt())); EXPECT_TRUE(matches("void f() { goto FOO; FOO: ;}", gotoStmt())); EXPECT_TRUE(matches("void f() { goto FOO; FOO: ;}", labelStmt( @@ -346,7 +436,11 @@ EXPECT_TRUE(matches("void f() { return; }", returnStmt())); } -TEST(Matcher, OverloadedOperatorCall) { +TEST_P(ASTMatchersTest, CXXOperatorCallExpr) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher OpCall = cxxOperatorCallExpr(); // Unary operator EXPECT_TRUE(matches("class Y { }; " @@ -372,7 +466,11 @@ EXPECT_TRUE(notMatches("int t = 5 << 2;", OpCall)); } -TEST(Matcher, ThisPointerType) { +TEST_P(ASTMatchersTest, ThisPointerType) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher MethodOnY = traverse(ast_type_traits::TK_AsIs, cxxMemberCallExpr(thisPointerType(recordDecl(hasName("Y"))))); @@ -403,7 +501,11 @@ "void z() { X *x; x->Y::x(); }", MethodOnY)); } -TEST(Matcher, VariableUsage) { +TEST_P(ASTMatchersTest, DeclRefExpr) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `declRefExpr()` that does not depend on C++. + return; + } StatementMatcher Reference = declRefExpr(to( varDecl(hasInitializer( @@ -429,7 +531,10 @@ "}", Reference)); } -TEST(Matcher, CalledVariable) { +TEST_P(ASTMatchersTest, CXXMemberCallExpr) { + if (!GetParam().isCXX()) { + return; + } StatementMatcher CallOnVariableY = cxxMemberCallExpr(on(declRefExpr(to(varDecl(hasName("y")))))); @@ -449,9 +554,12 @@ CallOnVariableY)); } -TEST(UnaryExprOrTypeTraitExpr, MatchesSizeOfAndAlignOf) { +TEST_P(ASTMatchersTest, UnaryExprOrTypeTraitExpr) { EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", unaryExprOrTypeTraitExpr())); +} + +TEST_P(ASTMatchersTest, AlignOfExpr) { EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", alignOfExpr(anything()))); // FIXME: Uncomment once alignof is enabled. @@ -461,14 +569,21 @@ // sizeOfExpr())); } -TEST(MemberExpression, DoesNotMatchClasses) { +TEST_P(ASTMatchersTest, MemberExpr_DoesNotMatchClasses) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpr())); EXPECT_TRUE(notMatches("class Y { void x() {} };", unresolvedMemberExpr())); EXPECT_TRUE( notMatches("class Y { void x() {} };", cxxDependentScopeMemberExpr())); } -TEST(MemberExpression, MatchesMemberFunctionCall) { +TEST_P(ASTMatchersTest, MemberExpr_MatchesMemberFunctionCall) { + if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { + // FIXME: Fix this test to work with delayed template parsing. + return; + } EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpr())); EXPECT_TRUE(matches("class Y { template <class T> void x() { x<T>(); } };", unresolvedMemberExpr())); @@ -476,7 +591,11 @@ cxxDependentScopeMemberExpr())); } -TEST(MemberExpression, MatchesVariable) { +TEST_P(ASTMatchersTest, MemberExpr_MatchesVariable) { + if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { + // FIXME: Fix this test to work with delayed template parsing. + return; + } EXPECT_TRUE( matches("class Y { void x() { this->y; } int y; };", memberExpr())); EXPECT_TRUE( @@ -492,7 +611,10 @@ cxxDependentScopeMemberExpr())); } -TEST(MemberExpression, MatchesStaticVariable) { +TEST_P(ASTMatchersTest, MemberExpr_MatchesStaticVariable) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", memberExpr())); EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", @@ -501,15 +623,34 @@ memberExpr())); } -TEST(Function, MatchesFunctionDeclarations) { +TEST_P(ASTMatchersTest, FunctionDecl) { StatementMatcher CallFunctionF = callExpr(callee(functionDecl(hasName("f")))); EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF)); EXPECT_TRUE(notMatches("void f() { }", CallFunctionF)); - if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() != - llvm::Triple::Win32) { - // FIXME: Make this work for MSVC. + EXPECT_TRUE(notMatches("void f(int);", functionDecl(isVariadic()))); + EXPECT_TRUE(notMatches("void f();", functionDecl(isVariadic()))); + EXPECT_TRUE(matches("void f(int, ...);", functionDecl(parameterCountIs(1)))); +} + +TEST_P(ASTMatchersTest, FunctionDecl_C) { + if (!GetParam().isC()) { + return; + } + EXPECT_TRUE(notMatches("void f();", functionDecl(isVariadic()))); + EXPECT_TRUE(matches("void f();", functionDecl(parameterCountIs(0)))); +} + +TEST_P(ASTMatchersTest, FunctionDecl_CXX) { + if (!GetParam().isCXX()) { + return; + } + + StatementMatcher CallFunctionF = callExpr(callee(functionDecl(hasName("f")))); + + if (!GetParam().hasDelayedTemplateParsing()) { + // FIXME: Fix this test to work with delayed template parsing. // Dependent contexts, but a non-dependent call. EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }", CallFunctionF)); @@ -528,29 +669,40 @@ CallFunctionF)); EXPECT_TRUE(matches("void f(...);", functionDecl(isVariadic()))); - EXPECT_TRUE(notMatches("void f(int);", functionDecl(isVariadic()))); + EXPECT_TRUE(matches("void f(...);", functionDecl(parameterCountIs(0)))); +} + +TEST_P(ASTMatchersTest, FunctionDecl_CXX11) { + if (!GetParam().isCXX11OrLater()) { + return; + } + EXPECT_TRUE(notMatches("template <typename... Ts> void f(Ts...);", functionDecl(isVariadic()))); - EXPECT_TRUE(notMatches("void f();", functionDecl(isVariadic()))); - EXPECT_TRUE(notMatchesC("void f();", functionDecl(isVariadic()))); - EXPECT_TRUE(matches("void f(...);", functionDecl(parameterCountIs(0)))); - EXPECT_TRUE(matchesC("void f();", functionDecl(parameterCountIs(0)))); - EXPECT_TRUE(matches("void f(int, ...);", functionDecl(parameterCountIs(1)))); } -TEST(FunctionTemplate, MatchesFunctionTemplateDeclarations) { +TEST_P(ASTMatchersTest, + FunctionTemplateDecl_MatchesFunctionTemplateDeclarations) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE( matches("template <typename T> void f(T t) {}", functionTemplateDecl(hasName("f")))); } -TEST(FunctionTemplate, DoesNotMatchFunctionDeclarations) { +TEST_P(ASTMatchersTest, FunctionTemplate_DoesNotMatchFunctionDeclarations) { EXPECT_TRUE( - notMatches("void f(double d); void f(int t) {}", - functionTemplateDecl(hasName("f")))); + notMatches("void f(double d);", functionTemplateDecl(hasName("f")))); + EXPECT_TRUE( + notMatches("void f(int t) {}", functionTemplateDecl(hasName("f")))); } -TEST(FunctionTemplate, DoesNotMatchFunctionTemplateSpecializations) { +TEST_P(ASTMatchersTest, + FunctionTemplateDecl_DoesNotMatchFunctionTemplateSpecializations) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE( notMatches("void g(); template <typename T> void f(T t) {}" "template <> void f(int t) { g(); }", @@ -559,7 +711,10 @@ functionDecl(hasName("g")))))))); } -TEST(Matcher, MatchesClassTemplateSpecialization) { +TEST_P(ASTMatchersTest, ClassTemplateSpecializationDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("template<typename T> struct A {};" "template<> struct A<int> {};", classTemplateSpecializationDecl())); @@ -569,17 +724,28 @@ classTemplateSpecializationDecl())); } -TEST(DeclaratorDecl, MatchesDeclaratorDecls) { +TEST_P(ASTMatchersTest, DeclaratorDecl) { EXPECT_TRUE(matches("int x;", declaratorDecl())); + EXPECT_TRUE(notMatches("struct A {};", declaratorDecl())); +} + +TEST_P(ASTMatchersTest, DeclaratorDecl_CXX) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("class A {};", declaratorDecl())); } -TEST(ParmVarDecl, MatchesParmVars) { +TEST_P(ASTMatchersTest, ParmVarDecl) { EXPECT_TRUE(matches("void f(int x);", parmVarDecl())); EXPECT_TRUE(notMatches("void f();", parmVarDecl())); } -TEST(Matcher, ConstructorCall) { +TEST_P(ASTMatchersTest, Matcher_ConstructorCall) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher Constructor = traverse(ast_type_traits::TK_AsIs, cxxConstructExpr()); @@ -594,19 +760,29 @@ EXPECT_TRUE(matches("class X {}; void x(int) { X x; }", Constructor)); } -TEST(Match, ConstructorInitializers) { +TEST_P(ASTMatchersTest, Match_ConstructorInitializers) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("class C { int i; public: C(int ii) : i(ii) {} };", cxxCtorInitializer(forField(hasName("i"))))); } -TEST(Matcher, ThisExpr) { +TEST_P(ASTMatchersTest, Matcher_ThisExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE( matches("struct X { int a; int f () { return a; } };", cxxThisExpr())); EXPECT_TRUE( notMatches("struct X { int f () { int a; return a; } };", cxxThisExpr())); } -TEST(Matcher, BindTemporaryExpression) { +TEST_P(ASTMatchersTest, Matcher_BindTemporaryExpression) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher TempExpression = traverse(ast_type_traits::TK_AsIs, cxxBindTemporaryExpr()); @@ -638,16 +814,30 @@ TempExpression)); } -TEST(MaterializeTemporaryExpr, MatchesTemporary) { - StringRef ClassString = "class string { public: string(); int length(); }; "; +TEST_P(ASTMatchersTest, MaterializeTemporaryExpr_MatchesTemporaryCXX11CXX14) { + if (GetParam().Language != Lang_CXX11 || GetParam().Language != Lang_CXX14 || + GetParam().Language != Lang_CXX17) { + return; + } + StatementMatcher TempExpression = traverse(ast_type_traits::TK_AsIs, materializeTemporaryExpr()); - EXPECT_TRUE(matches( - ClassString + "string GetStringByValue();" - "void FunctionTakesString(string s);" - "void run() { FunctionTakesString(GetStringByValue()); }", - TempExpression)); + EXPECT_TRUE(matches("class string { public: string(); }; " + "string GetStringByValue();" + "void FunctionTakesString(string s);" + "void run() { FunctionTakesString(GetStringByValue()); }", + TempExpression)); +} + +TEST_P(ASTMatchersTest, MaterializeTemporaryExpr_MatchesTemporary) { + if (!GetParam().isCXX()) { + return; + } + + StringRef ClassString = "class string { public: string(); int length(); }; "; + StatementMatcher TempExpression = + traverse(ast_type_traits::TK_AsIs, materializeTemporaryExpr()); EXPECT_TRUE(notMatches(ClassString + "string* GetStringPointer(); " @@ -669,7 +859,11 @@ TempExpression)); } -TEST(Matcher, NewExpression) { +TEST_P(ASTMatchersTest, Matcher_NewExpression) { + if (!GetParam().isCXX()) { + return; + } + StatementMatcher New = cxxNewExpr(); EXPECT_TRUE(matches("class X { public: X(); }; void x() { new X; }", New)); @@ -680,12 +874,18 @@ EXPECT_TRUE(matches("class X {}; void x(int) { new X; }", New)); } -TEST(Matcher, DeleteExpression) { +TEST_P(ASTMatchersTest, Matcher_DeleteExpression) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("struct A {}; void f(A* a) { delete a; }", cxxDeleteExpr())); } -TEST(Matcher, NoexceptExpression) { +TEST_P(ASTMatchersTest, Matcher_NoexceptExpression) { + if (!GetParam().isCXX11OrLater()) { + return; + } StatementMatcher NoExcept = cxxNoexceptExpr(); EXPECT_TRUE(matches("void foo(); bool bar = noexcept(foo());", NoExcept)); EXPECT_TRUE( @@ -695,37 +895,49 @@ EXPECT_TRUE(matches("void foo() noexcept(noexcept(1+1));", NoExcept)); } -TEST(Matcher, DefaultArgument) { +TEST_P(ASTMatchersTest, Matcher_DefaultArgument) { + if (!GetParam().isCXX()) { + return; + } StatementMatcher Arg = cxxDefaultArgExpr(); - EXPECT_TRUE(matches("void x(int, int = 0) { int y; x(y); }", Arg)); EXPECT_TRUE( matches("class X { void x(int, int = 0) { int y; x(y); } };", Arg)); EXPECT_TRUE(notMatches("void x(int, int = 0) { int y; x(y, 0); }", Arg)); } -TEST(Matcher, StringLiterals) { +TEST_P(ASTMatchersTest, StringLiteral) { StatementMatcher Literal = stringLiteral(); EXPECT_TRUE(matches("const char *s = \"string\";", Literal)); - // wide string - EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", Literal)); // with escaped characters EXPECT_TRUE(matches("const char *s = \"\x05five\";", Literal)); // no matching -- though the data type is the same, there is no string literal EXPECT_TRUE(notMatches("const char s[1] = {'a'};", Literal)); } -TEST(Matcher, CharacterLiterals) { - StatementMatcher CharLiteral = characterLiteral(); - EXPECT_TRUE(matches("const char c = 'c';", CharLiteral)); +TEST_P(ASTMatchersTest, StringLiteral_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", stringLiteral())); +} + +TEST_P(ASTMatchersTest, CharacterLiteral) { + EXPECT_TRUE(matches("const char c = 'c';", characterLiteral())); + EXPECT_TRUE(notMatches("const char c = 0x1;", characterLiteral())); +} + +TEST_P(ASTMatchersTest, CharacterLiteral_CXX) { + if (!GetParam().isCXX()) { + return; + } // wide character - EXPECT_TRUE(matches("const char c = L'c';", CharLiteral)); + EXPECT_TRUE(matches("const char c = L'c';", characterLiteral())); // wide character, Hex encoded, NOT MATCHED! - EXPECT_TRUE(notMatches("const wchar_t c = 0x2126;", CharLiteral)); - EXPECT_TRUE(notMatches("const char c = 0x1;", CharLiteral)); + EXPECT_TRUE(notMatches("const wchar_t c = 0x2126;", characterLiteral())); } -TEST(Matcher, IntegerLiterals) { +TEST_P(ASTMatchersTest, IntegerLiteral) { StatementMatcher HasIntLiteral = integerLiteral(); EXPECT_TRUE(matches("int i = 10;", HasIntLiteral)); EXPECT_TRUE(matches("int i = 0x1AB;", HasIntLiteral)); @@ -747,7 +959,7 @@ hasUnaryOperand(integerLiteral(equals(10)))))); } -TEST(Matcher, FloatLiterals) { +TEST_P(ASTMatchersTest, FloatLiteral) { StatementMatcher HasFloatLiteral = floatLiteral(); EXPECT_TRUE(matches("float i = 10.0;", HasFloatLiteral)); EXPECT_TRUE(matches("float i = 10.0f;", HasFloatLiteral)); @@ -766,42 +978,38 @@ notMatches("double i = 5.0;", floatLiteral(equals(llvm::APFloat(6.0))))); } -TEST(Matcher, NullPtrLiteral) { +TEST_P(ASTMatchersTest, CXXNullPtrLiteralExpr) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("int* i = nullptr;", cxxNullPtrLiteralExpr())); } -TEST(Matcher, ChooseExpr) { - EXPECT_TRUE(matchesC("void f() { (void)__builtin_choose_expr(1, 2, 3); }", - chooseExpr())); +TEST_P(ASTMatchersTest, ChooseExpr) { + EXPECT_TRUE(matches("void f() { (void)__builtin_choose_expr(1, 2, 3); }", + chooseExpr())); } -TEST(Matcher, GNUNullExpr) { +TEST_P(ASTMatchersTest, GNUNullExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("int* i = __null;", gnuNullExpr())); } -TEST(Matcher, AtomicExpr) { +TEST_P(ASTMatchersTest, AtomicExpr) { EXPECT_TRUE(matches("void foo() { int *ptr; __atomic_load_n(ptr, 1); }", atomicExpr())); } -TEST(Matcher, Initializers) { - const char *ToMatch = "void foo() { struct point { double x; double y; };" - " struct point ptarray[10] = " - " { [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }"; - EXPECT_TRUE(matchesConditionally( - ToMatch, - initListExpr( - has(cxxConstructExpr(requiresZeroInitialization())), - has(initListExpr( - hasType(asString("struct point")), has(floatLiteral(equals(1.0))), - has(implicitValueInitExpr(hasType(asString("double")))))), - has(initListExpr(hasType(asString("struct point")), - has(floatLiteral(equals(2.0))), - has(floatLiteral(equals(1.0)))))), - true, {"-std=gnu++03"})); - - EXPECT_TRUE(matchesC99( - ToMatch, +TEST_P(ASTMatchersTest, Initializers_C99) { + if (!GetParam().isC99OrLater()) { + return; + } + EXPECT_TRUE(matches( + "void foo() { struct point { double x; double y; };" + " struct point ptarray[10] = " + " { [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }", initListExpr(hasSyntacticForm(initListExpr( has(designatedInitExpr(designatorCountIs(2), hasDescendant(floatLiteral(equals(1.0))), @@ -814,19 +1022,41 @@ hasDescendant(integerLiteral(equals(0)))))))))); } -TEST(Matcher, ParenListExpr) { +TEST_P(ASTMatchersTest, Initializers_CXX) { + if (GetParam().Language != Lang_CXX03) { + // FIXME: Make this test pass with other C++ standard versions. + return; + } + EXPECT_TRUE(matches( + "void foo() { struct point { double x; double y; };" + " struct point ptarray[10] = " + " { [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }", + initListExpr( + has(cxxConstructExpr(requiresZeroInitialization())), + has(initListExpr( + hasType(asString("struct point")), has(floatLiteral(equals(1.0))), + has(implicitValueInitExpr(hasType(asString("double")))))), + has(initListExpr(hasType(asString("struct point")), + has(floatLiteral(equals(2.0))), + has(floatLiteral(equals(1.0)))))))); +} + +TEST_P(ASTMatchersTest, ParenListExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE( matches("template<typename T> class foo { void bar() { foo X(*this); } };" "template class foo<int>;", varDecl(hasInitializer(parenListExpr(has(unaryOperator())))))); } -TEST(Matcher, StmtExpr) { +TEST_P(ASTMatchersTest, StmtExpr) { EXPECT_TRUE(matches("void declToImport() { int C = ({int X=4; X;}); }", varDecl(hasInitializer(stmtExpr())))); } -TEST(Matcher, ImportPredefinedExpr) { +TEST_P(ASTMatchersTest, PredefinedExpr) { // __func__ expands as StringLiteral("foo") EXPECT_TRUE(matches("void foo() { __func__; }", predefinedExpr( @@ -834,11 +1064,16 @@ has(stringLiteral())))); } -TEST(Matcher, AsmStatement) { +TEST_P(ASTMatchersTest, AsmStatement) { EXPECT_TRUE(matches("void foo() { __asm(\"mov al, 2\"); }", asmStmt())); } -TEST(Matcher, Conditions) { +TEST_P(ASTMatchersTest, HasCondition) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `hasCondition()` that does not depend on C++. + return; + } + StatementMatcher Condition = ifStmt(hasCondition(cxxBoolLiteral(equals(true)))); @@ -849,7 +1084,13 @@ EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition)); } -TEST(Matcher, ConditionalOperator) { +TEST_P(ASTMatchersTest, ConditionalOperator) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `conditionalOperator()` that does not depend on + // C++. + return; + } + StatementMatcher Conditional = conditionalOperator( hasCondition(cxxBoolLiteral(equals(true))), hasTrueExpression(cxxBoolLiteral(equals(false)))); @@ -870,7 +1111,12 @@ notMatches("void x() { true ? false : true; }", ConditionalFalse)); } -TEST(Matcher, BinaryConditionalOperator) { +TEST_P(ASTMatchersTest, BinaryConditionalOperator) { + if (!GetParam().isCXX()) { + // FIXME: This test should work in non-C++ language modes. + return; + } + StatementMatcher AlwaysOne = traverse(ast_type_traits::TK_AsIs, binaryConditionalOperator( @@ -888,33 +1134,43 @@ EXPECT_TRUE(matches("void x() { 4 ?: 5; }", FourNotFive)); } -TEST(ArraySubscriptMatchers, ArraySubscripts) { +TEST_P(ASTMatchersTest, ArraySubscriptExpr) { EXPECT_TRUE(matches("int i[2]; void f() { i[1] = 1; }", arraySubscriptExpr())); EXPECT_TRUE(notMatches("int i; void f() { i = 1; }", arraySubscriptExpr())); } -TEST(For, FindsForLoops) { +TEST_P(ASTMatchersTest, ForStmt) { EXPECT_TRUE(matches("void f() { for(;;); }", forStmt())); - EXPECT_TRUE(matches("void f() { if(true) for(;;); }", forStmt())); + EXPECT_TRUE(matches("void f() { if(1) for(;;); }", forStmt())); +} + +TEST_P(ASTMatchersTest, ForStmt_CXX11) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(notMatches("int as[] = { 1, 2, 3 };" - "void f() { for (auto &a : as); }", + "void f() { for (auto &a : as); }", forStmt())); } -TEST(For, ReportsNoFalsePositives) { +TEST_P(ASTMatchersTest, ForStmt_NoFalsePositives) { EXPECT_TRUE(notMatches("void f() { ; }", forStmt())); - EXPECT_TRUE(notMatches("void f() { if(true); }", forStmt())); + EXPECT_TRUE(notMatches("void f() { if(1); }", forStmt())); } -TEST(CompoundStatement, HandlesSimpleCases) { +TEST_P(ASTMatchersTest, CompoundStatement) { EXPECT_TRUE(notMatches("void f();", compoundStmt())); EXPECT_TRUE(matches("void f() {}", compoundStmt())); EXPECT_TRUE(matches("void f() {{}}", compoundStmt())); } -TEST(CompoundStatement, DoesNotMatchEmptyStruct) { +TEST_P(ASTMatchersTest, CompoundStatement_DoesNotMatchEmptyStruct) { + if (!GetParam().isCXX()) { + // FIXME: Add a similar test that does not depend on C++. + return; + } // It's not a compound statement just because there's "{}" in the source // text. This is an AST search, not grep. EXPECT_TRUE(notMatches("namespace n { struct S {}; }", @@ -923,34 +1179,53 @@ compoundStmt())); } -TEST(CastExpression, MatchesExplicitCasts) { - EXPECT_TRUE(matches("char *p = reinterpret_cast<char *>(&p);",castExpr())); +TEST_P(ASTMatchersTest, CastExpr_MatchesExplicitCasts) { EXPECT_TRUE(matches("void *p = (void *)(&p);", castExpr())); +} + +TEST_P(ASTMatchersTest, CastExpr_MatchesExplicitCasts_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(matches("char *p = reinterpret_cast<char *>(&p);", castExpr())); EXPECT_TRUE(matches("char q, *p = const_cast<char *>(&q);", castExpr())); EXPECT_TRUE(matches("char c = char(0);", castExpr())); } -TEST(CastExpression, MatchesImplicitCasts) { + +TEST_P(ASTMatchersTest, CastExpression_MatchesImplicitCasts) { // This test creates an implicit cast from int to char. EXPECT_TRUE( matches("char c = 0;", traverse(ast_type_traits::TK_AsIs, castExpr()))); // This test creates an implicit cast from lvalue to rvalue. - EXPECT_TRUE(matches("char c = 0, d = c;", + EXPECT_TRUE(matches("void f() { char c = 0, d = c; }", traverse(ast_type_traits::TK_AsIs, castExpr()))); } -TEST(CastExpression, DoesNotMatchNonCasts) { +TEST_P(ASTMatchersTest, CastExpr_DoesNotMatchNonCasts) { EXPECT_TRUE(notMatches("char c = '0';", castExpr())); - EXPECT_TRUE(notMatches("char c, &q = c;", castExpr())); EXPECT_TRUE(notMatches("int i = (0);", castExpr())); EXPECT_TRUE(notMatches("int i = 0;", castExpr())); } -TEST(ReinterpretCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CastExpr_DoesNotMatchNonCasts_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(notMatches("char c, &q = c;", castExpr())); +} + +TEST_P(ASTMatchersTest, CXXReinterpretCastExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);", cxxReinterpretCastExpr())); } -TEST(ReinterpretCast, DoesNotMatchOtherCasts) { +TEST_P(ASTMatchersTest, CXXReinterpretCastExpr_DoesNotMatchOtherCasts) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("char* p = (char*)(&p);", cxxReinterpretCastExpr())); EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", cxxReinterpretCastExpr())); @@ -962,13 +1237,19 @@ cxxReinterpretCastExpr())); } -TEST(FunctionalCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CXXFunctionalCastExpr_MatchesSimpleCase) { + if (!GetParam().isCXX()) { + return; + } StringRef foo_class = "class Foo { public: Foo(const char*); };"; EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }", cxxFunctionalCastExpr())); } -TEST(FunctionalCast, DoesNotMatchOtherCasts) { +TEST_P(ASTMatchersTest, CXXFunctionalCastExpr_DoesNotMatchOtherCasts) { + if (!GetParam().isCXX()) { + return; + } StringRef FooClass = "class Foo { public: Foo(const char*); };"; EXPECT_TRUE( notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }", @@ -978,19 +1259,28 @@ cxxFunctionalCastExpr())); } -TEST(DynamicCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CXXDynamicCastExpr) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("struct B { virtual ~B() {} }; struct D : B {};" "B b;" "D* p = dynamic_cast<D*>(&b);", cxxDynamicCastExpr())); } -TEST(StaticCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CXXStaticCastExpr_MatchesSimpleCase) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("void* p(static_cast<void*>(&p));", cxxStaticCastExpr())); } -TEST(StaticCast, DoesNotMatchOtherCasts) { +TEST_P(ASTMatchersTest, CXXStaticCastExpr_DoesNotMatchOtherCasts) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("char* p = (char*)(&p);", cxxStaticCastExpr())); EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", cxxStaticCastExpr())); @@ -1002,11 +1292,14 @@ cxxStaticCastExpr())); } -TEST(CStyleCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CStyleCastExpr_MatchesSimpleCase) { EXPECT_TRUE(matches("int i = (int) 2.2f;", cStyleCastExpr())); } -TEST(CStyleCast, DoesNotMatchOtherCasts) { +TEST_P(ASTMatchersTest, CStyleCastExpr_DoesNotMatchOtherCasts) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);" "char q, *r = const_cast<char*>(&q);" "void* s = reinterpret_cast<char*>(&s);" @@ -1016,9 +1309,9 @@ cStyleCastExpr())); } -TEST(ImplicitCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, ImplicitCastExpr_MatchesSimpleCase) { // This test creates an implicit const cast. - EXPECT_TRUE(matches("int x = 0; const int y = x;", + EXPECT_TRUE(matches("void f() { int x = 0; const int y = x; }", traverse(ast_type_traits::TK_AsIs, varDecl(hasInitializer(implicitCastExpr()))))); // This test creates an implicit cast from int to char. @@ -1031,42 +1324,49 @@ varDecl(hasInitializer(implicitCastExpr()))))); } -TEST(ImplicitCast, DoesNotMatchIncorrectly) { +TEST_P(ASTMatchersTest, ImplicitCastExpr_DoesNotMatchIncorrectly) { // This test verifies that implicitCastExpr() matches exactly when implicit casts // are present, and that it ignores explicit and paren casts. // These two test cases have no casts. EXPECT_TRUE(notMatches("int x = 0;", varDecl(hasInitializer(implicitCastExpr())))); - EXPECT_TRUE(notMatches("int x = 0, &y = x;", + EXPECT_TRUE( + notMatches("int x = (0);", varDecl(hasInitializer(implicitCastExpr())))); + EXPECT_TRUE(notMatches("void f() { int x = 0; double d = (double) x; }", varDecl(hasInitializer(implicitCastExpr())))); +} - EXPECT_TRUE(notMatches("int x = 0; double d = (double) x;", +TEST_P(ASTMatchersTest, ImplicitCastExpr_DoesNotMatchIncorrectly_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(notMatches("int x = 0, &y = x;", varDecl(hasInitializer(implicitCastExpr())))); EXPECT_TRUE(notMatches("const int *p; int *q = const_cast<int *>(p);", varDecl(hasInitializer(implicitCastExpr())))); - - EXPECT_TRUE(notMatches("int x = (0);", - varDecl(hasInitializer(implicitCastExpr())))); } -TEST(Statement, DoesNotMatchDeclarations) { - EXPECT_TRUE(notMatches("class X {};", stmt())); +TEST_P(ASTMatchersTest, Stmt_DoesNotMatchDeclarations) { + EXPECT_TRUE(notMatches("struct X {};", stmt())); } -TEST(Statement, MatchesCompoundStatments) { +TEST_P(ASTMatchersTest, Stmt_MatchesCompoundStatments) { EXPECT_TRUE(matches("void x() {}", stmt())); } -TEST(DeclarationStatement, DoesNotMatchCompoundStatements) { +TEST_P(ASTMatchersTest, DeclStmt_DoesNotMatchCompoundStatements) { EXPECT_TRUE(notMatches("void x() {}", declStmt())); } -TEST(DeclarationStatement, MatchesVariableDeclarationStatements) { +TEST_P(ASTMatchersTest, DeclStmt_MatchesVariableDeclarationStatements) { EXPECT_TRUE(matches("void x() { int a; }", declStmt())); } -TEST(ExprWithCleanups, MatchesExprWithCleanups) { +TEST_P(ASTMatchersTest, ExprWithCleanups_MatchesExprWithCleanups) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("struct Foo { ~Foo(); };" "const Foo f = Foo();", traverse(ast_type_traits::TK_AsIs, @@ -1077,20 +1377,30 @@ varDecl(hasInitializer(exprWithCleanups()))))); } -TEST(InitListExpression, MatchesInitListExpression) { +TEST_P(ASTMatchersTest, InitListExpr) { EXPECT_TRUE(matches("int a[] = { 1, 2 };", initListExpr(hasType(asString("int [2]"))))); - EXPECT_TRUE(matches("struct B { int x, y; }; B b = { 5, 6 };", + EXPECT_TRUE(matches("struct B { int x, y; }; struct B b = { 5, 6 };", initListExpr(hasType(recordDecl(hasName("B")))))); - EXPECT_TRUE(matches("struct S { S(void (*a)()); };" - "void f();" - "S s[1] = { &f };", - declRefExpr(to(functionDecl(hasName("f")))))); EXPECT_TRUE( matches("int i[1] = {42, [0] = 43};", integerLiteral(equals(42)))); } -TEST(CXXStdInitializerListExpression, MatchesCXXStdInitializerListExpression) { +TEST_P(ASTMatchersTest, InitListExpr_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(matches("struct S { S(void (*a)()); };" + "void f();" + "S s[1] = { &f };", + declRefExpr(to(functionDecl(hasName("f")))))); +} + +TEST_P(ASTMatchersTest, + CXXStdInitializerListExpression_MatchesCXXStdInitializerListExpression) { + if (!GetParam().isCXX11OrLater()) { + return; + } StringRef code = "namespace std {" "template <typename> class initializer_list {" " public: initializer_list() noexcept {}" @@ -1117,54 +1427,65 @@ cxxStdInitializerListExpr())); } -TEST(UsingDeclaration, MatchesUsingDeclarations) { +TEST_P(ASTMatchersTest, UsingDecl_MatchesUsingDeclarations) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("namespace X { int x; } using X::x;", usingDecl())); } -TEST(UsingDeclaration, MatchesShadowUsingDelcarations) { +TEST_P(ASTMatchersTest, UsingDecl_MatchesShadowUsingDelcarations) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("namespace f { int a; } using f::a;", usingDecl(hasAnyUsingShadowDecl(hasName("a"))))); } -TEST(UsingDirectiveDeclaration, MatchesUsingNamespace) { +TEST_P(ASTMatchersTest, UsingDirectiveDecl_MatchesUsingNamespace) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("namespace X { int x; } using namespace X;", usingDirectiveDecl())); EXPECT_FALSE( matches("namespace X { int x; } using X::x;", usingDirectiveDecl())); } - -TEST(While, MatchesWhileLoops) { +TEST_P(ASTMatchersTest, WhileStmt) { EXPECT_TRUE(notMatches("void x() {}", whileStmt())); - EXPECT_TRUE(matches("void x() { while(true); }", whileStmt())); - EXPECT_TRUE(notMatches("void x() { do {} while(true); }", whileStmt())); + EXPECT_TRUE(matches("void x() { while(1); }", whileStmt())); + EXPECT_TRUE(notMatches("void x() { do {} while(1); }", whileStmt())); } -TEST(Do, MatchesDoLoops) { - EXPECT_TRUE(matches("void x() { do {} while(true); }", doStmt())); - EXPECT_TRUE(matches("void x() { do ; while(false); }", doStmt())); +TEST_P(ASTMatchersTest, DoStmt_MatchesDoLoops) { + EXPECT_TRUE(matches("void x() { do {} while(1); }", doStmt())); + EXPECT_TRUE(matches("void x() { do ; while(0); }", doStmt())); } -TEST(Do, DoesNotMatchWhileLoops) { - EXPECT_TRUE(notMatches("void x() { while(true) {} }", doStmt())); +TEST_P(ASTMatchersTest, DoStmt_DoesNotMatchWhileLoops) { + EXPECT_TRUE(notMatches("void x() { while(1) {} }", doStmt())); } -TEST(SwitchCase, MatchesCase) { +TEST_P(ASTMatchersTest, SwitchCase_MatchesCase) { EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchCase())); EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchCase())); EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchCase())); EXPECT_TRUE(notMatches("void x() { switch(42) {} }", switchCase())); } -TEST(SwitchCase, MatchesSwitch) { +TEST_P(ASTMatchersTest, SwitchCase_MatchesSwitch) { EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchStmt())); EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchStmt())); EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchStmt())); EXPECT_TRUE(notMatches("void x() {}", switchStmt())); } -TEST(ExceptionHandling, SimpleCases) { +TEST_P(ASTMatchersTest, CxxExceptionHandling_SimpleCases) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", cxxCatchStmt())); EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", cxxTryStmt())); EXPECT_TRUE( @@ -1183,18 +1504,18 @@ varDecl(isExceptionVariable()))); } -TEST(ParenExpression, SimpleCases) { +TEST_P(ASTMatchersTest, ParenExpr_SimpleCases) { EXPECT_TRUE( matches("int i = (3);", traverse(ast_type_traits::TK_AsIs, parenExpr()))); EXPECT_TRUE(matches("int i = (3 + 7);", traverse(ast_type_traits::TK_AsIs, parenExpr()))); EXPECT_TRUE(notMatches("int i = 3;", traverse(ast_type_traits::TK_AsIs, parenExpr()))); - EXPECT_TRUE(notMatches("int foo() { return 1; }; int a = foo();", + EXPECT_TRUE(notMatches("int f() { return 1; }; void g() { int a = f(); }", traverse(ast_type_traits::TK_AsIs, parenExpr()))); } -TEST(ParenExpression, IgnoringParens) { +TEST_P(ASTMatchersTest, IgnoringParens) { EXPECT_FALSE(matches( "const char* str = (\"my-string\");", traverse(ast_type_traits::TK_AsIs, @@ -1205,11 +1526,11 @@ ignoringParens(stringLiteral())))))); } -TEST(TypeMatching, MatchesTypes) { +TEST_P(ASTMatchersTest, QualType) { EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); } -TEST(TypeMatching, MatchesConstantArrayTypes) { +TEST_P(ASTMatchersTest, ConstantArrayType) { EXPECT_TRUE(matches("int a[2];", constantArrayType())); EXPECT_TRUE(notMatches( "void f() { int a[] = { 2, 3 }; int b[a[0]]; }", @@ -1220,7 +1541,10 @@ EXPECT_TRUE(notMatches("int c[41], d[43];", constantArrayType(hasSize(42)))); } -TEST(TypeMatching, MatchesDependentSizedArrayTypes) { +TEST_P(ASTMatchersTest, DependentSizedArrayType) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches( "template <typename T, int Size> class array { T data[Size]; };", dependentSizedArrayType())); @@ -1229,7 +1553,7 @@ dependentSizedArrayType())); } -TEST(TypeMatching, MatchesIncompleteArrayType) { +TEST_P(ASTMatchersTest, IncompleteArrayType) { EXPECT_TRUE(matches("int a[] = { 2, 3 };", incompleteArrayType())); EXPECT_TRUE(matches("void f(int a[]) {}", incompleteArrayType())); @@ -1237,7 +1561,7 @@ incompleteArrayType())); } -TEST(TypeMatching, MatchesVariableArrayType) { +TEST_P(ASTMatchersTest, VariableArrayType) { EXPECT_TRUE(matches("void f(int b) { int a[b]; }", variableArrayType())); EXPECT_TRUE(notMatches("int a[] = {2, 3}; int b[42];", variableArrayType())); @@ -1247,8 +1571,7 @@ varDecl(hasName("b"))))))))); } - -TEST(TypeMatching, MatchesAtomicTypes) { +TEST_P(ASTMatchersTest, AtomicType) { if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() != llvm::Triple::Win32) { // FIXME: Make this work for MSVC. @@ -1261,7 +1584,10 @@ } } -TEST(TypeMatching, MatchesAutoTypes) { +TEST_P(ASTMatchersTest, AutoType) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("auto i = 2;", autoType())); EXPECT_TRUE(matches("int v[] = { 2, 3 }; void f() { for (int i : v) {} }", autoType())); @@ -1278,34 +1604,48 @@ // autoType(hasDeducedType(isInteger())))); } -TEST(TypeMatching, MatchesDeclTypes) { +TEST_P(ASTMatchersTest, DecltypeType) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("decltype(1 + 1) sum = 1 + 1;", decltypeType())); EXPECT_TRUE(matches("decltype(1 + 1) sum = 1 + 1;", decltypeType(hasUnderlyingType(isInteger())))); } -TEST(TypeMatching, MatchesFunctionTypes) { +TEST_P(ASTMatchersTest, FunctionType) { EXPECT_TRUE(matches("int (*f)(int);", functionType())); EXPECT_TRUE(matches("void f(int i) {}", functionType())); } -TEST(TypeMatching, IgnoringParens) { +TEST_P(ASTMatchersTest, IgnoringParens_Type) { EXPECT_TRUE( notMatches("void (*fp)(void);", pointerType(pointee(functionType())))); EXPECT_TRUE(matches("void (*fp)(void);", pointerType(pointee(ignoringParens(functionType()))))); } -TEST(TypeMatching, MatchesFunctionProtoTypes) { +TEST_P(ASTMatchersTest, FunctionProtoType) { EXPECT_TRUE(matches("int (*f)(int);", functionProtoType())); EXPECT_TRUE(matches("void f(int i);", functionProtoType())); + EXPECT_TRUE(matches("void f(void);", functionProtoType(parameterCountIs(0)))); +} + +TEST_P(ASTMatchersTest, FunctionProtoType_C) { + if (!GetParam().isC()) { + return; + } + EXPECT_TRUE(notMatches("void f();", functionProtoType())); +} + +TEST_P(ASTMatchersTest, FunctionProtoType_CXX) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("void f();", functionProtoType(parameterCountIs(0)))); - EXPECT_TRUE(notMatchesC("void f();", functionProtoType())); - EXPECT_TRUE( - matchesC("void f(void);", functionProtoType(parameterCountIs(0)))); } -TEST(TypeMatching, MatchesParenType) { +TEST_P(ASTMatchersTest, ParenType) { EXPECT_TRUE( matches("int (*array)[4];", varDecl(hasType(pointsTo(parenType()))))); EXPECT_TRUE(notMatches("int *array[4];", varDecl(hasType(parenType())))); @@ -1318,7 +1658,7 @@ varDecl(hasType(pointsTo(parenType(innerType(functionType()))))))); } -TEST(TypeMatching, PointerTypes) { +TEST_P(ASTMatchersTest, PointerType) { // FIXME: Reactive when these tests can be more specific (not matching // implicit code on certain platforms), likely when we have hasDescendant for // Types/TypeLocs. @@ -1340,21 +1680,7 @@ "int* b; int* * const a = &b;", loc(qualType(isConstQualified(), pointerType())))); - StringRef Fragment = "struct A { int i; }; int A::* ptr = &A::i;"; - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), - hasType(blockPointerType())))); - EXPECT_TRUE(matches(Fragment, varDecl(hasName("ptr"), - hasType(memberPointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), - hasType(pointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), - hasType(referenceType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), - hasType(lValueReferenceType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), - hasType(rValueReferenceType())))); - - Fragment = "int *ptr;"; + StringRef Fragment = "int *ptr;"; EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), hasType(blockPointerType())))); EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), @@ -1363,37 +1689,65 @@ hasType(pointerType())))); EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), hasType(referenceType())))); +} + +TEST_P(ASTMatchersTest, PointerType_CXX) { + if (!GetParam().isCXX()) { + return; + } + StringRef Fragment = "struct A { int i; }; int A::* ptr = &A::i;"; + EXPECT_TRUE(notMatches(Fragment, + varDecl(hasName("ptr"), hasType(blockPointerType())))); + EXPECT_TRUE( + matches(Fragment, varDecl(hasName("ptr"), hasType(memberPointerType())))); + EXPECT_TRUE( + notMatches(Fragment, varDecl(hasName("ptr"), hasType(pointerType())))); + EXPECT_TRUE( + notMatches(Fragment, varDecl(hasName("ptr"), hasType(referenceType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ptr"), hasType(lValueReferenceType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ptr"), hasType(rValueReferenceType())))); Fragment = "int a; int &ref = a;"; - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(blockPointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(memberPointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(pointerType())))); - EXPECT_TRUE(matches(Fragment, varDecl(hasName("ref"), - hasType(referenceType())))); - EXPECT_TRUE(matches(Fragment, varDecl(hasName("ref"), - hasType(lValueReferenceType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(rValueReferenceType())))); + EXPECT_TRUE(notMatches(Fragment, + varDecl(hasName("ref"), hasType(blockPointerType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ref"), hasType(memberPointerType())))); + EXPECT_TRUE( + notMatches(Fragment, varDecl(hasName("ref"), hasType(pointerType())))); + EXPECT_TRUE( + matches(Fragment, varDecl(hasName("ref"), hasType(referenceType())))); + EXPECT_TRUE(matches(Fragment, + varDecl(hasName("ref"), hasType(lValueReferenceType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ref"), hasType(rValueReferenceType())))); +} - Fragment = "int &&ref = 2;"; - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(blockPointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(memberPointerType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(pointerType())))); - EXPECT_TRUE(matches(Fragment, varDecl(hasName("ref"), - hasType(referenceType())))); - EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), - hasType(lValueReferenceType())))); - EXPECT_TRUE(matches(Fragment, varDecl(hasName("ref"), - hasType(rValueReferenceType())))); +TEST_P(ASTMatchersTest, PointerType_CXX11) { + if (!GetParam().isCXX11OrLater()) { + return; + } + StringRef Fragment = "int &&ref = 2;"; + EXPECT_TRUE(notMatches(Fragment, + varDecl(hasName("ref"), hasType(blockPointerType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ref"), hasType(memberPointerType())))); + EXPECT_TRUE( + notMatches(Fragment, varDecl(hasName("ref"), hasType(pointerType())))); + EXPECT_TRUE( + matches(Fragment, varDecl(hasName("ref"), hasType(referenceType())))); + EXPECT_TRUE(notMatches( + Fragment, varDecl(hasName("ref"), hasType(lValueReferenceType())))); + EXPECT_TRUE(matches(Fragment, + varDecl(hasName("ref"), hasType(rValueReferenceType())))); } -TEST(TypeMatching, AutoRefTypes) { +TEST_P(ASTMatchersTest, AutoRefTypes) { + if (!GetParam().isCXX11OrLater()) { + return; + } + StringRef Fragment = "auto a = 1;" "auto b = a;" "auto &c = a;" @@ -1423,14 +1777,28 @@ hasType(rValueReferenceType())))); } -TEST(TypeMatching, MatchesEnumTypes) { +TEST_P(ASTMatchersTest, EnumType) { + EXPECT_TRUE( + matches("enum Color { Green }; enum Color color;", loc(enumType()))); +} + +TEST_P(ASTMatchersTest, EnumType_CXX) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("enum Color { Green }; Color color;", loc(enumType()))); +} + +TEST_P(ASTMatchersTest, EnumType_CXX11) { + if (!GetParam().isCXX11OrLater()) { + return; + } EXPECT_TRUE(matches("enum class Color { Green }; Color color;", loc(enumType()))); } -TEST(TypeMatching, MatchesPointersToConstTypes) { +TEST_P(ASTMatchersTest, PointerType_MatchesPointersToConstTypes) { EXPECT_TRUE(matches("int b; int * const a = &b;", loc(pointerType()))); EXPECT_TRUE(matches("int b; int * const a = &b;", @@ -1443,31 +1811,49 @@ pointerType(pointee(builtinType())))); } -TEST(TypeMatching, MatchesTypedefTypes) { +TEST_P(ASTMatchersTest, TypedefType) { EXPECT_TRUE(matches("typedef int X; X a;", varDecl(hasName("a"), hasType(typedefType())))); } -TEST(TypeMatching, MatchesTemplateSpecializationType) { +TEST_P(ASTMatchersTest, TemplateSpecializationType) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("template <typename T> class A{}; A<int> a;", templateSpecializationType())); } -TEST(TypeMatching, MatchesDeucedTemplateSpecializationType) { +TEST_P(ASTMatchersTest, DeducedTemplateSpecializationType) { + if (!GetParam().isCXX17OrLater()) { + return; + } EXPECT_TRUE( matches("template <typename T> class A{ public: A(T) {} }; A a(1);", - deducedTemplateSpecializationType(), langCxx17OrLater())); + deducedTemplateSpecializationType())); } -TEST(TypeMatching, MatchesRecordType) { - EXPECT_TRUE(matches("class C{}; C c;", recordType())); - EXPECT_TRUE(matches("struct S{}; S s;", +TEST_P(ASTMatchersTest, RecordType) { + EXPECT_TRUE(matches("struct S {}; struct S s;", recordType(hasDeclaration(recordDecl(hasName("S")))))); EXPECT_TRUE(notMatches("int i;", recordType(hasDeclaration(recordDecl(hasName("S")))))); } -TEST(TypeMatching, MatchesElaboratedType) { +TEST_P(ASTMatchersTest, RecordType_CXX) { + if (!GetParam().isCXX()) { + return; + } + EXPECT_TRUE(matches("class C {}; C c;", recordType())); + EXPECT_TRUE(matches("struct S {}; S s;", + recordType(hasDeclaration(recordDecl(hasName("S")))))); +} + +TEST_P(ASTMatchersTest, ElaboratedType) { + if (!GetParam().isCXX()) { + // FIXME: Add a test for `elaboratedType()` that does not depend on C++. + return; + } EXPECT_TRUE(matches( "namespace N {" " namespace M {" @@ -1479,7 +1865,10 @@ EXPECT_TRUE(notMatches("class C {}; C c;", elaboratedType())); } -TEST(TypeMatching, MatchesSubstTemplateTypeParmType) { +TEST_P(ASTMatchersTest, SubstTemplateTypeParmType) { + if (!GetParam().isCXX()) { + return; + } StringRef code = "template <typename T>" "int F() {" " return 1 + T();" @@ -1491,7 +1880,10 @@ expr(hasType(substTemplateTypeParmType())))))); } -TEST(NNS, MatchesNestedNameSpecifiers) { +TEST_P(ASTMatchersTest, NestedNameSpecifier) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", nestedNameSpecifier())); EXPECT_TRUE(matches("template <typename T> class A { typename T::B b; };", @@ -1509,17 +1901,23 @@ nestedNameSpecifier())); } -TEST(NullStatement, SimpleCases) { +TEST_P(ASTMatchersTest, NullStmt) { EXPECT_TRUE(matches("void f() {int i;;}", nullStmt())); EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt())); } -TEST(NS, Alias) { +TEST_P(ASTMatchersTest, NamespaceAliasDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("namespace test {} namespace alias = ::test;", namespaceAliasDecl(hasName("alias")))); } -TEST(NNS, MatchesTypes) { +TEST_P(ASTMatchersTest, NestedNameSpecifier_MatchesTypes) { + if (!GetParam().isCXX()) { + return; + } NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( specifiesType(hasDeclaration(recordDecl(hasName("A"))))); EXPECT_TRUE(matches("struct A { struct B {}; }; A::B b;", Matcher)); @@ -1528,7 +1926,10 @@ EXPECT_TRUE(notMatches("namespace A { struct B {}; } A::B b;", Matcher)); } -TEST(NNS, MatchesNamespaceDecls) { +TEST_P(ASTMatchersTest, NestedNameSpecifier_MatchesNamespaceDecls) { + if (!GetParam().isCXX()) { + return; + } NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( specifiesNamespace(hasName("ns"))); EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", Matcher)); @@ -1536,7 +1937,11 @@ EXPECT_TRUE(notMatches("struct ns { struct A {}; }; ns::A a;", Matcher)); } -TEST(NNS, MatchesNestedNameSpecifierPrefixes) { +TEST_P(ASTMatchersTest, + NestedNameSpecifier_MatchesNestedNameSpecifierPrefixes) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches( "struct A { struct B { struct C {}; }; }; A::B::C c;", nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))))); @@ -1550,7 +1955,6 @@ specifiesTypeLoc(loc(qualType(asString("struct N::A")))))))); } - template <typename T> class VerifyAncestorHasChildIsEqual : public BoundNodesCallback { public: @@ -1591,41 +1995,68 @@ } }; -TEST(IsEqualTo, MatchesNodesByIdentity) { +TEST_P(ASTMatchersTest, IsEqualTo_MatchesNodesByIdentity) { EXPECT_TRUE(matchAndVerifyResultTrue( - "class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""), - std::make_unique<VerifyAncestorHasChildIsEqual<CXXRecordDecl>>())); + "void f() { if (1) if(1) {} }", ifStmt().bind(""), + std::make_unique<VerifyAncestorHasChildIsEqual<IfStmt>>())); +} + +TEST_P(ASTMatchersTest, IsEqualTo_MatchesNodesByIdentity_Cxx) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matchAndVerifyResultTrue( - "void f() { if (true) if(true) {} }", ifStmt().bind(""), - std::make_unique<VerifyAncestorHasChildIsEqual<IfStmt>>())); + "class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""), + std::make_unique<VerifyAncestorHasChildIsEqual<CXXRecordDecl>>())); EXPECT_TRUE(matchAndVerifyResultTrue( - "class X { class Y {} y; };", - fieldDecl(hasName("y"), hasType(type().bind(""))).bind("decl"), - std::make_unique<VerifyAncestorHasChildIsEqual<Type>>())); + "class X { class Y {} y; };", + fieldDecl(hasName("y"), hasType(type().bind(""))).bind("decl"), + std::make_unique<VerifyAncestorHasChildIsEqual<Type>>())); } -TEST(TypedefDeclMatcher, Match) { +TEST_P(ASTMatchersTest, TypedefDecl) { EXPECT_TRUE(matches("typedef int typedefDeclTest;", typedefDecl(hasName("typedefDeclTest")))); - EXPECT_TRUE(notMatches("using typedefDeclTest2 = int;", - typedefDecl(hasName("typedefDeclTest2")))); } -TEST(TypeAliasDeclMatcher, Match) { - EXPECT_TRUE(matches("using typeAliasTest2 = int;", - typeAliasDecl(hasName("typeAliasTest2")))); +TEST_P(ASTMatchersTest, TypedefDecl_Cxx) { + if (!GetParam().isCXX11OrLater()) { + return; + } + EXPECT_TRUE(notMatches("using typedefDeclTest = int;", + typedefDecl(hasName("typedefDeclTest")))); +} + +TEST_P(ASTMatchersTest, TypeAliasDecl) { EXPECT_TRUE(notMatches("typedef int typeAliasTest;", typeAliasDecl(hasName("typeAliasTest")))); } -TEST(TypedefNameDeclMatcher, Match) { +TEST_P(ASTMatchersTest, TypeAliasDecl_CXX) { + if (!GetParam().isCXX11OrLater()) { + return; + } + EXPECT_TRUE(matches("using typeAliasTest = int;", + typeAliasDecl(hasName("typeAliasTest")))); +} + +TEST_P(ASTMatchersTest, TypedefNameDecl) { EXPECT_TRUE(matches("typedef int typedefNameDeclTest1;", typedefNameDecl(hasName("typedefNameDeclTest1")))); - EXPECT_TRUE(matches("using typedefNameDeclTest2 = int;", - typedefNameDecl(hasName("typedefNameDeclTest2")))); } -TEST(TypeAliasTemplateDeclMatcher, Match) { +TEST_P(ASTMatchersTest, TypedefNameDecl_CXX) { + if (!GetParam().isCXX11OrLater()) { + return; + } + EXPECT_TRUE(matches("using typedefNameDeclTest = int;", + typedefNameDecl(hasName("typedefNameDeclTest")))); +} + +TEST_P(ASTMatchersTest, TypeAliasTemplateDecl) { + if (!GetParam().isCXX11OrLater()) { + return; + } StringRef Code = R"( template <typename T> class X { T t; }; @@ -1641,8 +2072,8 @@ notMatches(Code, typeAliasTemplateDecl(hasName("typeAliasDecl")))); } -TEST(ObjCMessageExprMatcher, SimpleExprs) { - // don't find ObjCMessageExpr where none are present +TEST(ASTMatchersTestObjC, ObjCMessageExpr) { + // Don't find ObjCMessageExpr where none are present. EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything()))); StringRef Objc1String = "@interface Str " @@ -1698,7 +2129,7 @@ ))); } -TEST(ObjCDeclMatcher, CoreDecls) { +TEST(ASTMatchersTestObjC, ObjCDecls) { StringRef ObjCString = "@protocol Proto " "- (void)protoDidThing; " "@end " @@ -1745,7 +2176,7 @@ objcPropertyDecl(hasName("enabled")))); } -TEST(ObjCStmtMatcher, ExceptionStmts) { +TEST(ASTMatchersTestObjC, ObjCExceptionStmts) { StringRef ObjCString = "void f(id obj) {" " @try {" " @throw obj;" @@ -1767,7 +2198,7 @@ objcFinallyStmt())); } -TEST(ObjCAutoreleaseMatcher, AutoreleasePool) { +TEST(ASTMatchersTestObjC, ObjCAutoreleasePoolStmt) { StringRef ObjCString = "void f() {" "@autoreleasepool {" " int x = 1;" @@ -1778,7 +2209,7 @@ EXPECT_FALSE(matchesObjC(ObjCStringNoPool, autoreleasePoolStmt())); } -TEST(OMPExecutableDirective, Matches) { +TEST(ASTMatchersTestOpenMP, OMPExecutableDirective) { auto Matcher = stmt(ompExecutableDirective()); StringRef Source0 = R"( @@ -1802,7 +2233,7 @@ EXPECT_TRUE(notMatchesWithOpenMP(Source2, Matcher)); } -TEST(OMPDefaultClause, Matches) { +TEST(ASTMatchersTestOpenMP, OMPDefaultClause) { auto Matcher = ompExecutableDirective(hasAnyClause(ompDefaultClause())); StringRef Source0 = R"( @@ -1840,8 +2271,19 @@ EXPECT_TRUE(notMatchesWithOpenMP(Source4, Matcher)); } -TEST(MatchFinderAPI, matchesDynamic) { +TEST(ASTMatchersTest, Finder_DynamicOnlyAcceptsSomeMatchers) { + MatchFinder Finder; + EXPECT_TRUE(Finder.addDynamicMatcher(decl(), nullptr)); + EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), nullptr)); + EXPECT_TRUE( + Finder.addDynamicMatcher(constantArrayType(hasSize(42)), nullptr)); + + // Do not accept non-toplevel matchers. + EXPECT_FALSE(Finder.addDynamicMatcher(isMain(), nullptr)); + EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr)); +} +TEST(MatchFinderAPI, MatchesDynamic) { StringRef SourceCode = "struct A { void f() {} };"; auto Matcher = functionDecl(isDefinition()).bind("method"); @@ -1864,5 +2306,35 @@ EXPECT_EQ(MethodNode, GlobalMethodNode); } +static std::vector<TestClangConfig> allTestClangConfigs() { + std::vector<TestClangConfig> all_configs; + for (TestLanguage lang : {Lang_C89, Lang_C99, Lang_CXX03, Lang_CXX11, + Lang_CXX14, Lang_CXX17, Lang_CXX20}) { + TestClangConfig config; + config.Language = lang; + + // Use an unknown-unknown triple so we don't instantiate the full system + // toolchain. On Linux, instantiating the toolchain involves stat'ing + // large portions of /usr/lib, and this slows down not only this test, but + // all other tests, via contention in the kernel. + // + // FIXME: This is a hack to work around the fact that there's no way to do + // the equivalent of runToolOnCodeWithArgs without instantiating a full + // Driver. We should consider having a function, at least for tests, that + // invokes cc1. + config.Target = "i386-unknown-unknown"; + all_configs.push_back(config); + + // Windows target is interesting to test because it enables + // `-fdelayed-template-parsing`. + config.Target = "x86_64-pc-win32-msvc"; + all_configs.push_back(config); + } + return all_configs; +} + +INSTANTIATE_TEST_CASE_P(ASTMatchersTests, ASTMatchersTest, + testing::ValuesIn(allTestClangConfigs()), ); + } // namespace ast_matchers } // namespace clang Index: clang/include/clang/Testing/TestClangConfig.h =================================================================== --- /dev/null +++ clang/include/clang/Testing/TestClangConfig.h @@ -0,0 +1,77 @@ +//===--- TestClangConfig.h ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TESTING_TESTCLANGCONFIG_H +#define LLVM_CLANG_TESTING_TESTCLANGCONFIG_H + +#include "clang/Testing/CommandLineArgs.h" +#include <string> +#include <vector> + +namespace clang { + +struct TestClangConfig { + TestLanguage Language; + std::string Target; + + bool isC() const { return Language == Lang_C89 || Language == Lang_C99; } + + bool isC99OrLater() const { return Language == Lang_C99; } + + bool isCXX() const { + return Language == Lang_CXX03 || Language == Lang_CXX11 || + Language == Lang_CXX14 || Language == Lang_CXX17 || + Language == Lang_CXX20; + } + + bool isCXX11OrLater() const { + return Language == Lang_CXX11 || Language == Lang_CXX14 || + Language == Lang_CXX17 || Language == Lang_CXX20; + } + + bool isCXX14OrLater() const { + return Language == Lang_CXX14 || Language == Lang_CXX17 || + Language == Lang_CXX20; + } + + bool isCXX17OrLater() const { + return Language == Lang_CXX17 || Language == Lang_CXX20; + } + + bool supportsCXXDynamicExceptionSpecification() const { + return Language == Lang_CXX03 || Language == Lang_CXX11 || + Language == Lang_CXX14; + } + + bool hasDelayedTemplateParsing() const { + return Target == "x86_64-pc-win32-msvc"; + } + + std::vector<std::string> getCommandLineArgs() const { + std::vector<std::string> Result = getCommandLineArgsForTesting(Language); + Result.push_back("-target"); + Result.push_back(Target); + return Result; + } + + std::string toString() const { + std::string Result; + llvm::raw_string_ostream OS(Result); + OS << "{ Language=" << Language << ", Target=" << Target << " }"; + return OS.str(); + } + + friend std::ostream &operator<<(std::ostream &OS, + const TestClangConfig &ClangConfig) { + return OS << ClangConfig.toString(); + } +}; + +} // end namespace clang + +#endif
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits