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"
16+ #include " llvm/Support/Casting.h"
1417
1518namespace clang {
1619using namespace ast_matchers ;
@@ -22,7 +25,6 @@ using namespace ast_matchers;
2225// - ConditionalOperator
2326// - BinaryConditionalOperator
2427static bool canExprResolveTo (const Expr *Source, const Expr *Target) {
25-
2628 const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
2729 if (Matcher (E))
2830 return true ;
@@ -92,6 +94,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
9294
9395namespace {
9496
97+ AST_MATCHER (Type, isDependentType) { return Node.isDependentType (); }
98+
9599AST_MATCHER_P (LambdaExpr, hasCaptureInit, const Expr *, E) {
96100 return llvm::is_contained (Node.capture_inits (), E);
97101}
@@ -112,6 +116,53 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
112116 return canExprResolveTo (Exp, Target);
113117}
114118
119+ class ExprPointeeResolve {
120+ const Expr *T;
121+
122+ bool resolveExpr (const Expr *E) {
123+ if (E == T)
124+ return true ;
125+
126+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
127+ if (BO->isAdditiveOp ())
128+ return (resolveExpr (BO->getLHS ()) || resolveExpr (BO->getRHS ()));
129+ if (BO->isCommaOp ())
130+ return resolveExpr (BO->getRHS ());
131+ return false ;
132+ }
133+
134+ if (const auto *PE = dyn_cast<ParenExpr>(E))
135+ return resolveExpr (PE->getSubExpr ());
136+
137+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
138+ const CastKind kind = ICE->getCastKind ();
139+ if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
140+ kind == CK_UncheckedDerivedToBase)
141+ return resolveExpr (ICE->getSubExpr ());
142+ return false ;
143+ }
144+
145+ if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
146+ return resolve (ACE->getTrueExpr ()) || resolve (ACE->getFalseExpr ());
147+
148+ return false ;
149+ }
150+
151+ public:
152+ ExprPointeeResolve (const Expr *T) : T(T) {}
153+ bool resolve (const Expr *S) { return resolveExpr (S); }
154+ };
155+
156+ AST_MATCHER_P (Stmt, canResolveToExprPointee, const Stmt *, T) {
157+ auto *Exp = dyn_cast<Expr>(&Node);
158+ if (!Exp)
159+ return true ;
160+ auto *Target = dyn_cast<Expr>(T);
161+ if (!Target)
162+ return false ;
163+ return ExprPointeeResolve{Target}.resolve (Exp);
164+ }
165+
115166// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
116167// not have the 'arguments()' method.
117168AST_MATCHER_P (InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -219,7 +270,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
219270
220271const Stmt *
221272ExprMutationAnalyzer::Analyzer::findPointeeMutation (const Expr *Exp) {
222- return findMutationMemoized (Exp, {/* TODO*/ }, Memorized.PointeeResults );
273+ return findMutationMemoized (
274+ Exp,
275+ {
276+ &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
277+ &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
278+ &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
279+ },
280+ Memorized.PointeeResults );
223281}
224282
225283const Stmt *
@@ -388,7 +446,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
388446 // references.
389447 const auto NonConstRefParam = forEachArgumentWithParamType (
390448 anyOf (canResolveToExpr (Exp),
391- memberExpr (hasObjectExpression (canResolveToExpr (Exp)))),
449+ memberExpr (
450+ hasObjectExpression (ignoringImpCasts (canResolveToExpr (Exp))))),
392451 nonConstReferenceType ());
393452 const auto NotInstantiated = unless (hasDeclaration (isInstantiated ()));
394453
@@ -654,6 +713,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
654713 return nullptr ;
655714}
656715
716+ const Stmt *
717+ ExprMutationAnalyzer::Analyzer::findPointeeValueMutation (const Expr *Exp) {
718+ const auto Matches = match (
719+ stmt (forEachDescendant (
720+ expr (anyOf (
721+ // deref by *
722+ unaryOperator (hasOperatorName (" *" ),
723+ hasUnaryOperand (canResolveToExprPointee (Exp))),
724+ // deref by []
725+ arraySubscriptExpr (hasBase (canResolveToExprPointee (Exp)))))
726+ .bind (NodeID<Expr>::value))),
727+ Stm, Context);
728+ return findExprMutation (Matches);
729+ }
730+
731+ const Stmt *
732+ ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation (const Expr *Exp) {
733+ const Stmt *MemberCallExpr = selectFirst<Stmt>(
734+ " stmt" , match (stmt (forEachDescendant (
735+ cxxMemberCallExpr (on (canResolveToExprPointee (Exp)),
736+ unless (isConstCallee ()))
737+ .bind (" stmt" ))),
738+ Stm, Context));
739+ if (MemberCallExpr)
740+ return MemberCallExpr;
741+ const auto Matches =
742+ match (stmt (forEachDescendant (
743+ memberExpr (hasObjectExpression (canResolveToExprPointee (Exp)))
744+ .bind (NodeID<Expr>::value))),
745+ Stm, Context);
746+ return findExprMutation (Matches);
747+ }
748+
749+ const Stmt *
750+ ExprMutationAnalyzer::Analyzer::findPointeeToNonConst (const Expr *Exp) {
751+ const auto NonConstPointerOrDependentType =
752+ type (anyOf (nonConstPointerType (), isDependentType ()));
753+
754+ // assign
755+ const auto InitToNonConst =
756+ varDecl (hasType (NonConstPointerOrDependentType),
757+ hasInitializer (expr (canResolveToExprPointee (Exp)).bind (" stmt" )));
758+ const auto AssignToNonConst =
759+ binaryOperation (hasOperatorName (" =" ),
760+ hasLHS (expr (hasType (NonConstPointerOrDependentType))),
761+ hasRHS (canResolveToExprPointee (Exp)));
762+ // arguments like
763+ const auto ArgOfInstantiationDependent = allOf (
764+ hasAnyArgument (canResolveToExprPointee (Exp)), isInstantiationDependent ());
765+ const auto ArgOfNonConstParameter = forEachArgumentWithParamType (
766+ canResolveToExprPointee (Exp), NonConstPointerOrDependentType);
767+ const auto CallLikeMatcher =
768+ anyOf (ArgOfNonConstParameter, ArgOfInstantiationDependent);
769+ const auto PassAsNonConstArg =
770+ expr (anyOf (cxxUnresolvedConstructExpr (ArgOfInstantiationDependent),
771+ cxxConstructExpr (CallLikeMatcher), callExpr (CallLikeMatcher),
772+ parenListExpr (has (canResolveToExprPointee (Exp))),
773+ initListExpr (hasAnyInit (canResolveToExprPointee (Exp)))));
774+ // cast
775+ const auto CastToNonConst =
776+ explicitCastExpr (hasSourceExpression (canResolveToExprPointee (Exp)),
777+ hasDestinationType (NonConstPointerOrDependentType));
778+
779+ // capture
780+ // FIXME: false positive if the pointee does not change in lambda
781+ const auto CaptureNoConst = lambdaExpr (hasCaptureInit (Exp));
782+
783+ const auto Matches =
784+ match (stmt (anyOf (forEachDescendant (
785+ stmt (anyOf (AssignToNonConst, PassAsNonConstArg,
786+ CastToNonConst, CaptureNoConst))
787+ .bind (" stmt" )),
788+ forEachDescendant (decl (InitToNonConst)))),
789+ Stm, Context);
790+ return selectFirst<Stmt>(" stmt" , Matches);
791+ }
792+
657793FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer (
658794 const FunctionDecl &Func, ASTContext &Context,
659795 ExprMutationAnalyzer::Memoized &Memorized)
0 commit comments