xref: /llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp (revision e9192f83894cc0e09148515fb8cc59aeddc077c3)
1 //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "llvm/ADT/STLExtras.h"
12 
13 namespace clang {
14 using namespace ast_matchers;
15 
16 namespace {
17 
18 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
19   return llvm::is_contained(Node.capture_inits(), E);
20 }
21 
22 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
23               ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
24   const DeclStmt *const Range = Node.getRangeStmt();
25   return InnerMatcher.matches(*Range, Finder, Builder);
26 }
27 
28 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
29     cxxTypeidExpr;
30 
31 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
32   return Node.isPotentiallyEvaluated();
33 }
34 
35 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
36     cxxNoexceptExpr;
37 
38 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
39                                                           GenericSelectionExpr>
40     genericSelectionExpr;
41 
42 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
43               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
44   return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
45 }
46 
47 const auto nonConstReferenceType = [] {
48   return hasUnqualifiedDesugaredType(
49       referenceType(pointee(unless(isConstQualified()))));
50 };
51 
52 const auto nonConstPointerType = [] {
53   return hasUnqualifiedDesugaredType(
54       pointerType(pointee(unless(isConstQualified()))));
55 };
56 
57 const auto isMoveOnly = [] {
58   return cxxRecordDecl(
59       hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
60       hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
61       unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
62                                                 unless(isDeleted()))),
63                    hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
64                                            unless(isDeleted()))))));
65 };
66 
67 } // namespace
68 
69 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
70   const auto Memoized = Results.find(Exp);
71   if (Memoized != Results.end())
72     return Memoized->second;
73 
74   if (isUnevaluated(Exp))
75     return Results[Exp] = nullptr;
76 
77   for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation,
78                              &ExprMutationAnalyzer::findMemberMutation,
79                              &ExprMutationAnalyzer::findArrayElementMutation,
80                              &ExprMutationAnalyzer::findCastMutation,
81                              &ExprMutationAnalyzer::findRangeLoopMutation,
82                              &ExprMutationAnalyzer::findReferenceMutation}) {
83     if (const Stmt *S = (this->*Finder)(Exp))
84       return Results[Exp] = S;
85   }
86 
87   return Results[Exp] = nullptr;
88 }
89 
90 bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
91   return selectFirst<Expr>(
92              "expr",
93              match(
94                  findAll(
95                      expr(equalsNode(Exp),
96                           anyOf(
97                               // `Exp` is part of the underlying expression of
98                               // decltype/typeof if it has an ancestor of
99                               // typeLoc.
100                               hasAncestor(typeLoc(unless(
101                                   hasAncestor(unaryExprOrTypeTraitExpr())))),
102                               hasAncestor(expr(anyOf(
103                                   // `UnaryExprOrTypeTraitExpr` is unevaluated
104                                   // unless it's sizeof on VLA.
105                                   unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
106                                       hasArgumentOfType(variableArrayType())))),
107                                   // `CXXTypeidExpr` is unevaluated unless it's
108                                   // applied to an expression of glvalue of
109                                   // polymorphic class type.
110                                   cxxTypeidExpr(
111                                       unless(isPotentiallyEvaluated())),
112                                   // The controlling expression of
113                                   // `GenericSelectionExpr` is unevaluated.
114                                   genericSelectionExpr(hasControllingExpr(
115                                       hasDescendant(equalsNode(Exp)))),
116                                   cxxNoexceptExpr())))))
117                          .bind("expr")),
118                  Stm, Context)) != nullptr;
119 }
120 
121 const Stmt *
122 ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
123   for (const auto &Nodes : Matches) {
124     if (const Stmt *S = findMutation(Nodes.getNodeAs<Expr>("expr")))
125       return S;
126   }
127   return nullptr;
128 }
129 
130 const Stmt *
131 ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
132   for (const auto &DeclNodes : Matches) {
133     if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs<Decl>("decl")))
134       return S;
135   }
136   return nullptr;
137 }
138 
139 const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) {
140   const auto Refs = match(
141       findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context);
142   for (const auto &RefNodes : Refs) {
143     const auto *E = RefNodes.getNodeAs<Expr>("expr");
144     if (findMutation(E))
145       return E;
146   }
147   return nullptr;
148 }
149 
150 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
151   // LHS of any assignment operators.
152   const auto AsAssignmentLhs =
153       binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
154 
155   // Operand of increment/decrement operators.
156   const auto AsIncDecOperand =
157       unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
158                     hasUnaryOperand(equalsNode(Exp)));
159 
160   // Invoking non-const member function.
161   // A member function is assumed to be non-const when it is unresolved.
162   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
163   const auto AsNonConstThis =
164       expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
165                  cxxOperatorCallExpr(callee(NonConstMethod),
166                                      hasArgument(0, equalsNode(Exp))),
167                  callExpr(callee(expr(anyOf(
168                      unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
169                      cxxDependentScopeMemberExpr(
170                          hasObjectExpression(equalsNode(Exp)))))))));
171 
172   // Taking address of 'Exp'.
173   // We're assuming 'Exp' is mutated as soon as its address is taken, though in
174   // theory we can follow the pointer and see whether it escaped `Stm` or is
175   // dereferenced and then mutated. This is left for future improvements.
176   const auto AsAmpersandOperand =
177       unaryOperator(hasOperatorName("&"),
178                     // A NoOp implicit cast is adding const.
179                     unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
180                     hasUnaryOperand(equalsNode(Exp)));
181   const auto AsPointerFromArrayDecay =
182       castExpr(hasCastKind(CK_ArrayToPointerDecay),
183                unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
184   // Treat calling `operator->()` of move-only classes as taking address.
185   // These are typically smart pointers with unique ownership so we treat
186   // mutation of pointee as mutation of the smart pointer itself.
187   const auto AsOperatorArrowThis =
188       cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
189                           callee(cxxMethodDecl(ofClass(isMoveOnly()),
190                                                returns(nonConstPointerType()))),
191                           argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
192 
193   // Used as non-const-ref argument when calling a function.
194   // An argument is assumed to be non-const-ref when the function is unresolved.
195   const auto NonConstRefParam = forEachArgumentWithParam(
196       equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
197   const auto AsNonConstRefArg = anyOf(
198       callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam),
199       callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
200                                  cxxDependentScopeMemberExpr(),
201                                  hasType(templateTypeParmType())))),
202                hasAnyArgument(equalsNode(Exp))),
203       cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
204 
205   // Captured by a lambda by reference.
206   // If we're initializing a capture with 'Exp' directly then we're initializing
207   // a reference capture.
208   // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
209   const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
210 
211   // Returned as non-const-ref.
212   // If we're returning 'Exp' directly then it's returned as non-const-ref.
213   // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
214   // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
215   // adding const.)
216   const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
217 
218   const auto Matches =
219       match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
220                                AsAmpersandOperand, AsPointerFromArrayDecay,
221                                AsOperatorArrowThis, AsNonConstRefArg,
222                                AsLambdaRefCaptureInit, AsNonConstRefReturn))
223                         .bind("stmt")),
224             Stm, Context);
225   return selectFirst<Stmt>("stmt", Matches);
226 }
227 
228 const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
229   // Check whether any member of 'Exp' is mutated.
230   const auto MemberExprs =
231       match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
232                                cxxDependentScopeMemberExpr(
233                                    hasObjectExpression(equalsNode(Exp)))))
234                         .bind("expr")),
235             Stm, Context);
236   return findExprMutation(MemberExprs);
237 }
238 
239 const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
240   // Check whether any element of an array is mutated.
241   const auto SubscriptExprs = match(
242       findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
243                   .bind("expr")),
244       Stm, Context);
245   return findExprMutation(SubscriptExprs);
246 }
247 
248 const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
249   // If 'Exp' is casted to any non-const reference type, check the castExpr.
250   const auto Casts =
251       match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
252                              anyOf(explicitCastExpr(hasDestinationType(
253                                        nonConstReferenceType())),
254                                    implicitCastExpr(hasImplicitDestinationType(
255                                        nonConstReferenceType()))))
256                         .bind("expr")),
257             Stm, Context);
258   return findExprMutation(Casts);
259 }
260 
261 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
262   // If range for looping over 'Exp' with a non-const reference loop variable,
263   // check all declRefExpr of the loop variable.
264   const auto LoopVars =
265       match(findAll(cxxForRangeStmt(
266                 hasLoopVariable(
267                     varDecl(hasType(nonConstReferenceType())).bind("decl")),
268                 hasRangeInit(equalsNode(Exp)))),
269             Stm, Context);
270   return findDeclMutation(LoopVars);
271 }
272 
273 const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
274   // Follow non-const reference returned by `operator*()` of move-only classes.
275   // These are typically smart pointers with unique ownership so we treat
276   // mutation of pointee as mutation of the smart pointer itself.
277   const auto Ref = match(
278       findAll(cxxOperatorCallExpr(
279                   hasOverloadedOperatorName("*"),
280                   callee(cxxMethodDecl(ofClass(isMoveOnly()),
281                                        returns(hasUnqualifiedDesugaredType(
282                                            nonConstReferenceType())))),
283                   argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
284                   .bind("expr")),
285       Stm, Context);
286   if (const Stmt *S = findExprMutation(Ref))
287     return S;
288 
289   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
290   const auto Refs = match(
291       stmt(forEachDescendant(
292           varDecl(
293               hasType(nonConstReferenceType()),
294               hasInitializer(anyOf(equalsNode(Exp),
295                                    conditionalOperator(anyOf(
296                                        hasTrueExpression(equalsNode(Exp)),
297                                        hasFalseExpression(equalsNode(Exp)))))),
298               hasParent(declStmt().bind("stmt")),
299               // Don't follow the reference in range statement, we've handled
300               // that separately.
301               unless(hasParent(declStmt(hasParent(
302                   cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
303               .bind("decl"))),
304       Stm, Context);
305   return findDeclMutation(Refs);
306 }
307 
308 } // namespace clang
309