diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index 47f2c8a489ec..c93394096f2f 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -194,6 +194,16 @@ Example matches the operator. +
Matches user-defined and implicitly generated deduction guide. + +Example matches the deduction guide. + template<typename T> + class X { X(int) }; + X(int) -> X<int>; +
Matches explicit C++ destructor declarations. @@ -2222,18 +2232,26 @@ cxxConstructorDecl(isDelegatingConstructor()) will match #3 and #4, but not- Matcher<CXXConstructorDecl> isExplicit Matches constructor and conversion declarations that are marked with -the explicit keyword. +@@ -2251,18 +2269,26 @@ cxxConstructorDecl(isMoveConstructor()) will match #3, but not #1 or #2. Matches constructor, conversion function, and deduction guide declarations +that have an explicit specifier if this explicit specifier is resolved to +true. Given + template<bool b> struct S { S(int); // #1 explicit S(double); // #2 operator int(); // #3 explicit operator bool(); // #4 + explicit(false) S(bool) // # 7 + explicit(true) S(char) // # 8 + explicit(b) S(S) // # 9 }; -cxxConstructorDecl(isExplicit()) will match #2, but not #1. + S(int) -> S<true> // #5 + explicit S(double) -> S<false> // #6 +cxxConstructorDecl(isExplicit()) will match #2 and #8, but not #1, #7 or #9. cxxConversionDecl(isExplicit()) will match #4, but not #3. +cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5.- Matcher<CXXConversionDecl> isExplicit + Matches constructor and conversion declarations that are marked with -the explicit keyword. +@@ -2317,6 +2343,30 @@ cxxConstructorDecl(hasAnyConstructorInitializer(isWritten())) Matches constructor, conversion function, and deduction guide declarations +that have an explicit specifier if this explicit specifier is resolved to +true. Given + template<bool b> struct S { S(int); // #1 explicit S(double); // #2 operator int(); // #3 explicit operator bool(); // #4 + explicit(false) S(bool) // # 7 + explicit(true) S(char) // # 8 + explicit(b) S(S) // # 9 }; -cxxConstructorDecl(isExplicit()) will match #2, but not #1. + S(int) -> S<true> // #5 + explicit S(double) -> S<false> // #6 +cxxConstructorDecl(isExplicit()) will match #2 and #8, but not #1, #7 or #9. cxxConversionDecl(isExplicit()) will match #4, but not #3. +cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5.+ Matcher<CXXDeductionGuideDecl> isExplicit + + Matches constructor, conversion function, and deduction guide declarations +that have an explicit specifier if this explicit specifier is resolved to +true. + +Given + template<bool b> + struct S { + S(int); // #1 + explicit S(double); // #2 + operator int(); // #3 + explicit operator bool(); // #4 + explicit(false) S(bool) // # 7 + explicit(true) S(char) // # 8 + explicit(b) S(S) // # 9 + }; + S(int) -> S<true> // #5 + explicit S(double) -> S<false> // #6 +cxxConstructorDecl(isExplicit()) will match #2 and #8, but not #1, #7 or #9. +cxxConversionDecl(isExplicit()) will match #4, but not #3. +cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5. +Matcher<CXXDependentScopeMemberExpr> isArrow + Matches member expressions that are called with '->' as opposed to '.'. @@ -6007,6 +6057,29 @@ with compoundStmt()+ Matcher<FunctionDecl> hasExplicitSpecifier Matcher<Expr> InnerMatcher + + Matches the expression in an explicit specifier if present in the given +declaration. + +Given + template<bool b> + struct S { + S(int); // #1 + explicit S(double); // #2 + operator int(); // #3 + explicit operator bool(); // #4 + explicit(false) S(bool) // # 7 + explicit(true) S(char) // # 8 + explicit(b) S(S) // # 9 + }; + S(int) -> S<true> // #5 + explicit S(double) -> S<false> // #6 +cxxConstructorDecl(hasExplicitSpecifier(constantExpr())) will match #7, #8 and #9, but not #1 or #2. +cxxConversionDecl(hasExplicitSpecifier(constantExpr())) will not match #3 or #4. +cxxDeductionGuideDecl(hasExplicitSpecifier(constantExpr())) will not match #5 or #6. +Matcher<FunctionDecl> hasParameter unsigned N, Matcher<ParmVarDecl> InnerMatcher Matches the n'th parameter of a function or an ObjC method declaration or a block. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 6133d2fce754..0ec8c9b4aefd 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2033,6 +2033,9 @@ public: // if the given declaration has no explicit. the returned explicit specifier // is defaulted. .isSpecified() will be false. static ExplicitSpecifier getFromDecl(FunctionDecl *Function); + static const ExplicitSpecifier getFromDecl(const FunctionDecl *Function) { + return getFromDecl(const_cast(Function)); + } static ExplicitSpecifier Invalid() { return ExplicitSpecifier(nullptr, ExplicitSpecKind::Unresolved); } diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index d3ebbff42e70..063d8217d9aa 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1138,6 +1138,17 @@ extern const internal::VariadicDynCastAllOfMatcher extern const internal::VariadicDynCastAllOfMatcher cxxConversionDecl; +/// Matches user-defined and implicitly generated deduction guide. +/// +/// Example matches the deduction guide. +/// \code +/// template +/// class X { X(int) }; +/// X(int) -> X ; +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher + cxxDeductionGuideDecl; + /// Matches variable declarations. /// /// Note: this does not match declarations of member variables, which are @@ -6154,29 +6165,63 @@ AST_MATCHER(CXXConstructorDecl, isDelegatingConstructor) { return Node.isDelegatingConstructor(); } -/// Matches constructor and conversion declarations that are marked with -/// the explicit keyword. +/// Matches constructor, conversion function, and deduction guide declarations +/// that have an explicit specifier if this explicit specifier is resolved to +/// true. /// /// Given /// \code +/// template /// struct S { /// S(int); // #1 /// explicit S(double); // #2 /// operator int(); // #3 /// explicit operator bool(); // #4 +/// explicit(false) S(bool) // # 7 +/// explicit(true) S(char) // # 8 +/// explicit(b) S(S) // # 9 /// }; +/// S(int) -> S // #5 +/// explicit S(double) -> S // #6 /// \endcode -/// cxxConstructorDecl(isExplicit()) will match #2, but not #1. +/// cxxConstructorDecl(isExplicit()) will match #2 and #8, but not #1, #7 or #9. /// cxxConversionDecl(isExplicit()) will match #4, but not #3. -AST_POLYMORPHIC_MATCHER(isExplicit, - AST_POLYMORPHIC_SUPPORTED_TYPES(CXXConstructorDecl, - CXXConversionDecl)) { - // FIXME : it's not clear whether this should match a dependent - // explicit(....). this matcher should also be able to match - // CXXDeductionGuideDecl with explicit specifier. +/// cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5. +AST_POLYMORPHIC_MATCHER(isExplicit, AST_POLYMORPHIC_SUPPORTED_TYPES( + CXXConstructorDecl, CXXConversionDecl, + CXXDeductionGuideDecl)) { return Node.isExplicit(); } +/// Matches the expression in an explicit specifier if present in the given +/// declaration. +/// +/// Given +/// \code +/// template +/// struct S { +/// S(int); // #1 +/// explicit S(double); // #2 +/// operator int(); // #3 +/// explicit operator bool(); // #4 +/// explicit(false) S(bool) // # 7 +/// explicit(true) S(char) // # 8 +/// explicit(b) S(S) // # 9 +/// }; +/// S(int) -> S // #5 +/// explicit S(double) -> S // #6 +/// \endcode +/// cxxConstructorDecl(hasExplicitSpecifier(constantExpr())) will match #7, #8 and #9, but not #1 or #2. +/// cxxConversionDecl(hasExplicitSpecifier(constantExpr())) will not match #3 or #4. +/// cxxDeductionGuideDecl(hasExplicitSpecifier(constantExpr())) will not match #5 or #6. +AST_MATCHER_P(FunctionDecl, hasExplicitSpecifier, internal::Matcher , + InnerMatcher) { + ExplicitSpecifier ES = ExplicitSpecifier::getFromDecl(&Node); + if (!ES.getExpr()) + return false; + return InnerMatcher.matches(*ES.getExpr(), Finder, Builder); +} + /// Matches function and namespace declarations that are marked with /// the inline keyword. /// diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 8c37689a4c82..33058053571a 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -169,6 +169,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(cxxConstructorDecl); REGISTER_MATCHER(cxxConversionDecl); REGISTER_MATCHER(cxxCtorInitializer); + REGISTER_MATCHER(cxxDeductionGuideDecl); REGISTER_MATCHER(cxxDefaultArgExpr); REGISTER_MATCHER(cxxDeleteExpr); REGISTER_MATCHER(cxxDependentScopeMemberExpr); @@ -267,6 +268,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(hasEitherOperand); REGISTER_MATCHER(hasElementType); REGISTER_MATCHER(hasElse); + REGISTER_MATCHER(hasExplicitSpecifier); REGISTER_MATCHER(hasExternalFormalLinkage); REGISTER_MATCHER(hasFalseExpression); REGISTER_MATCHER(hasGlobalStorage); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 5515680da6e3..8ab472e9dc8f 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -878,6 +878,15 @@ TEST(ConversionDeclaration, IsExplicit) { cxxConversionDecl(isExplicit()))); EXPECT_TRUE(notMatches("struct S { operator int(); };", cxxConversionDecl(isExplicit()))); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) operator int(); };", + cxxConversionDecl(isExplicit()), false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(true) operator int(); };", + cxxConversionDecl(isExplicit()), true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(false) operator int(); };", + cxxConversionDecl(isExplicit()), false, "-std=c++2a")); } TEST(Matcher, ArgumentCount) { @@ -1197,6 +1206,38 @@ TEST(ConstructorDeclaration, IsExplicit) { cxxConstructorDecl(isExplicit()))); EXPECT_TRUE(notMatches("struct S { S(int); };", cxxConstructorDecl(isExplicit()))); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) S(int);};", + cxxConstructorDecl(isExplicit()), false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("struct S { explicit(true) S(int);};", + cxxConstructorDecl(isExplicit()), true, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("struct S { explicit(false) S(int);};", + cxxConstructorDecl(isExplicit()), false, + "-std=c++2a")); +} + +TEST(DeductionGuideDeclaration, IsExplicit) { + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "S(int) -> S ;", + cxxDeductionGuideDecl(isExplicit()), false, + "-std=c++17")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit S(int) -> S ;", + cxxDeductionGuideDecl(isExplicit()), true, + "-std=c++17")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit(true) S(int) -> S ;", + cxxDeductionGuideDecl(isExplicit()), true, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit(false) S(int) -> S ;", + cxxDeductionGuideDecl(isExplicit()), false, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "template struct S { S(int);};" + "template explicit(b) S(int) -> S ;", + cxxDeductionGuideDecl(isExplicit()), false, "-std=c++2a")); } TEST(ConstructorDeclaration, Kinds) { diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index dafc8c52e9a8..7c17a9b85a86 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1735,6 +1735,56 @@ TEST(SwitchCase, MatchesEachCase) { llvm::make_unique >("x", 3))); } +TEST(Declaration, HasExplicitSpecifier) { + EXPECT_TRUE(matchesConditionally( + "void f();", functionDecl(hasExplicitSpecifier(constantExpr())), false, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit operator int(); };", + cxxConversionDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(true) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(false) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) S(int); };", + cxxConstructorDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(true) S(int); };", + cxxConstructorDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(false) S(int); };", + cxxConstructorDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "template struct S { S(int); };" + "template explicit(b) S(int) -> S ;", + cxxDeductionGuideDecl( + hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))), + false, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int); };" + "explicit(true) S(int) -> S ;", + cxxDeductionGuideDecl(hasExplicitSpecifier( + constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int); };" + "explicit(false) S(int) -> S ;", + cxxDeductionGuideDecl(hasExplicitSpecifier( + constantExpr(has(cxxBoolLiteral())))), + true, "-std=c++2a")); +} + TEST(ForEachConstructorInitializer, MatchesInitializers) { EXPECT_TRUE(matches( "struct X { X() : i(42), j(42) {} int i, j; };",