diff --git a/clang/include/clang/Testing/TestClangConfig.h b/clang/include/clang/Testing/TestClangConfig.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Testing/TestClangConfig.h @@ -0,0 +1,85 @@ +//===--- 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 "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { + +/// A Clang configuration for end-to-end tests that can be converted to +/// command line arguments for the driver. +/// +/// The configuration is represented as typed, named values, making it easier +/// and safer to work with compared to an array of string command line flags. +struct TestClangConfig { + TestLanguage Language; + + /// The argument of the `-target` command line flag. + 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 getCommandLineArgs() const { + std::vector 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 diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/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 class X {};", ClassX)); EXPECT_TRUE(matches("class Z { template class X {}; };", ClassX)); } -TEST(ClassTemplate, DoesNotMatchClassTemplateExplicitSpecialization) { +TEST_P(ASTMatchersTest, + ClassTemplateDecl_DoesNotMatchClassTemplateExplicitSpecialization) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(notMatches("template class X { };" "template<> class X { 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 class X { };" "template class X { 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" - "T foo() { T a; return a; }" - "template" - "void bar() {" - " foo();" - "}", - 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" + "T foo() { T a; return a; }" + "template" + "void bar() {" + " foo();" + "}", + 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, 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\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 void f();", nonTypeTemplateParmDecl(hasName("N")))); EXPECT_TRUE( notMatches("template void f();", nonTypeTemplateParmDecl())); } -TEST(Matcher, templateTypeParmDecl) { +TEST_P(ASTMatchersTest, TemplateTypeParmDecl) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("template void f();", templateTypeParmDecl(hasName("T")))); EXPECT_TRUE( notMatches("template 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 void x() { x(); } };", 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 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 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 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 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 struct A {};" "template<> struct A {};", 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,29 @@ 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) { + 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 +858,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 +873,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 +894,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 +958,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 +977,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 +1021,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 class foo { void bar() { foo X(*this); } };" "template class foo;", 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 +1063,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 +1083,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 +1110,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 +1133,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 +1178,53 @@ compoundStmt())); } -TEST(CastExpression, MatchesExplicitCasts) { - EXPECT_TRUE(matches("char *p = reinterpret_cast(&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(&p);", castExpr())); EXPECT_TRUE(matches("char q, *p = const_cast(&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(&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(&q);", cxxReinterpretCastExpr())); @@ -962,13 +1236,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 +1258,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(&b);", cxxDynamicCastExpr())); } -TEST(StaticCast, MatchesSimpleCase) { +TEST_P(ASTMatchersTest, CXXStaticCastExpr_MatchesSimpleCase) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matches("void* p(static_cast(&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(&q);", cxxStaticCastExpr())); @@ -1002,11 +1291,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(0);" "char q, *r = const_cast(&q);" "void* s = reinterpret_cast(&s);" @@ -1016,9 +1308,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 +1323,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(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 +1376,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 class initializer_list {" " public: initializer_list() noexcept {}" @@ -1117,54 +1426,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 +1503,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 +1525,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 +1540,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 class array { T data[Size]; };", dependentSizedArrayType())); @@ -1229,7 +1552,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 +1560,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 +1570,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 +1583,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 +1603,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 +1657,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 +1679,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 +1688,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 +1776,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 +1810,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 class A{}; A a;", templateSpecializationType())); } -TEST(TypeMatching, MatchesDeucedTemplateSpecializationType) { +TEST_P(ASTMatchersTest, DeducedTemplateSpecializationType) { + if (!GetParam().isCXX17OrLater()) { + return; + } EXPECT_TRUE( matches("template 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 +1864,10 @@ EXPECT_TRUE(notMatches("class C {}; C c;", elaboratedType())); } -TEST(TypeMatching, MatchesSubstTemplateTypeParmType) { +TEST_P(ASTMatchersTest, SubstTemplateTypeParmType) { + if (!GetParam().isCXX()) { + return; + } StringRef code = "template " "int F() {" " return 1 + T();" @@ -1491,7 +1879,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 class A { typename T::B b; };", @@ -1509,17 +1900,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 +1925,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 +1936,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 +1954,6 @@ specifiesTypeLoc(loc(qualType(asString("struct N::A")))))))); } - template class VerifyAncestorHasChildIsEqual : public BoundNodesCallback { public: @@ -1591,41 +1994,68 @@ } }; -TEST(IsEqualTo, MatchesNodesByIdentity) { +TEST_P(ASTMatchersTest, IsEqualTo_MatchesNodesByIdentity) { EXPECT_TRUE(matchAndVerifyResultTrue( - "class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""), - std::make_unique>())); + "void f() { if (1) if(1) {} }", ifStmt().bind(""), + std::make_unique>())); +} + +TEST_P(ASTMatchersTest, IsEqualTo_MatchesNodesByIdentity_Cxx) { + if (!GetParam().isCXX()) { + return; + } EXPECT_TRUE(matchAndVerifyResultTrue( - "void f() { if (true) if(true) {} }", ifStmt().bind(""), - std::make_unique>())); + "class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""), + std::make_unique>())); EXPECT_TRUE(matchAndVerifyResultTrue( - "class X { class Y {} y; };", - fieldDecl(hasName("y"), hasType(type().bind(""))).bind("decl"), - std::make_unique>())); + "class X { class Y {} y; };", + fieldDecl(hasName("y"), hasType(type().bind(""))).bind("decl"), + std::make_unique>())); } -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 class X { T t; }; @@ -1641,8 +2071,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 +2128,7 @@ ))); } -TEST(ObjCDeclMatcher, CoreDecls) { +TEST(ASTMatchersTestObjC, ObjCDecls) { StringRef ObjCString = "@protocol Proto " "- (void)protoDidThing; " "@end " @@ -1745,7 +2175,7 @@ objcPropertyDecl(hasName("enabled")))); } -TEST(ObjCStmtMatcher, ExceptionStmts) { +TEST(ASTMatchersTestObjC, ObjCExceptionStmts) { StringRef ObjCString = "void f(id obj) {" " @try {" " @throw obj;" @@ -1767,7 +2197,7 @@ objcFinallyStmt())); } -TEST(ObjCAutoreleaseMatcher, AutoreleasePool) { +TEST(ASTMatchersTestObjC, ObjCAutoreleasePoolStmt) { StringRef ObjCString = "void f() {" "@autoreleasepool {" " int x = 1;" @@ -1778,7 +2208,7 @@ EXPECT_FALSE(matchesObjC(ObjCStringNoPool, autoreleasePoolStmt())); } -TEST(OMPExecutableDirective, Matches) { +TEST(ASTMatchersTestOpenMP, OMPExecutableDirective) { auto Matcher = stmt(ompExecutableDirective()); StringRef Source0 = R"( @@ -1802,7 +2232,7 @@ EXPECT_TRUE(notMatchesWithOpenMP(Source2, Matcher)); } -TEST(OMPDefaultClause, Matches) { +TEST(ASTMatchersTestOpenMP, OMPDefaultClause) { auto Matcher = ompExecutableDirective(hasAnyClause(ompDefaultClause())); StringRef Source0 = R"( @@ -1840,8 +2270,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 +2305,35 @@ EXPECT_EQ(MethodNode, GlobalMethodNode); } +static std::vector allTestClangConfigs() { + std::vector 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 diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h --- a/clang/unittests/ASTMatchers/ASTMatchersTest.h +++ b/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 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 Args(CompileArgs.begin(), CompileArgs.end()); - Args.insert(Args.end(), {"-frtti", "-fexceptions", - "-target", "i386-unknown-unknown"}); + std::vector 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(), VirtualMappedFiles)) { @@ -131,13 +144,9 @@ matchesConditionally(const Twine &Code, const T &AMatcher, bool ExpectMatch, ArrayRef TestLanguages) { for (auto Lang : TestLanguages) { - std::vector 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; } @@ -175,11 +184,6 @@ } template -testing::AssertionResult matchesC99(const Twine &Code, const T &AMatcher) { - return matchesConditionally(Code, AMatcher, true, {Lang_C99}); -} - -template 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 { +protected: + template + 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 + 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 diff --git a/clang/unittests/Tooling/Syntax/TreeTest.cpp b/clang/unittests/Tooling/Syntax/TreeTest.cpp --- a/clang/unittests/Tooling/Syntax/TreeTest.cpp +++ b/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,83 +48,6 @@ T->lastLeaf()->token() + 1); } -struct TestClangConfig { - TestLanguage Language; - std::string Target; - - bool isC99OrLater() const { return Language == Lang_C99; } - - bool isC() const { return Language == Lang_C89 || 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 getCommandLineArgs() const { - std::vector 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 &allConfigs() { - static std::vector all_configs = []() { - std::vector 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 { protected: @@ -3893,7 +3817,24 @@ EXPECT_TRUE(S->isDetached()); } +static std::vector allTestClangConfigs() { + std::vector 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