8
8
#include " clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9
9
#include " clang/AST/Expr.h"
10
10
#include " clang/AST/OperationKinds.h"
11
+ #include " clang/AST/Stmt.h"
11
12
#include " clang/ASTMatchers/ASTMatchFinder.h"
12
13
#include " clang/ASTMatchers/ASTMatchers.h"
14
+ #include " clang/ASTMatchers/ASTMatchersMacros.h"
13
15
#include " llvm/ADT/STLExtras.h"
14
16
15
17
namespace clang {
@@ -22,7 +24,6 @@ using namespace ast_matchers;
22
24
// - ConditionalOperator
23
25
// - BinaryConditionalOperator
24
26
static bool canExprResolveTo (const Expr *Source, const Expr *Target) {
25
-
26
27
const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
27
28
if (Matcher (E))
28
29
return true ;
@@ -79,6 +80,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
79
80
80
81
namespace {
81
82
83
+ AST_MATCHER (Type, isDependentType) { return Node.isDependentType (); }
84
+
82
85
AST_MATCHER_P (LambdaExpr, hasCaptureInit, const Expr *, E) {
83
86
return llvm::is_contained (Node.capture_inits (), E);
84
87
}
@@ -99,6 +102,59 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
99
102
return canExprResolveTo (Exp, Target);
100
103
}
101
104
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
+
102
158
// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
103
159
// not have the 'arguments()' method.
104
160
AST_MATCHER_P (InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -208,7 +264,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
208
264
209
265
const Stmt *
210
266
ExprMutationAnalyzer::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 );
212
275
}
213
276
214
277
const Stmt *
@@ -377,7 +440,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
377
440
// references.
378
441
const auto NonConstRefParam = forEachArgumentWithParamType (
379
442
anyOf (canResolveToExpr (Exp),
380
- memberExpr (hasObjectExpression (canResolveToExpr (Exp)))),
443
+ memberExpr (
444
+ hasObjectExpression (ignoringImpCasts (canResolveToExpr (Exp))))),
381
445
nonConstReferenceType ());
382
446
const auto NotInstantiated = unless (hasDeclaration (isInstantiated ()));
383
447
@@ -643,6 +707,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
643
707
return nullptr ;
644
708
}
645
709
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
+
646
787
FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer (
647
788
const FunctionDecl &Func, ASTContext &Context,
648
789
ExprMutationAnalyzer::Memoized &Memorized)
0 commit comments