shuaiwang updated this revision to Diff 166710.
shuaiwang added a comment.

Added test case place holder for cases that should be supported in later 
patches.


Repository:
  rC Clang

https://reviews.llvm.org/D52219

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===================================================================
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -52,11 +52,21 @@
 bool isMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) {
   const auto *const S = selectFirst<Stmt>("stmt", Results);
   const auto *const E = selectFirst<Expr>("expr", Results);
+  assert(S && E);
   return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
 }
 
+bool isPointeeMutated(const SmallVectorImpl<BoundNodes> &Results,
+                      ASTUnit *AST) {
+  const auto *const S = selectFirst<Stmt>("stmt", Results);
+  const auto *const E = selectFirst<Expr>("expr", Results);
+  assert(S && E);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
 SmallVector<std::string, 1>
 mutatedBy(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) {
+  EXPECT_TRUE(isMutated(Results, AST));
   const auto *const S = selectFirst<Stmt>("stmt", Results);
   SmallVector<std::string, 1> Chain;
   ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
@@ -71,6 +81,19 @@
   return Chain;
 }
 
+std::string pointeeMutatedBy(const SmallVectorImpl<BoundNodes> &Results,
+                             ASTUnit *AST) {
+  EXPECT_TRUE(isPointeeMutated(Results, AST));
+  const auto *const S = selectFirst<Stmt>("stmt", Results);
+  const auto *const E = selectFirst<Expr>("expr", Results);
+  ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
+  const Stmt *By = Analyzer.findPointeeMutation(E);
+  std::string buffer;
+  llvm::raw_string_ostream stream(buffer);
+  By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+  return StringRef(stream.str()).trim().str();
+}
+
 std::string removeSpace(std::string s) {
   s.erase(std::remove_if(s.begin(), s.end(),
                          [](char c) { return std::isspace(c); }),
@@ -100,10 +123,14 @@
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
-  const auto AST = buildASTFromCode("void f() { int x; x; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; x; }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode("void f() { const int x = 0; x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 class AssignmentTest : public ::testing::TestWithParam<std::string> {};
@@ -134,41 +161,111 @@
                         Values("++x", "--x", "x++", "x--"), );
 
 TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
-  const auto AST = buildASTFromCode(
+  auto AST = buildASTFromCode(
       "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
-  const auto Results =
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+
+  AST = buildASTFromCode(
+      "void f() { struct Foo { void mf(); }; Foo *p; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "p->mf()");
+
+  AST = buildASTFromCode(
+      "void f() { struct Foo { void mf(); }; Foo *x; Foo *&p = x; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "p->mf()");
 }
 
 TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
   auto AST = buildASTFromCodeWithArgs(
       "struct X { template <class T> void mf(); };"
-      "template <class T> void f() { X x; x.mf<T>(); }",
+      "template <class T> void f() { X x; x.mf<T>(); }"
+      "template <class T> void g() { X *p; p->mf<T>(); }",
       {"-fno-delayed-template-parsing"});
   auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf<T>()"));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "p->mf<T>()");
 
-  AST = buildASTFromCodeWithArgs("template <class T> void f() { T x; x.mf(); }",
-                                 {"-fno-delayed-template-parsing"});
+  AST =
+      buildASTFromCodeWithArgs("template <class T> void f() { T x; x.mf(); }"
+                               "template <class T> void g() { T *p; p->mf(); }",
+                               {"-fno-delayed-template-parsing"});
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "p->mf()");
 
   AST = buildASTFromCodeWithArgs(
       "template <class T> struct X;"
-      "template <class T> void f() { X<T> x; x.mf(); }",
+      "template <class T> void f() { X<T> x; x.mf(); }"
+      "template <class T> void g() { X<T> *p; p->mf(); }",
       {"-fno-delayed-template-parsing"});
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "p->mf()");
+
+  AST = buildASTFromCodeWithArgs(
+      "struct X { template <class T> void mf() const; };"
+      "template <class T> void f() { const X x; x.mf<T>(); }"
+      "template <class T> void g() { const X *p; p->mf<T>(); }",
+      {"-fno-delayed-template-parsing"});
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+
+  AST = buildASTFromCodeWithArgs(
+      "template <class T> void f() { const T x; x.mf(); }"
+      "template <class T> void g() { const T *p; p->mf(); }",
+      {"-fno-delayed-template-parsing"});
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+
+  AST = buildASTFromCodeWithArgs(
+      "template <class T> struct X;"
+      "template <class T> void f() { const X<T> x; x.mf(); }"
+      "template <class T> void g() { const X<T> *p; p->mf(); }",
+      {"-fno-delayed-template-parsing"});
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
-  const auto AST = buildASTFromCode(
+  auto AST = buildASTFromCode(
       "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
-  const auto Results =
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode(
+      "void f() { struct Foo { void mf() const; }; const Foo x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode(
+      "void f() { struct Foo { void mf() const; }; Foo *p; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+
+  AST = buildASTFromCode(
+      "void f() { struct Foo { void mf() const; }; const Foo *p; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, NonConstOperator) {
@@ -193,9 +290,10 @@
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
-  AST = buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  AST = buildASTFromCode("void g(int*); void f() { int* p; g(p); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "g(p)");
 
   AST = buildASTFromCode("typedef int* IntPtr;"
                          "void g(IntPtr); void f() { int* x; g(x); }");
@@ -538,11 +636,13 @@
   AST = buildASTFromCode("int* f() { int* x; return x; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "return x;");
 
   AST = buildASTFromCode("typedef int* IntPtr;"
                          "IntPtr f() { int* x; return x; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_EQ(pointeeMutatedBy(Results, AST.get()), "return x;");
 }
 
 TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) {
@@ -577,18 +677,31 @@
 }
 
 TEST(ExprMutationAnalyzerTest, TakeAddress) {
-  const auto AST = buildASTFromCode("void g(int*); void f() { int x; g(&x); }");
-  const auto Results =
+  auto AST = buildASTFromCode("void g(int*); void f() { int x; g(&x); }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("&x"));
+
+  AST = buildASTFromCode(
+      "void g(const int*); void f() { const int x = 10; g(&x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) {
-  const auto AST =
-      buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }");
-  const auto Results =
+  auto AST = buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = buildASTFromCode("void f() { int x[2]; *x = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = buildASTFromCode(
+      "void g(const int*); void f() { const int x[2] = {}; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
@@ -880,33 +993,53 @@
 }
 
 TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) {
-  const auto AST = buildASTFromCode("void f() { int x; [=]() { x; }; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; [=]() { x; }; }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode("void f() { int *p; [=]() { *p = 10; }; }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_THAT(pointeeMutatedBy(Results, AST.get()),
+              ResultOf(removeSpace, "[=](){*p=10;}"));
 }
 
 TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByValue) {
-  const auto AST = buildASTFromCode("void f() { int x; [x]() { x; }; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; [x]() { x; }; }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode("void f() { int *p; [p]() { *p = 10; }; }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_THAT(pointeeMutatedBy(Results, AST.get()),
+              ResultOf(removeSpace, "[p](){*p=10;}"));
 }
 
 TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByRef) {
-  const auto AST = buildASTFromCode("void f() { int x; [&]() { x = 10; }; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; [&]() { x = 10; }; }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
               ElementsAre(ResultOf(removeSpace, "[&](){x=10;}")));
+
+  AST = buildASTFromCode("void f() { const int x = 10; [&]() { x; }; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) {
-  const auto AST = buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }");
+  auto Results =
       match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
               ElementsAre(ResultOf(removeSpace, "[&x](){x=10;}")));
+
+  AST = buildASTFromCode("void f() { const int x = 10; [&x]() { x; }; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) {
@@ -1106,4 +1239,12 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, Dereference) {
+  // TODO: add follow cases:
+  // - multiple levels of pointers `int ***`, `int * const *`
+  //   (ensure that having a const pointer does no influence the pointee
+  //   analysis `int * const p = &i; *p = 42;`)
+  // - a class with `operator*` + `operator->`
+}
+
 } // namespace clang
Index: lib/Analysis/ExprMutationAnalyzer.cpp
===================================================================
--- lib/Analysis/ExprMutationAnalyzer.cpp
+++ lib/Analysis/ExprMutationAnalyzer.cpp
@@ -81,6 +81,34 @@
   return nullptr;
 }
 
+// Pointee of `Exp` can be *directly* mutated if the type of `Exp` is:
+// - Pointer to non-const
+// - Pointer-like type with `operator*` returning non-const reference
+bool isPointeeMutable(const Expr *Exp, const ASTContext &Context) {
+  if (const auto *PT = Exp->getType()->getAs<PointerType>()) {
+    return !PT->getPointeeType().isConstant(Context);
+  }
+  if (const auto *RT = Exp->getType()->getAs<RecordType>()) {
+    if (const CXXRecordDecl *RecordDecl = RT->getAsCXXRecordDecl()) {
+      bool ExpIsConst = Exp->getType().isConstant(Context);
+      return llvm::any_of(
+          RecordDecl->methods(),
+          [ExpIsConst, &Context](const CXXMethodDecl *MethodDecl) {
+            // Look for `operator*` with the same constness as `Exp`.
+            if (MethodDecl->getOverloadedOperator() != OO_Star ||
+                MethodDecl->isConst() != ExpIsConst)
+              return false;
+            // Check whether returning non-const reference.
+            if (const auto *RT =
+                    MethodDecl->getReturnType()->getAs<ReferenceType>())
+              return !RT->getPointeeType().isConstant(Context);
+            return false;
+          });
+    }
+  }
+  return false;
+}
+
 } // namespace
 
 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
@@ -100,7 +128,10 @@
 }
 
 const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
-  return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
+  return findMutationMemoized(Exp,
+                              {&ExprMutationAnalyzer::findPointeeDirectMutation,
+                               &ExprMutationAnalyzer::findPointeeCastMutation},
+                              PointeeResults);
 }
 
 const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
@@ -192,6 +223,12 @@
 }
 
 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
+  // `Exp` can be *directly* mutated if the type of `Exp` is not const.
+  // Bail out early otherwise.
+  if (Exp->getType().isConstant(Context))
+    return nullptr;
+  bool ExpIsPointer = Exp->getType()->getAs<PointerType>() != nullptr;
+
   // LHS of any assignment operators.
   const auto AsAssignmentLhs =
       binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
@@ -204,14 +241,17 @@
   // Invoking non-const member function.
   // A member function is assumed to be non-const when it is unresolved.
   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
-  const auto AsNonConstThis =
-      expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
-                 cxxOperatorCallExpr(callee(NonConstMethod),
-                                     hasArgument(0, equalsNode(Exp))),
-                 callExpr(callee(expr(anyOf(
-                     unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
-                     cxxDependentScopeMemberExpr(
-                         hasObjectExpression(equalsNode(Exp)))))))));
+  const auto AsNonConstNonPointerThis =
+      ExpIsPointer
+          ? expr(unless(anything()))
+          : expr(anyOf(
+                cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
+                cxxOperatorCallExpr(callee(NonConstMethod),
+                                    hasArgument(0, equalsNode(Exp))),
+                callExpr(callee(expr(anyOf(
+                    unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
+                    cxxDependentScopeMemberExpr(
+                        hasObjectExpression(equalsNode(Exp)))))))));
 
   // Taking address of 'Exp'.
   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
@@ -224,15 +264,19 @@
                     hasUnaryOperand(equalsNode(Exp)));
   const auto AsPointerFromArrayDecay =
       castExpr(hasCastKind(CK_ArrayToPointerDecay),
+               // A NoOp implicit cast is adding const.
+               unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
                unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
   // Treat calling `operator->()` of move-only classes as taking address.
   // These are typically smart pointers with unique ownership so we treat
   // mutation of pointee as mutation of the smart pointer itself.
   const auto AsOperatorArrowThis =
-      cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
-                          callee(cxxMethodDecl(ofClass(isMoveOnly()),
-                                               returns(nonConstPointerType()))),
-                          argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
+      ExpIsPointer ? cxxOperatorCallExpr(unless(anything()))
+                   : cxxOperatorCallExpr(
+                         hasOverloadedOperatorName("->"),
+                         callee(cxxMethodDecl(ofClass(isMoveOnly()),
+                                              returns(nonConstPointerType()))),
+                         argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
 
   // Used as non-const-ref argument when calling a function.
   // An argument is assumed to be non-const-ref when the function is unresolved.
@@ -265,10 +309,11 @@
   const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
 
   const auto Matches =
-      match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
-                               AsAmpersandOperand, AsPointerFromArrayDecay,
-                               AsOperatorArrowThis, AsNonConstRefArg,
-                               AsLambdaRefCaptureInit, AsNonConstRefReturn))
+      match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand,
+                               AsNonConstNonPointerThis, AsAmpersandOperand,
+                               AsPointerFromArrayDecay, AsOperatorArrowThis,
+                               AsNonConstRefArg, AsLambdaRefCaptureInit,
+                               AsNonConstRefReturn))
                         .bind("stmt")),
             Stm, Context);
   return selectFirst<Stmt>("stmt", Matches);
@@ -412,6 +457,62 @@
   return nullptr;
 }
 
+const Stmt *ExprMutationAnalyzer::findPointeeDirectMutation(const Expr *Exp) {
+  // Bail out early if pointee can't be *directly* mutated.
+  if (!isPointeeMutable(Exp, Context))
+    return nullptr;
+  bool ExpIsPointer = Exp->getType()->getAs<PointerType>() != nullptr;
+
+  // Invoking non-const member function.
+  // A member function is assumed to be non-const when it is unresolved.
+  // We don't handle pointer-like types here, because they need to be
+  // dereferenced first before any non-const member function of the pointee can
+  // be invoked, and once dereferenced they become real pointers.
+  const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
+  const auto AsNonConstPointerThis =
+      ExpIsPointer
+          ? expr(anyOf(
+                cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
+                cxxOperatorCallExpr(callee(NonConstMethod),
+                                    hasArgument(0, equalsNode(Exp))),
+                callExpr(callee(expr(anyOf(
+                    unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
+                    cxxDependentScopeMemberExpr(
+                        hasObjectExpression(equalsNode(Exp)))))))))
+          : expr(unless(anything()));
+
+  // Used as an argument when calling a function.
+  const auto AsArg =
+      anyOf(callExpr(hasAnyArgument(equalsNode(Exp))),
+            cxxConstructExpr(hasAnyArgument(equalsNode(Exp))),
+            cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
+
+  // Captured by a lambda.
+  const auto AsLambdaCaptureInit = lambdaExpr(hasCaptureInit(Exp));
+
+  // Returned from the function.
+  const auto AsReturnValue = returnStmt(hasReturnValue(equalsNode(Exp)));
+
+  const auto Matches =
+      match(findAll(stmt(anyOf(AsNonConstPointerThis, AsArg,
+                               AsLambdaCaptureInit, AsReturnValue))
+                        .bind("stmt")),
+            Stm, Context);
+  return selectFirst<Stmt>("stmt", Matches);
+}
+
+const Stmt *ExprMutationAnalyzer::findPointeeCastMutation(const Expr *Exp) {
+  // Only handling LValueToRValue for now for easier unit testing during
+  // implementation.
+  // TODO: properly handle all casts scenarios.
+  const auto Casts =
+      match(findAll(implicitCastExpr(hasSourceExpression(equalsNode(Exp)),
+                                     hasCastKind(CK_LValueToRValue))
+                        .bind(NodeID<Expr>::value)),
+            Stm, Context);
+  return findExprPointeeMutation(Casts);
+}
+
 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
     const FunctionDecl &Func, ASTContext &Context)
     : BodyAnalyzer(*Func.getBody(), Context) {
Index: include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
===================================================================
--- include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
+++ include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
@@ -66,6 +66,9 @@
   const Stmt *findReferenceMutation(const Expr *Exp);
   const Stmt *findFunctionArgMutation(const Expr *Exp);
 
+  const Stmt *findPointeeDirectMutation(const Expr *Exp);
+  const Stmt *findPointeeCastMutation(const Expr *Exp);
+
   const Stmt &Stm;
   ASTContext &Context;
   llvm::DenseMap<const FunctionDecl *,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to