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