r342409 - [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

Shuai Wang via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 17 13:10:56 PDT 2018


Author: shuaiwang
Date: Mon Sep 17 13:10:56 2018
New Revision: 342409

URL: http://llvm.org/viewvc/llvm-project?rev=342409&view=rev
Log:
[analyzer] Treat std::{move,forward} as casts in ExprMutationAnalyzer.

Summary:
This is a follow up of 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.

Reviewers: lebedev.ri, JonasToth, george.karpenkov

Subscribers: xazax.hun, szepet, a.sidorin, mikhail.ramalho, Szelethus, cfe-commits

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

Modified:
    cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
    cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Modified: cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp?rev=342409&r1=342408&r2=342409&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp (original)
+++ cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp Mon Sep 17 13:10:56 2018
@@ -304,7 +304,16 @@ const Stmt *ExprMutationAnalyzer::findCa
                                        nonConstReferenceType()))))
                         .bind(NodeID<Expr>::value)),
             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) {
@@ -360,7 +369,9 @@ const Stmt *ExprMutationAnalyzer::findFu
   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(NodeID<Expr>::value)),

Modified: cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp?rev=342409&r1=342408&r2=342409&view=diff
==============================================================================
--- cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp (original)
+++ cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp Mon Sep 17 13:10:56 2018
@@ -66,6 +66,25 @@ std::string removeSpace(std::string s) {
   return s;
 }
 
+const std::string StdRemoveReference =
+    "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; }; }";
+
+const std::string StdMove =
+    "namespace std {"
+    "template<class T> typename remove_reference<T>::type&& "
+    "move(T&& t) noexcept {"
+    "return static_cast<typename remove_reference<T>::type&&>(t); } }";
+
+const std::string StdForward =
+    "namespace std {"
+    "template<class T> T&& "
+    "forward(typename remove_reference<T>::type& t) noexcept { return t; }"
+    "template<class T> T&& "
+    "forward(typename remove_reference<T>::type&&) noexcept { return t; } }";
+
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,87 @@ TEST(ExprMutationAnalyzerTest, ByConstRR
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto 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> typename std::remove_reference<T>::type&& "
-      "move(T&& t) noexcept; }"
-      "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  auto AST =
+      tooling::buildASTFromCode(StdRemoveReference + StdMove +
+                                "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(
+      StdRemoveReference + StdMove +
+      "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"));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+                                  "void f() { int x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdMove +
+      "struct S { S(); S(const S&); S& operator=(const S&); };"
+      "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+      tooling::buildASTFromCode(StdRemoveReference + StdMove +
+                                "struct S { S(); S(S&&); S& operator=(S&&); };"
+                                "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST =
+      tooling::buildASTFromCode(StdRemoveReference + StdMove +
+                                "struct S { S(); S(const S&); S(S&&);"
+                                "S& operator=(const S&); S& operator=(S&&); };"
+                                "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdMove +
+      "struct S { S(); S(const S&); S(S&&);"
+      "S& operator=(const S&); S& operator=(S&&); };"
+      "void f() { const S x; S y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+                                  "struct S { S(); S(S); S& operator=(S); };"
+                                  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdMove +
+      "struct S{}; void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdMove +
+      "struct S{}; void f() { const S x; S y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, Forward) {
-  // Technically almost the same as ByNonConstRefArgument, just double checking
-  const auto 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&) noexcept;"
-      "template<class T> T&& "
-      "forward(typename std::remove_reference<T>::type&&) noexcept;"
+  auto AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdForward +
       "void f() { struct A {}; A x; std::forward<A &>(x); }");
-  const auto Results =
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdForward +
+      "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 +709,17 @@ TEST(ExprMutationAnalyzerTest, FollowFun
       "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(
+      StdRemoveReference + StdForward +
+      "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 +764,17 @@ TEST(ExprMutationAnalyzerTest, FollowFun
       "void f() { int x; S s(x); }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+      StdRemoveReference + StdForward +
+      "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) {




More information about the cfe-commits mailing list