ExprMutationAnalyzer.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
  9. #include "clang/AST/Expr.h"
  10. #include "clang/AST/OperationKinds.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/ASTMatchers/ASTMatchers.h"
  13. #include "llvm/ADT/STLExtras.h"
  14. namespace clang {
  15. using namespace ast_matchers;
  16. namespace {
  17. AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
  18. return llvm::is_contained(Node.capture_inits(), E);
  19. }
  20. AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
  21. ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
  22. const DeclStmt *const Range = Node.getRangeStmt();
  23. return InnerMatcher.matches(*Range, Finder, Builder);
  24. }
  25. AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>,
  26. InnerMatcher) {
  27. const Expr *Result = &Node;
  28. while (const auto *BOComma =
  29. dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
  30. if (!BOComma->isCommaOp())
  31. break;
  32. Result = BOComma->getRHS();
  33. }
  34. return InnerMatcher.matches(*Result, Finder, Builder);
  35. }
  36. AST_MATCHER_P(Stmt, canResolveToExpr, ast_matchers::internal::Matcher<Stmt>,
  37. InnerMatcher) {
  38. auto *Exp = dyn_cast<Expr>(&Node);
  39. if (!Exp) {
  40. return stmt().matches(Node, Finder, Builder);
  41. }
  42. auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) {
  43. return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase),
  44. hasCastKind(CK_UncheckedDerivedToBase)),
  45. hasSourceExpression(Inner));
  46. };
  47. auto IgnoreDerivedToBase =
  48. [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) {
  49. return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner))));
  50. };
  51. // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`.
  52. // This matching must be recursive because `<expr>` can be anything resolving
  53. // to the `InnerMatcher`, for example another conditional operator.
  54. // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
  55. // is handled, too. The implicit cast happens outside of the conditional.
  56. // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
  57. // below.
  58. auto const ConditionalOperator = conditionalOperator(anyOf(
  59. hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
  60. hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
  61. auto const ElvisOperator = binaryConditionalOperator(anyOf(
  62. hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
  63. hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
  64. auto const ComplexMatcher = ignoringParens(
  65. expr(anyOf(IgnoreDerivedToBase(InnerMatcher),
  66. maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)),
  67. IgnoreDerivedToBase(ConditionalOperator),
  68. IgnoreDerivedToBase(ElvisOperator))));
  69. return ComplexMatcher.matches(*Exp, Finder, Builder);
  70. }
  71. // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
  72. // not have the 'arguments()' method.
  73. AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
  74. InnerMatcher) {
  75. for (const Expr *Arg : Node.inits()) {
  76. ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
  77. if (InnerMatcher.matches(*Arg, Finder, &Result)) {
  78. *Builder = std::move(Result);
  79. return true;
  80. }
  81. }
  82. return false;
  83. }
  84. const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
  85. cxxTypeidExpr;
  86. AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
  87. return Node.isPotentiallyEvaluated();
  88. }
  89. AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
  90. ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
  91. return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
  92. }
  93. const auto nonConstReferenceType = [] {
  94. return hasUnqualifiedDesugaredType(
  95. referenceType(pointee(unless(isConstQualified()))));
  96. };
  97. const auto nonConstPointerType = [] {
  98. return hasUnqualifiedDesugaredType(
  99. pointerType(pointee(unless(isConstQualified()))));
  100. };
  101. const auto isMoveOnly = [] {
  102. return cxxRecordDecl(
  103. hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
  104. hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
  105. unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
  106. unless(isDeleted()))),
  107. hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
  108. unless(isDeleted()))))));
  109. };
  110. template <class T> struct NodeID;
  111. template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
  112. template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
  113. constexpr StringRef NodeID<Expr>::value;
  114. constexpr StringRef NodeID<Decl>::value;
  115. template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
  116. const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
  117. ExprMutationAnalyzer *Analyzer, F Finder) {
  118. const StringRef ID = NodeID<T>::value;
  119. for (const auto &Nodes : Matches) {
  120. if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
  121. return S;
  122. }
  123. return nullptr;
  124. }
  125. } // namespace
  126. const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
  127. return findMutationMemoized(Exp,
  128. {&ExprMutationAnalyzer::findDirectMutation,
  129. &ExprMutationAnalyzer::findMemberMutation,
  130. &ExprMutationAnalyzer::findArrayElementMutation,
  131. &ExprMutationAnalyzer::findCastMutation,
  132. &ExprMutationAnalyzer::findRangeLoopMutation,
  133. &ExprMutationAnalyzer::findReferenceMutation,
  134. &ExprMutationAnalyzer::findFunctionArgMutation},
  135. Results);
  136. }
  137. const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
  138. return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
  139. }
  140. const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
  141. return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
  142. }
  143. const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
  144. return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
  145. }
  146. const Stmt *ExprMutationAnalyzer::findMutationMemoized(
  147. const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
  148. ResultMap &MemoizedResults) {
  149. const auto Memoized = MemoizedResults.find(Exp);
  150. if (Memoized != MemoizedResults.end())
  151. return Memoized->second;
  152. if (isUnevaluated(Exp))
  153. return MemoizedResults[Exp] = nullptr;
  154. for (const auto &Finder : Finders) {
  155. if (const Stmt *S = (this->*Finder)(Exp))
  156. return MemoizedResults[Exp] = S;
  157. }
  158. return MemoizedResults[Exp] = nullptr;
  159. }
  160. const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
  161. MutationFinder Finder) {
  162. const auto Refs =
  163. match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
  164. Stm, Context);
  165. for (const auto &RefNodes : Refs) {
  166. const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
  167. if ((this->*Finder)(E))
  168. return E;
  169. }
  170. return nullptr;
  171. }
  172. bool ExprMutationAnalyzer::isUnevaluated(const Stmt *Exp, const Stmt &Stm,
  173. ASTContext &Context) {
  174. return selectFirst<Stmt>(
  175. NodeID<Expr>::value,
  176. match(
  177. findAll(
  178. stmt(canResolveToExpr(equalsNode(Exp)),
  179. anyOf(
  180. // `Exp` is part of the underlying expression of
  181. // decltype/typeof if it has an ancestor of
  182. // typeLoc.
  183. hasAncestor(typeLoc(unless(
  184. hasAncestor(unaryExprOrTypeTraitExpr())))),
  185. hasAncestor(expr(anyOf(
  186. // `UnaryExprOrTypeTraitExpr` is unevaluated
  187. // unless it's sizeof on VLA.
  188. unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
  189. hasArgumentOfType(variableArrayType())))),
  190. // `CXXTypeidExpr` is unevaluated unless it's
  191. // applied to an expression of glvalue of
  192. // polymorphic class type.
  193. cxxTypeidExpr(
  194. unless(isPotentiallyEvaluated())),
  195. // The controlling expression of
  196. // `GenericSelectionExpr` is unevaluated.
  197. genericSelectionExpr(hasControllingExpr(
  198. hasDescendant(equalsNode(Exp)))),
  199. cxxNoexceptExpr())))))
  200. .bind(NodeID<Expr>::value)),
  201. Stm, Context)) != nullptr;
  202. }
  203. bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
  204. return isUnevaluated(Exp, Stm, Context);
  205. }
  206. const Stmt *
  207. ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
  208. return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
  209. }
  210. const Stmt *
  211. ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
  212. return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
  213. }
  214. const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
  215. ArrayRef<ast_matchers::BoundNodes> Matches) {
  216. return tryEachMatch<Expr>(Matches, this,
  217. &ExprMutationAnalyzer::findPointeeMutation);
  218. }
  219. const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
  220. ArrayRef<ast_matchers::BoundNodes> Matches) {
  221. return tryEachMatch<Decl>(Matches, this,
  222. &ExprMutationAnalyzer::findPointeeMutation);
  223. }
  224. const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
  225. // LHS of any assignment operators.
  226. const auto AsAssignmentLhs = binaryOperator(
  227. isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp))));
  228. // Operand of increment/decrement operators.
  229. const auto AsIncDecOperand =
  230. unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
  231. hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
  232. // Invoking non-const member function.
  233. // A member function is assumed to be non-const when it is unresolved.
  234. const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
  235. const auto AsNonConstThis = expr(anyOf(
  236. cxxMemberCallExpr(callee(NonConstMethod),
  237. on(canResolveToExpr(equalsNode(Exp)))),
  238. cxxOperatorCallExpr(callee(NonConstMethod),
  239. hasArgument(0, canResolveToExpr(equalsNode(Exp)))),
  240. // In case of a templated type, calling overloaded operators is not
  241. // resolved and modelled as `binaryOperator` on a dependent type.
  242. // Such instances are considered a modification, because they can modify
  243. // in different instantiations of the template.
  244. binaryOperator(hasEitherOperand(
  245. allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))),
  246. isTypeDependent()))),
  247. // Within class templates and member functions the member expression might
  248. // not be resolved. In that case, the `callExpr` is considered to be a
  249. // modification.
  250. callExpr(
  251. callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression(
  252. canResolveToExpr(equalsNode(Exp)))),
  253. cxxDependentScopeMemberExpr(hasObjectExpression(
  254. canResolveToExpr(equalsNode(Exp)))))))),
  255. // Match on a call to a known method, but the call itself is type
  256. // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
  257. callExpr(allOf(isTypeDependent(),
  258. callee(memberExpr(hasDeclaration(NonConstMethod),
  259. hasObjectExpression(canResolveToExpr(
  260. equalsNode(Exp)))))))));
  261. // Taking address of 'Exp'.
  262. // We're assuming 'Exp' is mutated as soon as its address is taken, though in
  263. // theory we can follow the pointer and see whether it escaped `Stm` or is
  264. // dereferenced and then mutated. This is left for future improvements.
  265. const auto AsAmpersandOperand =
  266. unaryOperator(hasOperatorName("&"),
  267. // A NoOp implicit cast is adding const.
  268. unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
  269. hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
  270. const auto AsPointerFromArrayDecay =
  271. castExpr(hasCastKind(CK_ArrayToPointerDecay),
  272. unless(hasParent(arraySubscriptExpr())),
  273. has(canResolveToExpr(equalsNode(Exp))));
  274. // Treat calling `operator->()` of move-only classes as taking address.
  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 AsOperatorArrowThis = cxxOperatorCallExpr(
  278. hasOverloadedOperatorName("->"),
  279. callee(
  280. cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
  281. argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp))));
  282. // Used as non-const-ref argument when calling a function.
  283. // An argument is assumed to be non-const-ref when the function is unresolved.
  284. // Instantiated template functions are not handled here but in
  285. // findFunctionArgMutation which has additional smarts for handling forwarding
  286. // references.
  287. const auto NonConstRefParam = forEachArgumentWithParamType(
  288. anyOf(canResolveToExpr(equalsNode(Exp)),
  289. memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))),
  290. nonConstReferenceType());
  291. const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
  292. const auto TypeDependentCallee =
  293. callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
  294. cxxDependentScopeMemberExpr(),
  295. hasType(templateTypeParmType()), isTypeDependent())));
  296. const auto AsNonConstRefArg = anyOf(
  297. callExpr(NonConstRefParam, NotInstantiated),
  298. cxxConstructExpr(NonConstRefParam, NotInstantiated),
  299. callExpr(TypeDependentCallee,
  300. hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
  301. cxxUnresolvedConstructExpr(
  302. hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
  303. // Previous False Positive in the following Code:
  304. // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
  305. // Where the constructor of `Type` takes its argument as reference.
  306. // The AST does not resolve in a `cxxConstructExpr` because it is
  307. // type-dependent.
  308. parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))),
  309. // If the initializer is for a reference type, there is no cast for
  310. // the variable. Values are cast to RValue first.
  311. initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp))))));
  312. // Captured by a lambda by reference.
  313. // If we're initializing a capture with 'Exp' directly then we're initializing
  314. // a reference capture.
  315. // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
  316. const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
  317. // Returned as non-const-ref.
  318. // If we're returning 'Exp' directly then it's returned as non-const-ref.
  319. // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
  320. // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
  321. // adding const.)
  322. const auto AsNonConstRefReturn =
  323. returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp))));
  324. // It is used as a non-const-reference for initalizing a range-for loop.
  325. const auto AsNonConstRefRangeInit = cxxForRangeStmt(
  326. hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)),
  327. hasType(nonConstReferenceType())))));
  328. const auto Matches = match(
  329. traverse(TK_AsIs,
  330. findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand,
  331. AsNonConstThis, AsAmpersandOperand,
  332. AsPointerFromArrayDecay, AsOperatorArrowThis,
  333. AsNonConstRefArg, AsLambdaRefCaptureInit,
  334. AsNonConstRefReturn, AsNonConstRefRangeInit))
  335. .bind("stmt"))),
  336. Stm, Context);
  337. return selectFirst<Stmt>("stmt", Matches);
  338. }
  339. const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
  340. // Check whether any member of 'Exp' is mutated.
  341. const auto MemberExprs =
  342. match(findAll(expr(anyOf(memberExpr(hasObjectExpression(
  343. canResolveToExpr(equalsNode(Exp)))),
  344. cxxDependentScopeMemberExpr(hasObjectExpression(
  345. canResolveToExpr(equalsNode(Exp))))))
  346. .bind(NodeID<Expr>::value)),
  347. Stm, Context);
  348. return findExprMutation(MemberExprs);
  349. }
  350. const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
  351. // Check whether any element of an array is mutated.
  352. const auto SubscriptExprs =
  353. match(findAll(arraySubscriptExpr(
  354. anyOf(hasBase(canResolveToExpr(equalsNode(Exp))),
  355. hasBase(implicitCastExpr(
  356. allOf(hasCastKind(CK_ArrayToPointerDecay),
  357. hasSourceExpression(canResolveToExpr(
  358. equalsNode(Exp))))))))
  359. .bind(NodeID<Expr>::value)),
  360. Stm, Context);
  361. return findExprMutation(SubscriptExprs);
  362. }
  363. const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
  364. // If the 'Exp' is explicitly casted to a non-const reference type the
  365. // 'Exp' is considered to be modified.
  366. const auto ExplicitCast = match(
  367. findAll(
  368. stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
  369. explicitCastExpr(
  370. hasDestinationType(nonConstReferenceType()))))
  371. .bind("stmt")),
  372. Stm, Context);
  373. if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
  374. return CastStmt;
  375. // If 'Exp' is casted to any non-const reference type, check the castExpr.
  376. const auto Casts = match(
  377. findAll(
  378. expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
  379. anyOf(explicitCastExpr(
  380. hasDestinationType(nonConstReferenceType())),
  381. implicitCastExpr(hasImplicitDestinationType(
  382. nonConstReferenceType())))))
  383. .bind(NodeID<Expr>::value)),
  384. Stm, Context);
  385. if (const Stmt *S = findExprMutation(Casts))
  386. return S;
  387. // Treat std::{move,forward} as cast.
  388. const auto Calls =
  389. match(findAll(callExpr(callee(namedDecl(
  390. hasAnyName("::std::move", "::std::forward"))),
  391. hasArgument(0, canResolveToExpr(equalsNode(Exp))))
  392. .bind("expr")),
  393. Stm, Context);
  394. return findExprMutation(Calls);
  395. }
  396. const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
  397. // Keep the ordering for the specific initialization matches to happen first,
  398. // because it is cheaper to match all potential modifications of the loop
  399. // variable.
  400. // The range variable is a reference to a builtin array. In that case the
  401. // array is considered modified if the loop-variable is a non-const reference.
  402. const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
  403. hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
  404. const auto RefToArrayRefToElements =
  405. match(findAll(stmt(cxxForRangeStmt(
  406. hasLoopVariable(
  407. varDecl(anyOf(hasType(nonConstReferenceType()),
  408. hasType(nonConstPointerType())))
  409. .bind(NodeID<Decl>::value)),
  410. hasRangeStmt(DeclStmtToNonRefToArray),
  411. hasRangeInit(canResolveToExpr(equalsNode(Exp)))))
  412. .bind("stmt")),
  413. Stm, Context);
  414. if (const auto *BadRangeInitFromArray =
  415. selectFirst<Stmt>("stmt", RefToArrayRefToElements))
  416. return BadRangeInitFromArray;
  417. // Small helper to match special cases in range-for loops.
  418. //
  419. // It is possible that containers do not provide a const-overload for their
  420. // iterator accessors. If this is the case, the variable is used non-const
  421. // no matter what happens in the loop. This requires special detection as it
  422. // is then faster to find all mutations of the loop variable.
  423. // It aims at a different modification as well.
  424. const auto HasAnyNonConstIterator =
  425. anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
  426. unless(hasMethod(allOf(hasName("begin"), isConst())))),
  427. allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
  428. unless(hasMethod(allOf(hasName("end"), isConst())))));
  429. const auto DeclStmtToNonConstIteratorContainer = declStmt(
  430. hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
  431. pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
  432. const auto RefToContainerBadIterators =
  433. match(findAll(stmt(cxxForRangeStmt(allOf(
  434. hasRangeStmt(DeclStmtToNonConstIteratorContainer),
  435. hasRangeInit(canResolveToExpr(equalsNode(Exp))))))
  436. .bind("stmt")),
  437. Stm, Context);
  438. if (const auto *BadIteratorsContainer =
  439. selectFirst<Stmt>("stmt", RefToContainerBadIterators))
  440. return BadIteratorsContainer;
  441. // If range for looping over 'Exp' with a non-const reference loop variable,
  442. // check all declRefExpr of the loop variable.
  443. const auto LoopVars =
  444. match(findAll(cxxForRangeStmt(
  445. hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
  446. .bind(NodeID<Decl>::value)),
  447. hasRangeInit(canResolveToExpr(equalsNode(Exp))))),
  448. Stm, Context);
  449. return findDeclMutation(LoopVars);
  450. }
  451. const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
  452. // Follow non-const reference returned by `operator*()` of move-only classes.
  453. // These are typically smart pointers with unique ownership so we treat
  454. // mutation of pointee as mutation of the smart pointer itself.
  455. const auto Ref =
  456. match(findAll(cxxOperatorCallExpr(
  457. hasOverloadedOperatorName("*"),
  458. callee(cxxMethodDecl(ofClass(isMoveOnly()),
  459. returns(nonConstReferenceType()))),
  460. argumentCountIs(1),
  461. hasArgument(0, canResolveToExpr(equalsNode(Exp))))
  462. .bind(NodeID<Expr>::value)),
  463. Stm, Context);
  464. if (const Stmt *S = findExprMutation(Ref))
  465. return S;
  466. // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
  467. const auto Refs = match(
  468. stmt(forEachDescendant(
  469. varDecl(
  470. hasType(nonConstReferenceType()),
  471. hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)),
  472. memberExpr(hasObjectExpression(
  473. canResolveToExpr(equalsNode(Exp)))))),
  474. hasParent(declStmt().bind("stmt")),
  475. // Don't follow the reference in range statement, we've
  476. // handled that separately.
  477. unless(hasParent(declStmt(hasParent(
  478. cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
  479. .bind(NodeID<Decl>::value))),
  480. Stm, Context);
  481. return findDeclMutation(Refs);
  482. }
  483. const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
  484. const auto NonConstRefParam = forEachArgumentWithParam(
  485. canResolveToExpr(equalsNode(Exp)),
  486. parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
  487. const auto IsInstantiated = hasDeclaration(isInstantiated());
  488. const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
  489. const auto Matches = match(
  490. traverse(
  491. TK_AsIs,
  492. findAll(
  493. expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
  494. unless(callee(namedDecl(hasAnyName(
  495. "::std::move", "::std::forward"))))),
  496. cxxConstructExpr(NonConstRefParam, IsInstantiated,
  497. FuncDecl)))
  498. .bind(NodeID<Expr>::value))),
  499. Stm, Context);
  500. for (const auto &Nodes : Matches) {
  501. const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
  502. const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
  503. if (!Func->getBody() || !Func->getPrimaryTemplate())
  504. return Exp;
  505. const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
  506. const ArrayRef<ParmVarDecl *> AllParams =
  507. Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
  508. QualType ParmType =
  509. AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
  510. AllParams.size() - 1)]
  511. ->getType();
  512. if (const auto *T = ParmType->getAs<PackExpansionType>())
  513. ParmType = T->getPattern();
  514. // If param type is forwarding reference, follow into the function
  515. // definition and see whether the param is mutated inside.
  516. if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
  517. if (!RefType->getPointeeType().getQualifiers() &&
  518. RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
  519. std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
  520. FuncParmAnalyzer[Func];
  521. if (!Analyzer)
  522. Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
  523. if (Analyzer->findMutation(Parm))
  524. return Exp;
  525. continue;
  526. }
  527. }
  528. // Not forwarding reference.
  529. return Exp;
  530. }
  531. return nullptr;
  532. }
  533. FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
  534. const FunctionDecl &Func, ASTContext &Context)
  535. : BodyAnalyzer(*Func.getBody(), Context) {
  536. if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
  537. // CXXCtorInitializer might also mutate Param but they're not part of
  538. // function body, check them eagerly here since they're typically trivial.
  539. for (const CXXCtorInitializer *Init : Ctor->inits()) {
  540. ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
  541. for (const ParmVarDecl *Parm : Ctor->parameters()) {
  542. if (Results.find(Parm) != Results.end())
  543. continue;
  544. if (const Stmt *S = InitAnalyzer.findMutation(Parm))
  545. Results[Parm] = S;
  546. }
  547. }
  548. }
  549. }
  550. const Stmt *
  551. FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
  552. const auto Memoized = Results.find(Parm);
  553. if (Memoized != Results.end())
  554. return Memoized->second;
  555. if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
  556. return Results[Parm] = S;
  557. return Results[Parm] = nullptr;
  558. }
  559. } // namespace clang