forked from OSchip/llvm-project
Added AST matcher for ignoring elidable constructors
Summary: Added AST matcher for ignoring elidable move constructors Reviewers: hokein, gribozavr Reviewed By: hokein, gribozavr Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D63149 Patch by Johan Vikström. llvm-svn: 363262
This commit is contained in:
parent
0eb763c559
commit
31d68804fd
|
@ -5728,6 +5728,32 @@ Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>></td><td class="name" onclick="toggle('ignoringElidableConstructorCall0')"><a name="ignoringElidableConstructorCall0Anchor">ignoringElidableConstructorCall</a></td><td>ast_matchers::Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="ignoringElidableConstructorCall0"><pre>Matches expressions that match InnerMatcher that are possibly wrapped in an
|
||||
elidable constructor.
|
||||
|
||||
In C++17 copy elidable constructors are no longer being
|
||||
generated in the AST as it is not permitted by the standard. They are
|
||||
however part of the AST in C++14 and earlier. Therefore, to write a matcher
|
||||
that works in all language modes, the matcher has to skip elidable
|
||||
constructor AST nodes if they appear in the AST. This matcher can be used to
|
||||
skip those elidable constructors.
|
||||
|
||||
Given
|
||||
|
||||
struct H {};
|
||||
H G();
|
||||
void f() {
|
||||
H D = G();
|
||||
}
|
||||
|
||||
``varDecl(hasInitializer(any(
|
||||
ignoringElidableConstructorCall(callExpr()),
|
||||
exprWithCleanups(ignoringElidableConstructorCall(callExpr()))))``
|
||||
matches ``H D = G()``
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>></td><td class="name" onclick="toggle('ignoringImpCasts0')"><a name="ignoringImpCasts0Anchor">ignoringImpCasts</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="ignoringImpCasts0"><pre>Matches expressions that match InnerMatcher after any implicit casts
|
||||
are stripped off.
|
||||
|
|
|
@ -6452,6 +6452,44 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Matches expressions that match InnerMatcher that are possibly wrapped in an
|
||||
/// elidable constructor.
|
||||
///
|
||||
/// In C++17 copy elidable constructors are no longer being
|
||||
/// generated in the AST as it is not permitted by the standard. They are
|
||||
/// however part of the AST in C++14 and earlier. Therefore, to write a matcher
|
||||
/// that works in all language modes, the matcher has to skip elidable
|
||||
/// constructor AST nodes if they appear in the AST. This matcher can be used to
|
||||
/// skip those elidable constructors.
|
||||
///
|
||||
/// Given
|
||||
///
|
||||
/// \code
|
||||
/// struct H {};
|
||||
/// H G();
|
||||
/// void f() {
|
||||
/// H D = G();
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// ``varDecl(hasInitializer(any(
|
||||
/// ignoringElidableConstructorCall(callExpr()),
|
||||
/// exprWithCleanups(ignoringElidableConstructorCall(callExpr()))))``
|
||||
/// matches ``H D = G()``
|
||||
AST_MATCHER_P(Expr, ignoringElidableConstructorCall,
|
||||
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
|
||||
if (const auto *CtorExpr = dyn_cast<CXXConstructExpr>(&Node)) {
|
||||
if (CtorExpr->isElidable()) {
|
||||
if (const auto *MaterializeTemp =
|
||||
dyn_cast<MaterializeTemporaryExpr>(CtorExpr->getArg(0))) {
|
||||
return InnerMatcher.matches(*MaterializeTemp->GetTemporaryExpr(),
|
||||
Finder, Builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
return InnerMatcher.matches(Node, Finder, Builder);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// OpenMP handling.
|
||||
//----------------------------------------------------------------------------//
|
||||
|
|
|
@ -320,6 +320,7 @@ RegistryMaps::RegistryMaps() {
|
|||
REGISTER_MATCHER(hasUnqualifiedDesugaredType);
|
||||
REGISTER_MATCHER(hasValueType);
|
||||
REGISTER_MATCHER(ifStmt);
|
||||
REGISTER_MATCHER(ignoringElidableConstructorCall);
|
||||
REGISTER_MATCHER(ignoringImpCasts);
|
||||
REGISTER_MATCHER(ignoringImplicit);
|
||||
REGISTER_MATCHER(ignoringParenCasts);
|
||||
|
|
|
@ -566,6 +566,74 @@ TEST(Matcher, BindMatchedNodes) {
|
|||
llvm::make_unique<VerifyIdIsBoundTo<CXXMemberCallExpr>>("x")));
|
||||
}
|
||||
|
||||
TEST(Matcher, IgnoresElidableConstructors) {
|
||||
EXPECT_TRUE(
|
||||
matches("struct H {};"
|
||||
"template<typename T> H B(T A);"
|
||||
"void f() {"
|
||||
" H D1;"
|
||||
" D1 = B(B(1));"
|
||||
"}",
|
||||
cxxOperatorCallExpr(hasArgument(
|
||||
1, callExpr(hasArgument(
|
||||
0, ignoringElidableConstructorCall(callExpr()))))),
|
||||
LanguageMode::Cxx11OrLater));
|
||||
EXPECT_TRUE(
|
||||
matches("struct H {};"
|
||||
"template<typename T> H B(T A);"
|
||||
"void f() {"
|
||||
" H D1;"
|
||||
" D1 = B(1);"
|
||||
"}",
|
||||
cxxOperatorCallExpr(hasArgument(
|
||||
1, callExpr(hasArgument(0, ignoringElidableConstructorCall(
|
||||
integerLiteral()))))),
|
||||
LanguageMode::Cxx11OrLater));
|
||||
EXPECT_TRUE(matches(
|
||||
"struct H {};"
|
||||
"H G();"
|
||||
"void f() {"
|
||||
" H D = G();"
|
||||
"}",
|
||||
varDecl(hasInitializer(anyOf(
|
||||
ignoringElidableConstructorCall(callExpr()),
|
||||
exprWithCleanups(has(ignoringElidableConstructorCall(callExpr())))))),
|
||||
LanguageMode::Cxx11OrLater));
|
||||
}
|
||||
|
||||
TEST(Matcher, IgnoresElidableInReturn) {
|
||||
auto matcher = expr(ignoringElidableConstructorCall(declRefExpr()));
|
||||
EXPECT_TRUE(matches("struct H {};"
|
||||
"H f() {"
|
||||
" H g;"
|
||||
" return g;"
|
||||
"}",
|
||||
matcher, LanguageMode::Cxx11OrLater));
|
||||
EXPECT_TRUE(notMatches("struct H {};"
|
||||
"H f() {"
|
||||
" return H();"
|
||||
"}",
|
||||
matcher, LanguageMode::Cxx11OrLater));
|
||||
}
|
||||
|
||||
TEST(Matcher, IgnoreElidableConstructorDoesNotMatchConstructors) {
|
||||
EXPECT_TRUE(matches("struct H {};"
|
||||
"void f() {"
|
||||
" H D;"
|
||||
"}",
|
||||
varDecl(hasInitializer(
|
||||
ignoringElidableConstructorCall(cxxConstructExpr()))),
|
||||
LanguageMode::Cxx11OrLater));
|
||||
}
|
||||
|
||||
TEST(Matcher, IgnoresElidableDoesNotPreventMatches) {
|
||||
EXPECT_TRUE(matches("void f() {"
|
||||
" int D = 10;"
|
||||
"}",
|
||||
expr(ignoringElidableConstructorCall(integerLiteral())),
|
||||
LanguageMode::Cxx11OrLater));
|
||||
}
|
||||
|
||||
TEST(Matcher, BindTheSameNameInAlternatives) {
|
||||
StatementMatcher matcher = anyOf(
|
||||
binaryOperator(hasOperatorName("+"),
|
||||
|
@ -914,10 +982,10 @@ TEST(isConstexpr, MatchesConstexprDeclarations) {
|
|||
varDecl(hasName("foo"), isConstexpr())));
|
||||
EXPECT_TRUE(matches("constexpr int bar();",
|
||||
functionDecl(hasName("bar"), isConstexpr())));
|
||||
EXPECT_TRUE(matchesConditionally("void baz() { if constexpr(1 > 0) {} }",
|
||||
ifStmt(isConstexpr()), true, "-std=c++17"));
|
||||
EXPECT_TRUE(matchesConditionally("void baz() { if (1 > 0) {} }",
|
||||
ifStmt(isConstexpr()), false, "-std=c++17"));
|
||||
EXPECT_TRUE(matches("void baz() { if constexpr(1 > 0) {} }",
|
||||
ifStmt(isConstexpr()), LanguageMode::Cxx17OrLater));
|
||||
EXPECT_TRUE(notMatches("void baz() { if (1 > 0) {} }", ifStmt(isConstexpr()),
|
||||
LanguageMode::Cxx17OrLater));
|
||||
}
|
||||
|
||||
TEST(TemplateArgumentCountIs, Matches) {
|
||||
|
|
|
@ -57,6 +57,17 @@ private:
|
|||
const std::unique_ptr<BoundNodesCallback> FindResultReviewer;
|
||||
};
|
||||
|
||||
enum class LanguageMode {
|
||||
Cxx11,
|
||||
Cxx14,
|
||||
Cxx17,
|
||||
Cxx2a,
|
||||
Cxx11OrLater,
|
||||
Cxx14OrLater,
|
||||
Cxx17OrLater,
|
||||
Cxx2aOrLater
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
testing::AssertionResult matchesConditionally(
|
||||
const std::string &Code, const T &AMatcher, bool ExpectMatch,
|
||||
|
@ -116,14 +127,71 @@ testing::AssertionResult matchesConditionally(
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
|
||||
return matchesConditionally(Code, AMatcher, true, "-std=c++11");
|
||||
testing::AssertionResult
|
||||
matchesConditionally(const std::string &Code, const T &AMatcher,
|
||||
bool ExpectMatch, const LanguageMode &Mode) {
|
||||
std::vector<LanguageMode> LangModes;
|
||||
switch (Mode) {
|
||||
case LanguageMode::Cxx11:
|
||||
case LanguageMode::Cxx14:
|
||||
case LanguageMode::Cxx17:
|
||||
case LanguageMode::Cxx2a:
|
||||
LangModes = {Mode};
|
||||
break;
|
||||
case LanguageMode::Cxx11OrLater:
|
||||
LangModes = {LanguageMode::Cxx11, LanguageMode::Cxx14, LanguageMode::Cxx17,
|
||||
LanguageMode::Cxx2a};
|
||||
break;
|
||||
case LanguageMode::Cxx14OrLater:
|
||||
LangModes = {LanguageMode::Cxx14, LanguageMode::Cxx17, LanguageMode::Cxx2a};
|
||||
break;
|
||||
case LanguageMode::Cxx17OrLater:
|
||||
LangModes = {LanguageMode::Cxx17, LanguageMode::Cxx2a};
|
||||
break;
|
||||
case LanguageMode::Cxx2aOrLater:
|
||||
LangModes = {LanguageMode::Cxx2a};
|
||||
}
|
||||
|
||||
for (auto Mode : LangModes) {
|
||||
std::string LangModeArg;
|
||||
switch (Mode) {
|
||||
case LanguageMode::Cxx11:
|
||||
LangModeArg = "-std=c++11";
|
||||
break;
|
||||
case LanguageMode::Cxx14:
|
||||
LangModeArg = "-std=c++14";
|
||||
break;
|
||||
case LanguageMode::Cxx17:
|
||||
LangModeArg = "-std=c++17";
|
||||
break;
|
||||
case LanguageMode::Cxx2a:
|
||||
LangModeArg = "-std=c++2a";
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Invalid language mode");
|
||||
}
|
||||
|
||||
auto Result =
|
||||
matchesConditionally(Code, AMatcher, ExpectMatch, LangModeArg);
|
||||
if (!Result)
|
||||
return Result;
|
||||
}
|
||||
|
||||
return testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
testing::AssertionResult notMatches(const std::string &Code,
|
||||
const T &AMatcher) {
|
||||
return matchesConditionally(Code, AMatcher, false, "-std=c++11");
|
||||
testing::AssertionResult
|
||||
matches(const std::string &Code, const T &AMatcher,
|
||||
const LanguageMode &Mode = LanguageMode::Cxx11) {
|
||||
return matchesConditionally(Code, AMatcher, true, Mode);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
testing::AssertionResult
|
||||
notMatches(const std::string &Code, const T &AMatcher,
|
||||
const LanguageMode &Mode = LanguageMode::Cxx11) {
|
||||
return matchesConditionally(Code, AMatcher, false, Mode);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
Loading…
Reference in New Issue