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 <congcongcai0...@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits