[clang] [analysis] support mutation analysis for pointee wip (PR #118593)
Congcong Cai via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 3 22:02:18 PST 2024
https://github.com/HerrCai0907 created https://github.com/llvm/llvm-project/pull/118593
None
>From 04e220ec2c16c65a3c785a586653ea4bc47337f5 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Tue, 3 Dec 2024 23:31:32 +0800
Subject: [PATCH] [analysis] support mutation analysis for pointee wip
---
.../Analysis/Analyses/ExprMutationAnalyzer.h | 4 +
clang/lib/Analysis/ExprMutationAnalyzer.cpp | 96 +++++-
.../Analysis/ExprMutationAnalyzerTest.cpp | 274 ++++++++++++++++++
3 files changed, 371 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..946d47ed1764c8 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -8,13 +8,28 @@
#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 {
using namespace ast_matchers;
+namespace {
+
+AST_MATCHER(Stmt, dumpStmt) {
+ Node.dumpColor();
+ return true;
+}
+AST_MATCHER(Decl, dumpDecl) {
+ Node.dumpColor();
+ return true;
+}
+
+} // namespace
+
// Check if result of Source expression could be a Target expression.
// Checks:
// - Implicit Casts
@@ -22,7 +37,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 +106,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);
}
@@ -219,7 +235,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 +411,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 +678,72 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
return nullptr;
}
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
+ const auto Matches = match(
+ stmt(forEachDescendant(
+ unaryOperator(hasOperatorName("*"), hasUnaryOperand(ignoringImpCasts(
+ canResolveToExpr(Exp))))
+ .bind(NodeID<Expr>::value))),
+ Stm, Context);
+ return findExprMutation(Matches);
+}
+
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
+ const auto Matches = match(
+ stmt(forEachDescendant(memberExpr(hasObjectExpression(ignoringImpCasts(
+ canResolveToExpr(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()));
+ const auto CanResolveToExprWithImpCast =
+ ignoringImpCasts(canResolveToExpr(Exp));
+
+ const auto InitToNonConst =
+ varDecl(hasType(NonConstPointerOrDependentType),
+ hasInitializer(expr(CanResolveToExprWithImpCast).bind("stmt")));
+
+ const auto AssignToNonConst = binaryOperation(
+ hasOperatorName("="), hasLHS(expr(hasType(NonConstPointerOrDependentType))),
+ hasRHS(CanResolveToExprWithImpCast));
+
+ const auto ArgOfInstantiationDependent = allOf(
+ hasAnyArgument(CanResolveToExprWithImpCast), isInstantiationDependent());
+ const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
+ CanResolveToExprWithImpCast, NonConstPointerOrDependentType);
+ const auto CallLikeMatcher =
+ anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
+ const auto PassAsNonConstArg =
+ expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
+ cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
+ parenListExpr(has(CanResolveToExprWithImpCast)),
+ initListExpr(hasAnyInit(CanResolveToExprWithImpCast))));
+ const auto CallNonConstMethod =
+ cxxMemberCallExpr(on(ignoringImpCasts(expr(canResolveToExpr(Exp)))),
+ unless(isConstCallee()));
+ const auto CastToNonConst =
+ explicitCastExpr(hasSourceExpression(CanResolveToExprWithImpCast),
+ hasDestinationType(NonConstPointerOrDependentType));
+
+ // TODO: lambda, BinaryOperator/ArraySubscriptExpr
+
+ const auto Matches =
+ match(stmt(anyOf(forEachDescendant(
+ stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
+ CallNonConstMethod, CastToNonConst))
+ .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..c79e19035e0553 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -1609,4 +1609,278 @@ 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);
+ TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
+ return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
+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()));
+ }
+}
+
} // namespace clang
More information about the cfe-commits
mailing list