clang-tools  7.0.0
ExprMutationAnalyzer.cpp
Go to the documentation of this file.
1 //===---------- ExprMutationAnalyzer.cpp - clang-tidy ---------------------===//
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 "ExprMutationAnalyzer.h"
10 
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "llvm/ADT/STLExtras.h"
13 
14 namespace clang {
15 namespace tidy {
16 namespace utils {
17 using namespace ast_matchers;
18 
19 namespace {
20 
21 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
22  return llvm::is_contained(Node.capture_inits(), E);
23 }
24 
25 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
26  ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
27  const DeclStmt *const Range = Node.getRangeStmt();
28  return InnerMatcher.matches(*Range, Finder, Builder);
29 }
30 
31 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
32  cxxTypeidExpr;
33 
34 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
35  return Node.isPotentiallyEvaluated();
36 }
37 
38 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
39  cxxNoexceptExpr;
40 
41 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
42  GenericSelectionExpr>
43  genericSelectionExpr;
44 
45 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
46  ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
47  return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
48 }
49 
50 const auto nonConstReferenceType = [] {
51  return referenceType(pointee(unless(isConstQualified())));
52 };
53 
54 } // namespace
55 
56 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
57  const auto Memoized = Results.find(Exp);
58  if (Memoized != Results.end())
59  return Memoized->second;
60 
61  if (isUnevaluated(Exp))
62  return Results[Exp] = nullptr;
63 
64  for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation,
65  &ExprMutationAnalyzer::findMemberMutation,
66  &ExprMutationAnalyzer::findArrayElementMutation,
67  &ExprMutationAnalyzer::findCastMutation,
68  &ExprMutationAnalyzer::findRangeLoopMutation,
69  &ExprMutationAnalyzer::findReferenceMutation}) {
70  if (const Stmt *S = (this->*Finder)(Exp))
71  return Results[Exp] = S;
72  }
73 
74  return Results[Exp] = nullptr;
75 }
76 
77 bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
78  return selectFirst<Expr>(
79  "expr",
80  match(
81  findAll(
82  expr(equalsNode(Exp),
83  anyOf(
84  // `Exp` is part of the underlying expression of
85  // decltype/typeof if it has an ancestor of
86  // typeLoc.
87  hasAncestor(typeLoc(unless(
88  hasAncestor(unaryExprOrTypeTraitExpr())))),
89  hasAncestor(expr(anyOf(
90  // `UnaryExprOrTypeTraitExpr` is unevaluated
91  // unless it's sizeof on VLA.
92  unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
93  hasArgumentOfType(variableArrayType())))),
94  // `CXXTypeidExpr` is unevaluated unless it's
95  // applied to an expression of glvalue of
96  // polymorphic class type.
97  cxxTypeidExpr(
98  unless(isPotentiallyEvaluated())),
99  // The controlling expression of
100  // `GenericSelectionExpr` is unevaluated.
101  genericSelectionExpr(hasControllingExpr(
102  hasDescendant(equalsNode(Exp)))),
103  cxxNoexceptExpr())))))
104  .bind("expr")),
105  *Stm, *Context)) != nullptr;
106 }
107 
108 const Stmt *
109 ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
110  for (const auto &Nodes : Matches) {
111  if (const Stmt *S = findMutation(Nodes.getNodeAs<Expr>("expr")))
112  return S;
113  }
114  return nullptr;
115 }
116 
117 const Stmt *
118 ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
119  for (const auto &DeclNodes : Matches) {
120  if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs<Decl>("decl")))
121  return S;
122  }
123  return nullptr;
124 }
125 
126 const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) {
127  const auto Refs = match(
128  findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), *Stm, *Context);
129  for (const auto &RefNodes : Refs) {
130  const auto *E = RefNodes.getNodeAs<Expr>("expr");
131  if (findMutation(E))
132  return E;
133  }
134  return nullptr;
135 }
136 
137 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
138  // LHS of any assignment operators.
139  const auto AsAssignmentLhs =
140  binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
141 
142  // Operand of increment/decrement operators.
143  const auto AsIncDecOperand =
144  unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
145  hasUnaryOperand(equalsNode(Exp)));
146 
147  // Invoking non-const member function.
148  const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
149  const auto AsNonConstThis =
150  expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
151  cxxOperatorCallExpr(callee(NonConstMethod),
152  hasArgument(0, equalsNode(Exp)))));
153 
154  // Taking address of 'Exp'.
155  // We're assuming 'Exp' is mutated as soon as its address is taken, though in
156  // theory we can follow the pointer and see whether it escaped `Stm` or is
157  // dereferenced and then mutated. This is left for future improvements.
158  const auto AsAmpersandOperand =
159  unaryOperator(hasOperatorName("&"),
160  // A NoOp implicit cast is adding const.
161  unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
162  hasUnaryOperand(equalsNode(Exp)));
163  const auto AsPointerFromArrayDecay =
164  castExpr(hasCastKind(CK_ArrayToPointerDecay),
165  unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
166 
167  // Used as non-const-ref argument when calling a function.
168  const auto NonConstRefParam = forEachArgumentWithParam(
169  equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
170  const auto AsNonConstRefArg =
171  anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam));
172 
173  // Captured by a lambda by reference.
174  // If we're initializing a capture with 'Exp' directly then we're initializing
175  // a reference capture.
176  // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
177  const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
178 
179  // Returned as non-const-ref.
180  // If we're returning 'Exp' directly then it's returned as non-const-ref.
181  // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
182  // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
183  // adding const.)
184  const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
185 
186  const auto Matches =
187  match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
188  AsAmpersandOperand, AsPointerFromArrayDecay,
189  AsNonConstRefArg, AsLambdaRefCaptureInit,
190  AsNonConstRefReturn))
191  .bind("stmt")),
192  *Stm, *Context);
193  return selectFirst<Stmt>("stmt", Matches);
194 }
195 
196 const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
197  // Check whether any member of 'Exp' is mutated.
198  const auto MemberExprs = match(
199  findAll(memberExpr(hasObjectExpression(equalsNode(Exp))).bind("expr")),
200  *Stm, *Context);
201  return findExprMutation(MemberExprs);
202 }
203 
204 const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
205  // Check whether any element of an array is mutated.
206  const auto SubscriptExprs = match(
207  findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
208  .bind("expr")),
209  *Stm, *Context);
210  return findExprMutation(SubscriptExprs);
211 }
212 
213 const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
214  // If 'Exp' is casted to any non-const reference type, check the castExpr.
215  const auto Casts =
216  match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
217  anyOf(explicitCastExpr(hasDestinationType(
218  nonConstReferenceType())),
219  implicitCastExpr(hasImplicitDestinationType(
220  nonConstReferenceType()))))
221  .bind("expr")),
222  *Stm, *Context);
223  return findExprMutation(Casts);
224 }
225 
226 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
227  // If range for looping over 'Exp' with a non-const reference loop variable,
228  // check all declRefExpr of the loop variable.
229  const auto LoopVars =
230  match(findAll(cxxForRangeStmt(
231  hasLoopVariable(
232  varDecl(hasType(nonConstReferenceType())).bind("decl")),
233  hasRangeInit(equalsNode(Exp)))),
234  *Stm, *Context);
235  return findDeclMutation(LoopVars);
236 }
237 
238 const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
239  // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
240  const auto Refs = match(
241  stmt(forEachDescendant(
242  varDecl(
243  hasType(nonConstReferenceType()),
244  hasInitializer(anyOf(equalsNode(Exp),
245  conditionalOperator(anyOf(
246  hasTrueExpression(equalsNode(Exp)),
247  hasFalseExpression(equalsNode(Exp)))))),
248  hasParent(declStmt().bind("stmt")),
249  // Don't follow the reference in range statement, we've handled
250  // that separately.
251  unless(hasParent(declStmt(hasParent(
252  cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
253  .bind("decl"))),
254  *Stm, *Context);
255  return findDeclMutation(Refs);
256 }
257 
258 } // namespace utils
259 } // namespace tidy
260 } // namespace clang
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
std::vector< CodeCompletionResult > Results
const Stmt * findMutation(const Expr *Exp)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
AST_MATCHER_P(NamespaceAliasDecl, hasTargetNamespace, ast_matchers::internal::Matcher< NamespaceDecl >, innerMatcher)