shuaiwang created this revision.
shuaiwang added reviewers: lebedev.ri, JonasToth.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, xazax.hun.
Herald added a reviewer: george.karpenkov.

This is a follow up of https://reviews.llvm.org/D52008 and should make the 
analyzer being able to handle perfect forwardings in real world cases where 
forwardings are done through multiple layers of function calls with 
`std::forward`.

Fixes PR38891.


Repository:
  rC Clang

https://reviews.llvm.org/D52120

Files:
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===================================================================
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -373,36 +373,46 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
+  const std::string Move =
       "namespace std {"
       "template<class T> struct remove_reference { typedef T type; };"
       "template<class T> struct remove_reference<T&> { typedef T type; };"
       "template<class T> struct remove_reference<T&&> { typedef T type; };"
       "template<class T> typename std::remove_reference<T>::type&& "
-      "move(T&& t) noexcept; }"
-      "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+      "move(T&& t) noexcept {} }";
+  auto AST = tooling::buildASTFromCode(
+      Move + "void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      Move + "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
 }
 
 TEST(ExprMutationAnalyzerTest, Forward) {
-  // Technically almost the same as ByNonConstRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
+  const std::string Forward =
       "namespace std {"
       "template<class T> struct remove_reference { typedef T type; };"
       "template<class T> struct remove_reference<T&> { typedef T type; };"
       "template<class T> struct remove_reference<T&&> { typedef T type; };"
       "template<class T> T&& "
-      "forward(typename std::remove_reference<T>::type&) noexcept;"
+      "forward(typename std::remove_reference<T>::type&) noexcept {}"
       "template<class T> T&& "
-      "forward(typename std::remove_reference<T>::type&&) noexcept;"
-      "void f() { struct A {}; A x; std::forward<A &>(x); }");
-  const auto Results =
+      "forward(typename std::remove_reference<T>::type&&) noexcept {} }";
+  auto AST = tooling::buildASTFromCode(
+      Forward + "void f() { struct A {}; A x; std::forward<A &>(x); }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      Forward + "void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
-              ElementsAre("std::forward<A &>(x)"));
+              ElementsAre("std::forward<A &>(x) = y"));
 }
 
 TEST(ExprMutationAnalyzerTest, CallUnresolved) {
@@ -639,6 +649,26 @@
       "void f() { int x; S s(x); }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+      "namespace std {"
+      "template<class T> struct remove_reference { typedef T type; };"
+      "template<class T> struct remove_reference<T&> { typedef T type; };"
+      "template<class T> struct remove_reference<T&&> { typedef T type; };"
+      "template<class T> T&& "
+      "forward(typename std::remove_reference<T>::type& t) noexcept"
+      "{ return t; }"
+      "template<class T> T&& "
+      "forward(typename std::remove_reference<T>::type&& t) noexcept"
+      "{ return t; } }"
+      "template <class... Args> void u(Args&...);"
+      "template <class... Args> void h(Args&&... args)"
+      "{ u(std::forward<Args>(args)...); }"
+      "template <class... Args> void g(Args&&... args)"
+      "{ h(std::forward<Args>(args)...); }"
+      "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 }
 
 TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
@@ -683,6 +713,26 @@
       "void f() { int x; S s(x); }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      "namespace std {"
+      "template<class T> struct remove_reference { typedef T type; };"
+      "template<class T> struct remove_reference<T&> { typedef T type; };"
+      "template<class T> struct remove_reference<T&&> { typedef T type; };"
+      "template<class T> T&& "
+      "forward(typename std::remove_reference<T>::type& t) noexcept"
+      "{ return t; }"
+      "template<class T> T&& "
+      "forward(typename std::remove_reference<T>::type&& t) noexcept"
+      "{ return t; } }"
+      "template <class... Args> void u(Args...);"
+      "template <class... Args> void h(Args&&... args)"
+      "{ u(std::forward<Args>(args)...); }"
+      "template <class... Args> void g(Args&&... args)"
+      "{ h(std::forward<Args>(args)...); }"
+      "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
Index: lib/Analysis/ExprMutationAnalyzer.cpp
===================================================================
--- lib/Analysis/ExprMutationAnalyzer.cpp
+++ lib/Analysis/ExprMutationAnalyzer.cpp
@@ -261,7 +261,16 @@
                                        nonConstReferenceType()))))
                         .bind("expr")),
             Stm, Context);
-  return findExprMutation(Casts);
+  if (const Stmt *S = findExprMutation(Casts))
+    return S;
+  // Treat std::{move,forward} as cast.
+  const auto Calls =
+      match(findAll(callExpr(callee(namedDecl(
+                                 hasAnyName("::std::move", "::std::forward"))),
+                             hasArgument(0, equalsNode(Exp)))
+                        .bind("expr")),
+            Stm, Context);
+  return findExprMutation(Calls);
 }
 
 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
@@ -318,7 +327,9 @@
   const auto IsInstantiated = hasDeclaration(isInstantiated());
   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
   const auto Matches = match(
-      findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
+      findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
+                                  unless(callee(namedDecl(hasAnyName(
+                                      "::std::move", "::std::forward"))))),
                          cxxConstructExpr(NonConstRefParam, IsInstantiated,
                                           FuncDecl)))
                   .bind("expr")),
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to