[clang] [mutation analyzer] support mutation analysis for pointee (PR #118593)
Congcong Cai via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 4 01:07:16 PST 2024
https://github.com/HerrCai0907 updated https://github.com/llvm/llvm-project/pull/118593
>From 01657045893c3fca226f0682523f999cabffc1ca Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Wed, 4 Dec 2024 15:38:06 +0800
Subject: [PATCH 1/2] [analysis] support mutation analysis for pointee wip
---
.../Analysis/Analyses/ExprMutationAnalyzer.h | 4 +
clang/lib/Analysis/ExprMutationAnalyzer.cpp | 142 +++++++-
.../Analysis/ExprMutationAnalyzerTest.cpp | 333 ++++++++++++++++++
3 files changed, 476 insertions(+), 3 deletions(-)
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..a1b26d0e063d55 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -8,9 +8,12 @@
#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"
+#include "llvm/Support/Casting.h"
namespace clang {
using namespace ast_matchers;
@@ -22,7 +25,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 +94,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 +116,53 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
return canExprResolveTo(Exp, Target);
}
+class ExprPointeeResolve {
+ const Expr *T;
+
+ bool resolveExpr(const Expr *E) {
+ 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 +270,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 +446,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 +713,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..a6575cde10223a 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,336 @@ 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() {
+ int* x;
+ [x] () { *x = 1; };
+ })";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isDeclPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByLambdaCaptureInit) {
+ const std::string Code = R"(
+ void f() {
+ int* x;
+ [t = x] () { *t = 1; };
+ })";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isDeclPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerArithmeticAdd) {
+ const std::string Code = R"(
+ void f() {
+ int* x;
+ int* y = x + 1;
+ })";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerArithmeticSubElement) {
+ const std::string Code = R"(
+ void f() {
+ int* x;
+ int* y = &x[1];
+ })";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
} // namespace clang
>From bea4abd2312d0c336fea82fcc29b9bc2790cc5f0 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Wed, 4 Dec 2024 17:07:04 +0800
Subject: [PATCH 2/2] fix
---
clang/lib/Analysis/ExprMutationAnalyzer.cpp | 5 ++++-
.../Analysis/ExprMutationAnalyzerTest.cpp | 17 +++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index a1b26d0e063d55..490bb8cfb71b46 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -13,7 +13,6 @@
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/Casting.h"
namespace clang {
using namespace ast_matchers;
@@ -116,10 +115,14 @@ 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;
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index a6575cde10223a..75930f16984724 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -1779,7 +1779,7 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInConstruct) {
TEST(ExprMutationAnalyzerTest,
PointeeMutatedByPassAsArgumentInTemplateConstruct) {
const std::string Code = "template<class T> void f() { int *x; new T(x); }";
- auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto AST = buildASTFromCodeWithArgs(Code, {"-fno-delayed-template-parsing"});
auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
@@ -1793,7 +1793,8 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInInitList) {
"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 AST =
+ buildASTFromCodeWithArgs(Code, {"-fno-delayed-template-parsing"});
auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
@@ -1942,4 +1943,16 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerArithmeticSubElement) {
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByConditionOperator) {
+ const std::string Code = R"(
+ void f() {
+ int* x;
+ int* y = 1 ? nullptr : x;
+ })";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
} // namespace clang
More information about the cfe-commits
mailing list