Skip to content

[mutation analyzer] support mutation analysis for pointee #118593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 22, 2025

Conversation

HerrCai0907
Copy link
Contributor

@HerrCai0907 HerrCai0907 commented Dec 4, 2024

This patch wants to add mutation analyzer for pointee object.

Copy link

github-actions bot commented Dec 4, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@HerrCai0907 HerrCai0907 force-pushed the support-check-mutation-pointee branch from 04e220e to 019ee3d Compare December 4, 2024 07:30
@HerrCai0907 HerrCai0907 force-pushed the support-check-mutation-pointee branch from 019ee3d to 0165704 Compare December 4, 2024 07:38
@HerrCai0907 HerrCai0907 changed the title [analysis] support mutation analysis for pointee wip [mutation analyzer] support mutation analysis for pointee wip Dec 4, 2024
@HerrCai0907 HerrCai0907 force-pushed the support-check-mutation-pointee branch from 9c38149 to 58b6e46 Compare December 4, 2024 07:46
@HerrCai0907 HerrCai0907 marked this pull request as ready for review December 4, 2024 07:46
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:analysis labels Dec 4, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 4, 2024

@llvm/pr-subscribers-clang-analysis

@llvm/pr-subscribers-clang

Author: Congcong Cai (HerrCai0907)

Changes

Patch is 21.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/118593.diff

3 Files Affected:

  • (modified) clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h (+4)
  • (modified) clang/lib/Analysis/ExprMutationAnalyzer.cpp (+142-3)
  • (modified) clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp (+345)
diff --git a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
index 7442f4aad531b7..3344959072c221 100644
--- a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
+++ b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
@@ -71,6 +71,10 @@ class ExprMutationAnalyzer {
     const Stmt *findReferenceMutation(const Expr *Exp);
     const Stmt *findFunctionArgMutation(const Expr *Exp);
 
+    const Stmt *findPointeeValueMutation(const Expr *Exp);
+    const Stmt *findPointeeMemberMutation(const Expr *Exp);
+    const Stmt *findPointeeToNonConst(const Expr *Exp);
+
     const Stmt &Stm;
     ASTContext &Context;
     Memoized &Memorized;
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index be0e8aa5743dd9..490bb8cfb71b46 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -8,8 +8,10 @@
 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
 #include "llvm/ADT/STLExtras.h"
 
 namespace clang {
@@ -22,7 +24,6 @@ using namespace ast_matchers;
 //  - ConditionalOperator
 //  - BinaryConditionalOperator
 static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
-
   const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
     if (Matcher(E))
       return true;
@@ -92,6 +93,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
 
 namespace {
 
+AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
+
 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
   return llvm::is_contained(Node.capture_inits(), E);
 }
@@ -112,6 +115,57 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
   return canExprResolveTo(Exp, Target);
 }
 
+// use class member to store data can reduce stack usage to avoid stack overflow
+// when recursive call.
+class ExprPointeeResolve {
+  const Expr *T;
+
+  bool resolveExpr(const Expr *E) {
+    if (E == nullptr)
+      return false;
+    if (E == T)
+      return true;
+
+    if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+      if (BO->isAdditiveOp())
+        return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
+      if (BO->isCommaOp())
+        return resolveExpr(BO->getRHS());
+      return false;
+    }
+
+    if (const auto *PE = dyn_cast<ParenExpr>(E))
+      return resolveExpr(PE->getSubExpr());
+
+    if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+      const CastKind kind = ICE->getCastKind();
+      if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
+          kind == CK_UncheckedDerivedToBase)
+        return resolveExpr(ICE->getSubExpr());
+      return false;
+    }
+
+    if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
+      return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
+
+    return false;
+  }
+
+public:
+  ExprPointeeResolve(const Expr *T) : T(T) {}
+  bool resolve(const Expr *S) { return resolveExpr(S); }
+};
+
+AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
+  auto *Exp = dyn_cast<Expr>(&Node);
+  if (!Exp)
+    return true;
+  auto *Target = dyn_cast<Expr>(T);
+  if (!Target)
+    return false;
+  return ExprPointeeResolve{Target}.resolve(Exp);
+}
+
 // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
 // not have the 'arguments()' method.
 AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -219,7 +273,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
 
 const Stmt *
 ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
-  return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults);
+  return findMutationMemoized(
+      Exp,
+      {
+          &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
+          &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
+          &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
+      },
+      Memorized.PointeeResults);
 }
 
 const Stmt *
@@ -388,7 +449,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
   // references.
   const auto NonConstRefParam = forEachArgumentWithParamType(
       anyOf(canResolveToExpr(Exp),
-            memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
+            memberExpr(
+                hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
       nonConstReferenceType());
   const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
 
@@ -654,6 +716,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
   return nullptr;
 }
 
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
+  const auto Matches = match(
+      stmt(forEachDescendant(
+          expr(anyOf(
+                   // deref by *
+                   unaryOperator(hasOperatorName("*"),
+                                 hasUnaryOperand(canResolveToExprPointee(Exp))),
+                   // deref by []
+                   arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp)))))
+              .bind(NodeID<Expr>::value))),
+      Stm, Context);
+  return findExprMutation(Matches);
+}
+
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
+  const Stmt *MemberCallExpr = selectFirst<Stmt>(
+      "stmt", match(stmt(forEachDescendant(
+                        cxxMemberCallExpr(on(canResolveToExprPointee(Exp)),
+                                          unless(isConstCallee()))
+                            .bind("stmt"))),
+                    Stm, Context));
+  if (MemberCallExpr)
+    return MemberCallExpr;
+  const auto Matches =
+      match(stmt(forEachDescendant(
+                memberExpr(hasObjectExpression(canResolveToExprPointee(Exp)))
+                    .bind(NodeID<Expr>::value))),
+            Stm, Context);
+  return findExprMutation(Matches);
+}
+
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
+  const auto NonConstPointerOrDependentType =
+      type(anyOf(nonConstPointerType(), isDependentType()));
+
+  // assign
+  const auto InitToNonConst =
+      varDecl(hasType(NonConstPointerOrDependentType),
+              hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
+  const auto AssignToNonConst =
+      binaryOperation(hasOperatorName("="),
+                      hasLHS(expr(hasType(NonConstPointerOrDependentType))),
+                      hasRHS(canResolveToExprPointee(Exp)));
+  // arguments like
+  const auto ArgOfInstantiationDependent = allOf(
+      hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
+  const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
+      canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
+  const auto CallLikeMatcher =
+      anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
+  const auto PassAsNonConstArg =
+      expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
+                 cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
+                 parenListExpr(has(canResolveToExprPointee(Exp))),
+                 initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
+  // cast
+  const auto CastToNonConst =
+      explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
+                       hasDestinationType(NonConstPointerOrDependentType));
+
+  // capture
+  // FIXME: false positive if the pointee does not change in lambda
+  const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp));
+
+  const auto Matches =
+      match(stmt(anyOf(forEachDescendant(
+                           stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
+                                      CastToNonConst, CaptureNoConst))
+                               .bind("stmt")),
+                       forEachDescendant(decl(InitToNonConst)))),
+            Stm, Context);
+  return selectFirst<Stmt>("stmt", Matches);
+}
+
 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
     const FunctionDecl &Func, ASTContext &Context,
     ExprMutationAnalyzer::Memoized &Memorized)
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 137baab53301ae..c632cae266c204 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -14,6 +14,7 @@
 #include "clang/Tooling/Tooling.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <cassert>
 #include <cctype>
 
 namespace clang {
@@ -1609,4 +1610,348 @@ TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) {
       match(withEnclosingCompound(declRefTo("x")), AST11->getASTContext());
   EXPECT_FALSE(isMutated(Results11, AST11.get()));
 }
+
+static 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);
+  TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
+static bool isDeclPointeeMutated(const SmallVectorImpl<BoundNodes> &Results,
+                                 ASTUnit *AST) {
+  const auto *const S = selectFirst<Stmt>("stmt", Results);
+  const auto *const D = selectFirst<Decl>("decl", Results);
+  assert(S && D);
+  TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(D);
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssign) {
+  {
+    const std::string Code = "void f() { int* x = nullptr; int b = *x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "void f() { int* x = nullptr; *x = 100; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "void f() { int* x = nullptr; (*x)++; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByMember) {
+  {
+    const std::string Code =
+        "struct A { int v; }; void f() { A* x = nullptr; int b = x->v; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "struct A { int v; }; void f() { A* x = nullptr; x->v = 1; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "struct A { int v; }; void f() { A* x = nullptr; x->v++; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByInitToNonConst) {
+  {
+    const std::string Code = "void f() { int* x = nullptr; int const* b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "void f() { int* x = nullptr; int* b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "void f() { int* x = nullptr; int* const b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToNonConst) {
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; int const* b; b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "void f() { int* x = nullptr; int* b; b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgument) {
+  {
+    const std::string Code =
+        "void b(int const*); void f() { int* x = nullptr; b(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void b(int *); void f() { int* x = nullptr; b(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInConstruct) {
+  {
+    const std::string Code = "struct A { A(int const*); };"
+                             "void f() { int *x; A a{x}; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "struct A { A(int const*); };"
+                             "void f() { int *x; A a(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "struct A { A(int const*); };"
+                             "void f() { int *x; A a = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "struct A { A(int *); };"
+                             "void f() { int *x; A a{x}; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest,
+     PointeeMutatedByPassAsArgumentInTemplateConstruct) {
+  const std::string Code = "template<class T> void f() { int *x; new T(x); }";
+  auto AST = buildASTFromCodeWithArgs(Code, {});
+  auto Results =
+      match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInInitList) {
+  {
+    const std::string Code =
+        "namespace std {"
+        "template<class T>"
+        "struct initializer_list{ T const* begin; T const* end; };"
+        "}"
+        "void f() { int *x; std::initializer_list<int*> a{x, x, x}; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByThis) {
+  {
+    const std::string Code =
+        "struct A { void m() const; }; void f() { A* x = nullptr; x->m(); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "struct A { void m(); }; void f() { A* x = nullptr; x->m(); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByExplicitCastToNonConst) {
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; static_cast<int const*>(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; static_cast<int*>(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByConstCastToNonConst) {
+  // const_cast to non-const even treat as mutated.
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; const_cast<int const*>(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; const_cast<int*>(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByUnresolvedCall) {
+  const std::string Code =
+      "template <class T> struct S;"
+      "template <class T> void f() { S<T> s; int* x = nullptr; s.m(x); }";
+  auto AST = buildASTFromCodeWithArgs(
+      Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+  auto Results =
+      match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToUnknownType) {
+  {
+    const std::string Code = "template <class T> void f() {"
+                             "  int* x = nullptr;"
+                             "  T t = x;"
+                             "}";
+    auto AST = buildASTFromCodeWithArgs(
+        Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = "template <class T> void f() {"
+                             "  int* x = nullptr;"
+                             "  typename T::t t = x;"
+                             "}";
+    auto AST = buildASTFromCodeWithArgs(
+        Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByLambdaCapture) {
+  const std::string Code = R"(
+      void f() {
+        i...
[truncated]

@nikic nikic removed their request for review December 4, 2024 08:36
@HerrCai0907 HerrCai0907 changed the title [mutation analyzer] support mutation analysis for pointee wip [mutation analyzer] support mutation analysis for pointee Dec 4, 2024
@HerrCai0907 HerrCai0907 force-pushed the support-check-mutation-pointee branch from 58b6e46 to bea4abd Compare December 4, 2024 09:07
Copy link
Contributor Author

ping

@5chmidti
Copy link
Contributor

I'll try to find some time for this one, sorry. I'll check it out in the next days.

Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good in general, could you add a small description to the pr?

@HerrCai0907 HerrCai0907 requested a review from 5chmidti January 21, 2025 14:29
@HerrCai0907 HerrCai0907 merged commit 53ea5ff into llvm:main Jan 22, 2025
8 checks passed
@HerrCai0907 HerrCai0907 deleted the support-check-mutation-pointee branch January 22, 2025 07:58
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 22, 2025

LLVM Buildbot has detected a new failure on builder flang-aarch64-dylib running on linaro-flang-aarch64-dylib while building clang at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/50/builds/9209

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure)
...
365.296 [2044/1/4773] Building CXX object tools/mlir/lib/Dialect/MemRef/Utils/CMakeFiles/obj.MLIRMemRefUtils.dir/MemRefUtils.cpp.o
365.465 [2043/1/4774] Building CXX object tools/mlir/lib/Dialect/GPU/CMakeFiles/obj.MLIRGPUTransforms.dir/Transforms/BufferDeallocationOpInterfaceImpl.cpp.o
365.577 [2042/1/4775] Building CXX object tools/mlir/lib/Dialect/GPU/CMakeFiles/obj.MLIRGPUTransforms.dir/Transforms/DecomposeMemRefs.cpp.o
365.688 [2041/1/4776] Building CXX object tools/mlir/lib/Dialect/GPU/TransformOps/CMakeFiles/obj.MLIRGPUTransformOps.dir/Utils.cpp.o
365.792 [2040/1/4777] Building CXX object tools/mlir/lib/Dialect/Index/IR/CMakeFiles/obj.MLIRIndexDialect.dir/InferIntRangeInterfaceImpls.cpp.o
365.903 [2039/1/4778] Building CXX object tools/mlir/lib/Conversion/ArithToAMDGPU/CMakeFiles/obj.MLIRArithToAMDGPU.dir/ArithToAMDGPU.cpp.o
366.024 [2038/1/4779] Building CXX object tools/mlir/lib/Conversion/FuncToSPIRV/CMakeFiles/obj.MLIRFuncToSPIRV.dir/FuncToSPIRVPass.cpp.o
366.202 [2037/1/4780] Building CXX object tools/mlir/lib/CAPI/Conversion/CMakeFiles/obj.MLIRCAPIConversion.dir/Passes.cpp.o
366.289 [2036/1/4781] Building CXX object tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestPrintNesting.cpp.o
383.131 [2035/1/4782] Building CXX object tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestSideEffects.cpp.o
FAILED: tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestSideEffects.cpp.o 
/usr/local/bin/c++ -DGTEST_HAS_RTTI=0 -DMLIR_INCLUDE_TESTS -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/build/tools/mlir/test/lib/IR -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/test/lib/IR -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/build/tools/mlir/include -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/include -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/build/include -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/llvm/include -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/test/lib/IR/../Dialect/Test -I/home/tcwg-buildbot/worker/flang-aarch64-dylib/build/tools/mlir/test/lib/IR/../Dialect/Test -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wundef -Werror=mismatched-tags -O3 -DNDEBUG -std=c++17  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -MD -MT tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestSideEffects.cpp.o -MF tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestSideEffects.cpp.o.d -o tools/mlir/test/lib/IR/CMakeFiles/MLIRTestIR.dir/TestSideEffects.cpp.o -c /home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/test/lib/IR/TestSideEffects.cpp
In file included from /home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/test/lib/IR/TestSideEffects.cpp:9:
/home/tcwg-buildbot/worker/flang-aarch64-dylib/llvm-project/mlir/test/lib/IR/../Dialect/Test/TestOps.h:148:10: fatal error: 'TestOps.h.inc' file not found
  148 | #include "TestOps.h.inc"
      |          ^~~~~~~~~~~~~~~
1 error generated.
ninja: build stopped: subcommand failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:analysis clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants