[clang] Adapt ASTMatcher to explicit(bool) specifier

Summary:
Changes:
 - add an ast matcher for deductiong guide.
 - allow isExplicit matcher for deductiong guide.
 - add hasExplicitSpecifier matcher which give access to the expression of the explicit specifier if present.

Reviewers: klimek, rsmith, aaron.ballman

Reviewed By: aaron.ballman

Subscribers: aaron.ballman, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D61552

llvm-svn: 363855
This commit is contained in:
Gauthier Harnisch 2019-06-19 18:27:56 +00:00
parent 61d7e35b22
commit e1f4ba85e5
6 changed files with 229 additions and 15 deletions

View File

@ -194,6 +194,16 @@ Example matches the operator.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('cxxDeductionGuideDecl0')"><a name="cxxDeductionGuideDecl0Anchor">cxxDeductionGuideDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDeductionGuideDecl.html">CXXDeductionGuideDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="cxxDeductionGuideDecl0"><pre>Matches user-defined and implicitly generated deduction guide.
Example matches the deduction guide.
template&lt;typename T&gt;
class X { X(int) };
X(int) -&gt; X&lt;int&gt;;
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt;</td><td class="name" onclick="toggle('cxxDestructorDecl0')"><a name="cxxDestructorDecl0Anchor">cxxDestructorDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDestructorDecl.html">CXXDestructorDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="cxxDestructorDecl0"><pre>Matches explicit C++ destructor declarations.
@ -2222,18 +2232,26 @@ cxxConstructorDecl(isDelegatingConstructor()) will match #3 and #4, but not
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructorDecl.html">CXXConstructorDecl</a>&gt;</td><td class="name" onclick="toggle('isExplicit0')"><a name="isExplicit0Anchor">isExplicit</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isExplicit0"><pre>Matches constructor and conversion declarations that are marked with
the explicit keyword.
<tr><td colspan="4" class="doc" id="isExplicit0"><pre>Matches constructor, conversion function, and deduction guide declarations
that have an explicit specifier if this explicit specifier is resolved to
true.
Given
template&lt;bool b&gt;
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) -&gt; S&lt;true&gt; // #5
explicit S(double) -&gt; S&lt;false&gt; // #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.
</pre></td></tr>
@ -2251,18 +2269,26 @@ cxxConstructorDecl(isMoveConstructor()) will match #3, but not #1 or #2.
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConversionDecl.html">CXXConversionDecl</a>&gt;</td><td class="name" onclick="toggle('isExplicit1')"><a name="isExplicit1Anchor">isExplicit</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isExplicit1"><pre>Matches constructor and conversion declarations that are marked with
the explicit keyword.
<tr><td colspan="4" class="doc" id="isExplicit1"><pre>Matches constructor, conversion function, and deduction guide declarations
that have an explicit specifier if this explicit specifier is resolved to
true.
Given
template&lt;bool b&gt;
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) -&gt; S&lt;true&gt; // #5
explicit S(double) -&gt; S&lt;false&gt; // #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.
</pre></td></tr>
@ -2317,6 +2343,30 @@ cxxConstructorDecl(hasAnyConstructorInitializer(isWritten()))
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDeductionGuideDecl.html">CXXDeductionGuideDecl</a>&gt;</td><td class="name" onclick="toggle('isExplicit2')"><a name="isExplicit2Anchor">isExplicit</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isExplicit2"><pre>Matches constructor, conversion function, and deduction guide declarations
that have an explicit specifier if this explicit specifier is resolved to
true.
Given
template&lt;bool b&gt;
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) -&gt; S&lt;true&gt; // #5
explicit S(double) -&gt; S&lt;false&gt; // #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.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>&gt;</td><td class="name" onclick="toggle('isArrow2')"><a name="isArrow2Anchor">isArrow</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isArrow2"><pre>Matches member expressions that are called with '-&gt;' as opposed
to '.'.
@ -6007,6 +6057,29 @@ with compoundStmt()
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>&gt;</td><td class="name" onclick="toggle('hasExplicitSpecifier0')"><a name="hasExplicitSpecifier0Anchor">hasExplicitSpecifier</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasExplicitSpecifier0"><pre>Matches the expression in an explicit specifier if present in the given
declaration.
Given
template&lt;bool b&gt;
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) -&gt; S&lt;true&gt; // #5
explicit S(double) -&gt; S&lt;false&gt; // #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.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>&gt;</td><td class="name" onclick="toggle('hasParameter0')"><a name="hasParameter0Anchor">hasParameter</a></td><td>unsigned N, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ParmVarDecl.html">ParmVarDecl</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasParameter0"><pre>Matches the n'th parameter of a function or an ObjC method
declaration or a block.

View File

@ -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<FunctionDecl *>(Function));
}
static ExplicitSpecifier Invalid() {
return ExplicitSpecifier(nullptr, ExplicitSpecKind::Unresolved);
}

View File

@ -1138,6 +1138,17 @@ extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXMethodDecl>
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl>
cxxConversionDecl;
/// Matches user-defined and implicitly generated deduction guide.
///
/// Example matches the deduction guide.
/// \code
/// template<typename T>
/// class X { X(int) };
/// X(int) -> X<int>;
/// \endcode
extern const internal::VariadicDynCastAllOfMatcher<Decl, CXXDeductionGuideDecl>
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<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
/// \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<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
/// \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<Expr>,
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.
///

View File

@ -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);

View File

@ -878,6 +878,15 @@ TEST(ConversionDeclaration, IsExplicit) {
cxxConversionDecl(isExplicit())));
EXPECT_TRUE(notMatches("struct S { operator int(); };",
cxxConversionDecl(isExplicit())));
EXPECT_TRUE(matchesConditionally(
"template<bool b> 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<bool b> 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<typename T> struct S { S(int);};"
"S(int) -> S<int>;",
cxxDeductionGuideDecl(isExplicit()), false,
"-std=c++17"));
EXPECT_TRUE(matchesConditionally("template<typename T> struct S { S(int);};"
"explicit S(int) -> S<int>;",
cxxDeductionGuideDecl(isExplicit()), true,
"-std=c++17"));
EXPECT_TRUE(matchesConditionally("template<typename T> struct S { S(int);};"
"explicit(true) S(int) -> S<int>;",
cxxDeductionGuideDecl(isExplicit()), true,
"-std=c++2a"));
EXPECT_TRUE(matchesConditionally("template<typename T> struct S { S(int);};"
"explicit(false) S(int) -> S<int>;",
cxxDeductionGuideDecl(isExplicit()), false,
"-std=c++2a"));
EXPECT_TRUE(matchesConditionally(
"template<typename T> struct S { S(int);};"
"template<bool b = true> explicit(b) S(int) -> S<int>;",
cxxDeductionGuideDecl(isExplicit()), false, "-std=c++2a"));
}
TEST(ConstructorDeclaration, Kinds) {

View File

@ -1735,6 +1735,56 @@ TEST(SwitchCase, MatchesEachCase) {
llvm::make_unique<VerifyIdIsBoundTo<CaseStmt>>("x", 3)));
}
TEST(Declaration, HasExplicitSpecifier) {
EXPECT_TRUE(matchesConditionally(
"void f();", functionDecl(hasExplicitSpecifier(constantExpr())), false,
"-std=c++2a"));
EXPECT_TRUE(matchesConditionally(
"template<bool b> struct S { explicit operator int(); };",
cxxConversionDecl(hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))),
false, "-std=c++2a"));
EXPECT_TRUE(matchesConditionally(
"template<bool b> 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<bool b> 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<typename T> struct S { S(int); };"
"template<bool b = true> explicit(b) S(int) -> S<int>;",
cxxDeductionGuideDecl(
hasExplicitSpecifier(constantExpr(has(cxxBoolLiteral())))),
false, "-std=c++2a"));
EXPECT_TRUE(matchesConditionally("template<typename T> struct S { S(int); };"
"explicit(true) S(int) -> S<int>;",
cxxDeductionGuideDecl(hasExplicitSpecifier(
constantExpr(has(cxxBoolLiteral())))),
true, "-std=c++2a"));
EXPECT_TRUE(matchesConditionally("template<typename T> struct S { S(int); };"
"explicit(false) S(int) -> S<int>;",
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; };",