diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -5728,6 +5728,30 @@ +
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(ignoringElidableMoveConstructorCall(callExpr())))`` +matches ``H D = G()`` +
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 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -6452,6 +6452,56 @@ 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(ignoringElidableMoveConstructorCall(callExpr())))`` +/// matches ``H D = G()`` +AST_MATCHER_P(Expr, ignoringElidableMoveConstructorCall, + ast_matchers::internal::Matcher, InnerMatcher) { + + if (const auto *expr_cleanups = dyn_cast (&Node)) { + bool matches = false; + for (const auto *obj : expr_cleanups->children()) { + if (const auto *cxx_construct_expr = dyn_cast (obj)) { + matches = matches | ignoringElidableMoveConstructorCall(InnerMatcher) + .matches(*cxx_construct_expr, Finder, Builder); + } + } + return matches; + } + + if (const auto *cxx_construct_expr = dyn_cast (&Node)) { + if (cxx_construct_expr->isElidable()) { + if (const auto *materialize_temp = dyn_cast ( + cxx_construct_expr->getArg(0))) { + const auto *first_child = + dyn_cast (*materialize_temp->children().begin()); + return InnerMatcher.matches(*first_child, Finder, Builder); + } + return InnerMatcher.matches(*cxx_construct_expr, 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 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -320,6 +320,7 @@ REGISTER_MATCHER(hasUnqualifiedDesugaredType); REGISTER_MATCHER(hasValueType); REGISTER_MATCHER(ifStmt); + REGISTER_MATCHER(ignoringElidableMoveConstructorCall); REGISTER_MATCHER(ignoringImpCasts); REGISTER_MATCHER(ignoringImplicit); REGISTER_MATCHER(ignoringParenCasts); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -566,6 +566,81 @@ llvm::make_unique >("x"))); } +TEST(Matcher, IgnoresElidableConstructors) { + auto matcher1 = cxxOperatorCallExpr(hasArgument( + 1, callExpr(hasArgument( + 0, ignoringElidableMoveConstructorCall(callExpr().bind("c")))))); + auto matcher2 = cxxOperatorCallExpr(hasArgument( + 1, callExpr(hasArgument(0, ignoringElidableMoveConstructorCall( + integerLiteral().bind("c")))))); + auto matcher3 = + varDecl(hasInitializer(ignoringElidableMoveConstructorCall(callExpr()))); + + auto code1 = "struct H {};" + "template H B(T A);" + "void f(){" + "H D1;" + "D1=B(B(1));" + "}"; + auto code2 = "struct H {};" + "template H B(T A);" + "void f(){" + "H D1;" + "D1=B(1);" + "}"; + auto code3 = "struct H {};" + "H G();" + "void f(){" + "H D = G();" + "}"; + + EXPECT_TRUE(matchesConditionally(code1, matcher1, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code1, matcher1, true, "-std=c++17")); + + EXPECT_TRUE(matchesConditionally(code2, matcher2, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code2, matcher2, true, "-std=c++17")); + + EXPECT_TRUE(matchesConditionally(code3, matcher3, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code3, matcher3, true, "-std=c++17")); +} + +TEST(Matcher, IgnoresElidableInReturn) { + auto matcher = expr(ignoringElidableMoveConstructorCall(declRefExpr())); + auto code1 = "struct H{};" + "H f(){" + "H g;" + "return g;" + "}"; + auto code2 = "struct H{};" + "H f(){" + "return H();" + "}"; + EXPECT_TRUE(matchesConditionally(code1, matcher, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code1, matcher, true, "-std=c++17")); + EXPECT_TRUE(matchesConditionally(code2, matcher, false, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code2, matcher, false, "-std=c++17")); +} + +TEST(Matcher, IgnoreElidableConstructorDoesNotMatchConstructors) { + auto matcher = varDecl( + hasInitializer(ignoringElidableMoveConstructorCall(cxxConstructExpr()))); + auto code = "struct H {};" + "void f(){" + "H D;" + "}"; + EXPECT_TRUE(matchesConditionally(code, matcher, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code, matcher, true, "-std=c++17")); +}; + +TEST(Matcher, IgnoresElidableDoesNotPreventMatches) { + auto matcher = expr(ignoringElidableMoveConstructorCall(integerLiteral())); + auto code = "void f(){" + "int D = 10;" + "}"; + EXPECT_TRUE(matchesConditionally(code, matcher, true, "-std=c++14")); + EXPECT_TRUE(matchesConditionally(code, matcher, true, "-std=c++17")); +} + TEST(Matcher, BindTheSameNameInAlternatives) { StatementMatcher matcher = anyOf( binaryOperator(hasOperatorName("+"),