88#include " clang/Analysis/Analyses/ExprMutationAnalyzer.h"
99#include " clang/AST/Expr.h"
1010#include " clang/AST/OperationKinds.h"
11+ #include " clang/AST/Stmt.h"
1112#include " clang/ASTMatchers/ASTMatchFinder.h"
1213#include " clang/ASTMatchers/ASTMatchers.h"
14+ #include " clang/ASTMatchers/ASTMatchersMacros.h"
1315#include " llvm/ADT/STLExtras.h"
1416
1517namespace clang {
@@ -22,7 +24,6 @@ using namespace ast_matchers;
2224// - ConditionalOperator
2325// - BinaryConditionalOperator
2426static bool canExprResolveTo (const Expr *Source, const Expr *Target) {
25-
2627 const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
2728 if (Matcher (E))
2829 return true ;
@@ -79,6 +80,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
7980
8081namespace {
8182
83+ AST_MATCHER (Type, isDependentType) { return Node.isDependentType (); }
84+
8285AST_MATCHER_P (LambdaExpr, hasCaptureInit, const Expr *, E) {
8386 return llvm::is_contained (Node.capture_inits (), E);
8487}
@@ -99,6 +102,59 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
99102 return canExprResolveTo (Exp, Target);
100103}
101104
105+ // use class member to store data can reduce stack usage to avoid stack overflow
106+ // when recursive call.
107+ class ExprPointeeResolve {
108+ const Expr *T;
109+
110+ bool resolveExpr (const Expr *E) {
111+ if (E == nullptr )
112+ return false ;
113+ if (E == T)
114+ return true ;
115+
116+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
117+ if (BO->isAdditiveOp ())
118+ return (resolveExpr (BO->getLHS ()) || resolveExpr (BO->getRHS ()));
119+ if (BO->isCommaOp ())
120+ return resolveExpr (BO->getRHS ());
121+ return false ;
122+ }
123+
124+ if (const auto *PE = dyn_cast<ParenExpr>(E))
125+ return resolveExpr (PE->getSubExpr ());
126+
127+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
128+ // only implicit cast needs to be treated as resolvable.
129+ // explicit cast will be checked in `findPointeeToNonConst`
130+ const CastKind kind = ICE->getCastKind ();
131+ if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
132+ kind == CK_UncheckedDerivedToBase)
133+ return resolveExpr (ICE->getSubExpr ());
134+ return false ;
135+ }
136+
137+ if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
138+ return resolve (ACE->getTrueExpr ()) || resolve (ACE->getFalseExpr ());
139+
140+ return false ;
141+ }
142+
143+ public:
144+ ExprPointeeResolve (const Expr *T) : T(T) {}
145+ bool resolve (const Expr *S) { return resolveExpr (S); }
146+ };
147+
148+ AST_MATCHER_P (Stmt, canResolveToExprPointee, const Stmt *, T) {
149+ auto *Exp = dyn_cast<Expr>(&Node);
150+ if (!Exp)
151+ return true ;
152+ auto *Target = dyn_cast<Expr>(T);
153+ if (!Target)
154+ return false ;
155+ return ExprPointeeResolve{Target}.resolve (Exp);
156+ }
157+
102158// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
103159// not have the 'arguments()' method.
104160AST_MATCHER_P (InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -208,7 +264,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
208264
209265const Stmt *
210266ExprMutationAnalyzer::Analyzer::findPointeeMutation (const Expr *Exp) {
211- return findMutationMemoized (Exp, {/* TODO*/ }, Memorized.PointeeResults );
267+ return findMutationMemoized (
268+ Exp,
269+ {
270+ &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
271+ &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
272+ &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
273+ },
274+ Memorized.PointeeResults );
212275}
213276
214277const Stmt *
@@ -377,7 +440,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
377440 // references.
378441 const auto NonConstRefParam = forEachArgumentWithParamType (
379442 anyOf (canResolveToExpr (Exp),
380- memberExpr (hasObjectExpression (canResolveToExpr (Exp)))),
443+ memberExpr (
444+ hasObjectExpression (ignoringImpCasts (canResolveToExpr (Exp))))),
381445 nonConstReferenceType ());
382446 const auto NotInstantiated = unless (hasDeclaration (isInstantiated ()));
383447
@@ -643,6 +707,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
643707 return nullptr ;
644708}
645709
710+ const Stmt *
711+ ExprMutationAnalyzer::Analyzer::findPointeeValueMutation (const Expr *Exp) {
712+ const auto Matches = match (
713+ stmt (forEachDescendant (
714+ expr (anyOf (
715+ // deref by *
716+ unaryOperator (hasOperatorName (" *" ),
717+ hasUnaryOperand (canResolveToExprPointee (Exp))),
718+ // deref by []
719+ arraySubscriptExpr (hasBase (canResolveToExprPointee (Exp)))))
720+ .bind (NodeID<Expr>::value))),
721+ Stm, Context);
722+ return findExprMutation (Matches);
723+ }
724+
725+ const Stmt *
726+ ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation (const Expr *Exp) {
727+ const Stmt *MemberCallExpr = selectFirst<Stmt>(
728+ " stmt" , match (stmt (forEachDescendant (
729+ cxxMemberCallExpr (on (canResolveToExprPointee (Exp)),
730+ unless (isConstCallee ()))
731+ .bind (" stmt" ))),
732+ Stm, Context));
733+ if (MemberCallExpr)
734+ return MemberCallExpr;
735+ const auto Matches =
736+ match (stmt (forEachDescendant (
737+ memberExpr (hasObjectExpression (canResolveToExprPointee (Exp)))
738+ .bind (NodeID<Expr>::value))),
739+ Stm, Context);
740+ return findExprMutation (Matches);
741+ }
742+
743+ const Stmt *
744+ ExprMutationAnalyzer::Analyzer::findPointeeToNonConst (const Expr *Exp) {
745+ const auto NonConstPointerOrDependentType =
746+ type (anyOf (nonConstPointerType (), isDependentType ()));
747+
748+ // assign
749+ const auto InitToNonConst =
750+ varDecl (hasType (NonConstPointerOrDependentType),
751+ hasInitializer (expr (canResolveToExprPointee (Exp)).bind (" stmt" )));
752+ const auto AssignToNonConst =
753+ binaryOperation (hasOperatorName (" =" ),
754+ hasLHS (expr (hasType (NonConstPointerOrDependentType))),
755+ hasRHS (canResolveToExprPointee (Exp)));
756+ // arguments like
757+ const auto ArgOfInstantiationDependent = allOf (
758+ hasAnyArgument (canResolveToExprPointee (Exp)), isInstantiationDependent ());
759+ const auto ArgOfNonConstParameter = forEachArgumentWithParamType (
760+ canResolveToExprPointee (Exp), NonConstPointerOrDependentType);
761+ const auto CallLikeMatcher =
762+ anyOf (ArgOfNonConstParameter, ArgOfInstantiationDependent);
763+ const auto PassAsNonConstArg =
764+ expr (anyOf (cxxUnresolvedConstructExpr (ArgOfInstantiationDependent),
765+ cxxConstructExpr (CallLikeMatcher), callExpr (CallLikeMatcher),
766+ parenListExpr (has (canResolveToExprPointee (Exp))),
767+ initListExpr (hasAnyInit (canResolveToExprPointee (Exp)))));
768+ // cast
769+ const auto CastToNonConst =
770+ explicitCastExpr (hasSourceExpression (canResolveToExprPointee (Exp)),
771+ hasDestinationType (NonConstPointerOrDependentType));
772+
773+ // capture
774+ // FIXME: false positive if the pointee does not change in lambda
775+ const auto CaptureNoConst = lambdaExpr (hasCaptureInit (Exp));
776+
777+ const auto Matches =
778+ match (stmt (anyOf (forEachDescendant (
779+ stmt (anyOf (AssignToNonConst, PassAsNonConstArg,
780+ CastToNonConst, CaptureNoConst))
781+ .bind (" stmt" )),
782+ forEachDescendant (InitToNonConst))),
783+ Stm, Context);
784+ return selectFirst<Stmt>(" stmt" , Matches);
785+ }
786+
646787FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer (
647788 const FunctionDecl &Func, ASTContext &Context,
648789 ExprMutationAnalyzer::Memoized &Memorized)
0 commit comments