diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2019-06-13 13:48:24 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2019-06-13 13:48:24 +0000 |
commit | 31d68804fd4612f0ba1c99ca6686c3825044b46d (patch) | |
tree | 374634b3c90845b43e13ce8e2836e616305ba9dd | |
parent | 0eb763c5597590710b33e8b8f55bc74d40028c30 (diff) | |
download | bcm5719-llvm-31d68804fd4612f0ba1c99ca6686c3825044b46d.tar.gz bcm5719-llvm-31d68804fd4612f0ba1c99ca6686c3825044b46d.zip |
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
-rw-r--r-- | clang/docs/LibASTMatchersReference.html | 26 | ||||
-rw-r--r-- | clang/include/clang/ASTMatchers/ASTMatchers.h | 38 | ||||
-rw-r--r-- | clang/lib/ASTMatchers/Dynamic/Registry.cpp | 1 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp | 76 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/ASTMatchersTest.h | 78 |
5 files changed, 210 insertions, 9 deletions
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index 255ec4271e3..47f2c8a489e 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -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. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index d973b48a176..d3ebbff42e7 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -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. //----------------------------------------------------------------------------// diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index fedb9dd2abe..8c37689a4c8 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -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); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 01b168da2b7..5515680da6e 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -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) { diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h index 78c551f806f..745122a2819 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTest.h +++ b/clang/unittests/ASTMatchers/ASTMatchersTest.h @@ -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 +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) { - return matchesConditionally(Code, AMatcher, false, "-std=c++11"); +testing::AssertionResult +notMatches(const std::string &Code, const T &AMatcher, + const LanguageMode &Mode = LanguageMode::Cxx11) { + return matchesConditionally(Code, AMatcher, false, Mode); } template <typename T> |