Author: Congcong Cai Date: 2025-01-22T15:58:48+08:00 New Revision: 53ea5ffcb38d428e446d357f310e9c28957eaec7
URL: https://github.com/llvm/llvm-project/commit/53ea5ffcb38d428e446d357f310e9c28957eaec7 DIFF: https://github.com/llvm/llvm-project/commit/53ea5ffcb38d428e446d357f310e9c28957eaec7.diff LOG: [mutation analyzer] support mutation analysis for pointee (#118593) This patch wants to add mutation analyzer for pointee object. Added: Modified: clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h clang/lib/Analysis/ExprMutationAnalyzer.cpp clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp Removed: ################################################################################ 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 cefe64409c9776..d7b44149d0fc4b 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; @@ -79,6 +80,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); } @@ -99,6 +102,59 @@ 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)) { + // only implicit cast needs to be treated as resolvable. + // explicit cast will be checked in `findPointeeToNonConst` + 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>, @@ -208,7 +264,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 * @@ -377,7 +440,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())); @@ -643,6 +707,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(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..cc277d56b37a2c 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,398 @@ 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, PointeeMutatedByMethod) { + { + const std::string Code = "struct A { int v; void foo(); };" + "void f() { A* x = nullptr; x->foo(); }"; + 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 foo() const; };" + "void f() { A* x = nullptr; x->foo(); }"; + auto AST = buildASTFromCodeWithArgs(Code, {}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isPointeeMutated(Results, AST.get())); + } +} +TEST(ExprMutationAnalyzerTest, PointeeMutatedByOperatorOverload) { + { + const std::string Code = "struct A { int v; int operator++(); };" + "void f() { A* x = nullptr; x->operator++(); }"; + 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; int operator++() const; };" + "void f() { A* x = nullptr; x->operator++(); }"; + auto AST = buildASTFromCodeWithArgs(Code, {}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(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, {"-fno-delayed-template-parsing"}); + 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, {"-fno-delayed-template-parsing"}); + 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())); + } + { + const std::string Code = R"( + void f() { + int* x; + x + 1; + })"; + auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(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())); +} + +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