ExprMutationAnalyzer.cpp 27 KB

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