Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1138,6 +1138,17 @@ extern const internal::VariadicDynCastAllOfMatcher cxxConversionDecl; +/// Matches deduction guide declarations. +/// +/// 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 @@ 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. /// 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. +/// cxxConstructorDecl(isExplicit()) will match #8, but not #7 or #9. +AST_POLYMORPHIC_MATCHER(isExplicit, AST_POLYMORPHIC_SUPPORTED_TYPES( + CXXConstructorDecl, CXXConversionDecl, + CXXDeductionGuideDecl)) { return Node.isExplicit(); } +/// Matches constructor, conversion function and deduction guide declarations +/// that have an explicit specifier. +/// +/// 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, #7, #8 and #9, but not #1. +/// cxxConversionDecl(isExplicit()) will match #4, but not #3. +/// cxxDeductionGuideDecl(isExplicit()) will match #6, but not #5. +AST_POLYMORPHIC_MATCHER( + hasExplicitSpecifier, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXConstructorDecl, CXXConversionDecl, + CXXDeductionGuideDecl)) { + return Node.getExplicitSpecifier().isSpecified(); +} + /// Matches function and namespace declarations that are marked with /// the inline keyword. /// Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -168,6 +168,7 @@ REGISTER_MATCHER(cxxConstructExpr); REGISTER_MATCHER(cxxConstructorDecl); REGISTER_MATCHER(cxxConversionDecl); + REGISTER_MATCHER(cxxDeductionGuideDecl); REGISTER_MATCHER(cxxCtorInitializer); REGISTER_MATCHER(cxxDefaultArgExpr); REGISTER_MATCHER(cxxDeleteExpr); Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -810,6 +810,31 @@ 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(ConversionDeclaration, HasExplicitSpecifier) { + EXPECT_TRUE(matches("struct S { explicit operator int(); };", + cxxConversionDecl(hasExplicitSpecifier()))); + EXPECT_TRUE(notMatches("struct S { operator int(); };", + cxxConversionDecl(hasExplicitSpecifier()))); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier()), true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(true) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier()), true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally( + "struct S { explicit(false) operator int(); };", + cxxConversionDecl(hasExplicitSpecifier()), true, "-std=c++2a")); } TEST(Matcher, ArgumentCount) { @@ -1129,6 +1154,77 @@ 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(ConstructorDeclaration, HasExplicitSpecifier) { + EXPECT_TRUE(matches("struct S { explicit S(int); };", + cxxConstructorDecl(hasExplicitSpecifier()))); + EXPECT_TRUE(notMatches("struct S { S(int); };", + cxxConstructorDecl(hasExplicitSpecifier()))); + EXPECT_TRUE(matchesConditionally( + "template struct S { explicit(b) S(int);};", + cxxConstructorDecl(hasExplicitSpecifier()), true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("struct S { explicit(true) S(int);};", + cxxConstructorDecl(hasExplicitSpecifier()), + true, "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("struct S { explicit(false) S(int);};", + cxxConstructorDecl(hasExplicitSpecifier()), + true, "-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(DeductionGuideDeclaration, HasExplicitSpecifier) { + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "S(int) -> S;", + cxxDeductionGuideDecl(hasExplicitSpecifier()), false, + "-std=c++17")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit S(int) -> S;", + cxxDeductionGuideDecl(hasExplicitSpecifier()), true, + "-std=c++17")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit(true) S(int) -> S;", + cxxDeductionGuideDecl(hasExplicitSpecifier()), true, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "explicit(false) S(int) -> S;", + cxxDeductionGuideDecl(hasExplicitSpecifier()), true, + "-std=c++2a")); + EXPECT_TRUE(matchesConditionally("template struct S { S(int);};" + "template explicit(b) S(int) -> S;", + cxxDeductionGuideDecl(hasExplicitSpecifier()), true, + "-std=c++2a")); } TEST(ConstructorDeclaration, Kinds) {