17330f729Sjoerg //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9*e038c9c4Sjoerg #include "clang/AST/Expr.h"
10*e038c9c4Sjoerg #include "clang/AST/OperationKinds.h"
117330f729Sjoerg #include "clang/ASTMatchers/ASTMatchFinder.h"
12*e038c9c4Sjoerg #include "clang/ASTMatchers/ASTMatchers.h"
137330f729Sjoerg #include "llvm/ADT/STLExtras.h"
147330f729Sjoerg
157330f729Sjoerg namespace clang {
167330f729Sjoerg using namespace ast_matchers;
177330f729Sjoerg
187330f729Sjoerg namespace {
197330f729Sjoerg
AST_MATCHER_P(LambdaExpr,hasCaptureInit,const Expr *,E)207330f729Sjoerg AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
217330f729Sjoerg return llvm::is_contained(Node.capture_inits(), E);
227330f729Sjoerg }
237330f729Sjoerg
AST_MATCHER_P(CXXForRangeStmt,hasRangeStmt,ast_matchers::internal::Matcher<DeclStmt>,InnerMatcher)247330f729Sjoerg AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
257330f729Sjoerg ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
267330f729Sjoerg const DeclStmt *const Range = Node.getRangeStmt();
277330f729Sjoerg return InnerMatcher.matches(*Range, Finder, Builder);
287330f729Sjoerg }
297330f729Sjoerg
AST_MATCHER_P(Expr,maybeEvalCommaExpr,ast_matchers::internal::Matcher<Expr>,InnerMatcher)30*e038c9c4Sjoerg AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>,
31*e038c9c4Sjoerg InnerMatcher) {
327330f729Sjoerg const Expr *Result = &Node;
337330f729Sjoerg while (const auto *BOComma =
347330f729Sjoerg dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
357330f729Sjoerg if (!BOComma->isCommaOp())
367330f729Sjoerg break;
377330f729Sjoerg Result = BOComma->getRHS();
387330f729Sjoerg }
397330f729Sjoerg return InnerMatcher.matches(*Result, Finder, Builder);
407330f729Sjoerg }
417330f729Sjoerg
AST_MATCHER_P(Expr,canResolveToExpr,ast_matchers::internal::Matcher<Expr>,InnerMatcher)42*e038c9c4Sjoerg AST_MATCHER_P(Expr, canResolveToExpr, ast_matchers::internal::Matcher<Expr>,
43*e038c9c4Sjoerg InnerMatcher) {
44*e038c9c4Sjoerg auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) {
45*e038c9c4Sjoerg return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase),
46*e038c9c4Sjoerg hasCastKind(CK_UncheckedDerivedToBase)),
47*e038c9c4Sjoerg hasSourceExpression(Inner));
48*e038c9c4Sjoerg };
49*e038c9c4Sjoerg auto IgnoreDerivedToBase =
50*e038c9c4Sjoerg [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) {
51*e038c9c4Sjoerg return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner))));
52*e038c9c4Sjoerg };
53*e038c9c4Sjoerg
54*e038c9c4Sjoerg // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`.
55*e038c9c4Sjoerg // This matching must be recursive because `<expr>` can be anything resolving
56*e038c9c4Sjoerg // to the `InnerMatcher`, for example another conditional operator.
57*e038c9c4Sjoerg // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
58*e038c9c4Sjoerg // is handled, too. The implicit cast happens outside of the conditional.
59*e038c9c4Sjoerg // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
60*e038c9c4Sjoerg // below.
61*e038c9c4Sjoerg auto const ConditionalOperator = conditionalOperator(anyOf(
62*e038c9c4Sjoerg hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
63*e038c9c4Sjoerg hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
64*e038c9c4Sjoerg auto const ElvisOperator = binaryConditionalOperator(anyOf(
65*e038c9c4Sjoerg hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
66*e038c9c4Sjoerg hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
67*e038c9c4Sjoerg
68*e038c9c4Sjoerg auto const ComplexMatcher = ignoringParens(
69*e038c9c4Sjoerg expr(anyOf(IgnoreDerivedToBase(InnerMatcher),
70*e038c9c4Sjoerg maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)),
71*e038c9c4Sjoerg IgnoreDerivedToBase(ConditionalOperator),
72*e038c9c4Sjoerg IgnoreDerivedToBase(ElvisOperator))));
73*e038c9c4Sjoerg
74*e038c9c4Sjoerg return ComplexMatcher.matches(Node, Finder, Builder);
75*e038c9c4Sjoerg }
76*e038c9c4Sjoerg
77*e038c9c4Sjoerg // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
78*e038c9c4Sjoerg // not have the 'arguments()' method.
AST_MATCHER_P(InitListExpr,hasAnyInit,ast_matchers::internal::Matcher<Expr>,InnerMatcher)79*e038c9c4Sjoerg AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
80*e038c9c4Sjoerg InnerMatcher) {
81*e038c9c4Sjoerg for (const Expr *Arg : Node.inits()) {
82*e038c9c4Sjoerg ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
83*e038c9c4Sjoerg if (InnerMatcher.matches(*Arg, Finder, &Result)) {
84*e038c9c4Sjoerg *Builder = std::move(Result);
85*e038c9c4Sjoerg return true;
86*e038c9c4Sjoerg }
87*e038c9c4Sjoerg }
88*e038c9c4Sjoerg return false;
89*e038c9c4Sjoerg }
90*e038c9c4Sjoerg
917330f729Sjoerg const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
927330f729Sjoerg cxxTypeidExpr;
937330f729Sjoerg
AST_MATCHER(CXXTypeidExpr,isPotentiallyEvaluated)947330f729Sjoerg AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
957330f729Sjoerg return Node.isPotentiallyEvaluated();
967330f729Sjoerg }
977330f729Sjoerg
AST_MATCHER_P(GenericSelectionExpr,hasControllingExpr,ast_matchers::internal::Matcher<Expr>,InnerMatcher)987330f729Sjoerg AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
997330f729Sjoerg ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
1007330f729Sjoerg return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
1017330f729Sjoerg }
1027330f729Sjoerg
__anon78d823ce0402null1037330f729Sjoerg const auto nonConstReferenceType = [] {
1047330f729Sjoerg return hasUnqualifiedDesugaredType(
1057330f729Sjoerg referenceType(pointee(unless(isConstQualified()))));
1067330f729Sjoerg };
1077330f729Sjoerg
__anon78d823ce0502null1087330f729Sjoerg const auto nonConstPointerType = [] {
1097330f729Sjoerg return hasUnqualifiedDesugaredType(
1107330f729Sjoerg pointerType(pointee(unless(isConstQualified()))));
1117330f729Sjoerg };
1127330f729Sjoerg
__anon78d823ce0602null1137330f729Sjoerg const auto isMoveOnly = [] {
1147330f729Sjoerg return cxxRecordDecl(
1157330f729Sjoerg hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
1167330f729Sjoerg hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
1177330f729Sjoerg unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
1187330f729Sjoerg unless(isDeleted()))),
1197330f729Sjoerg hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
1207330f729Sjoerg unless(isDeleted()))))));
1217330f729Sjoerg };
1227330f729Sjoerg
1237330f729Sjoerg template <class T> struct NodeID;
124*e038c9c4Sjoerg template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
125*e038c9c4Sjoerg template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
126*e038c9c4Sjoerg constexpr StringRef NodeID<Expr>::value;
127*e038c9c4Sjoerg constexpr StringRef NodeID<Decl>::value;
1287330f729Sjoerg
1297330f729Sjoerg template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,ExprMutationAnalyzer * Analyzer,F Finder)1307330f729Sjoerg const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
1317330f729Sjoerg ExprMutationAnalyzer *Analyzer, F Finder) {
1327330f729Sjoerg const StringRef ID = NodeID<T>::value;
1337330f729Sjoerg for (const auto &Nodes : Matches) {
1347330f729Sjoerg if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
1357330f729Sjoerg return S;
1367330f729Sjoerg }
1377330f729Sjoerg return nullptr;
1387330f729Sjoerg }
1397330f729Sjoerg
1407330f729Sjoerg } // namespace
1417330f729Sjoerg
findMutation(const Expr * Exp)1427330f729Sjoerg const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
1437330f729Sjoerg return findMutationMemoized(Exp,
1447330f729Sjoerg {&ExprMutationAnalyzer::findDirectMutation,
1457330f729Sjoerg &ExprMutationAnalyzer::findMemberMutation,
1467330f729Sjoerg &ExprMutationAnalyzer::findArrayElementMutation,
1477330f729Sjoerg &ExprMutationAnalyzer::findCastMutation,
1487330f729Sjoerg &ExprMutationAnalyzer::findRangeLoopMutation,
1497330f729Sjoerg &ExprMutationAnalyzer::findReferenceMutation,
1507330f729Sjoerg &ExprMutationAnalyzer::findFunctionArgMutation},
1517330f729Sjoerg Results);
1527330f729Sjoerg }
1537330f729Sjoerg
findMutation(const Decl * Dec)1547330f729Sjoerg const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
1557330f729Sjoerg return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
1567330f729Sjoerg }
1577330f729Sjoerg
findPointeeMutation(const Expr * Exp)1587330f729Sjoerg const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
1597330f729Sjoerg return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
1607330f729Sjoerg }
1617330f729Sjoerg
findPointeeMutation(const Decl * Dec)1627330f729Sjoerg const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
1637330f729Sjoerg return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
1647330f729Sjoerg }
1657330f729Sjoerg
findMutationMemoized(const Expr * Exp,llvm::ArrayRef<MutationFinder> Finders,ResultMap & MemoizedResults)1667330f729Sjoerg const Stmt *ExprMutationAnalyzer::findMutationMemoized(
1677330f729Sjoerg const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
1687330f729Sjoerg ResultMap &MemoizedResults) {
1697330f729Sjoerg const auto Memoized = MemoizedResults.find(Exp);
1707330f729Sjoerg if (Memoized != MemoizedResults.end())
1717330f729Sjoerg return Memoized->second;
1727330f729Sjoerg
1737330f729Sjoerg if (isUnevaluated(Exp))
1747330f729Sjoerg return MemoizedResults[Exp] = nullptr;
1757330f729Sjoerg
1767330f729Sjoerg for (const auto &Finder : Finders) {
1777330f729Sjoerg if (const Stmt *S = (this->*Finder)(Exp))
1787330f729Sjoerg return MemoizedResults[Exp] = S;
1797330f729Sjoerg }
1807330f729Sjoerg
1817330f729Sjoerg return MemoizedResults[Exp] = nullptr;
1827330f729Sjoerg }
1837330f729Sjoerg
tryEachDeclRef(const Decl * Dec,MutationFinder Finder)1847330f729Sjoerg const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
1857330f729Sjoerg MutationFinder Finder) {
1867330f729Sjoerg const auto Refs =
1877330f729Sjoerg match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
1887330f729Sjoerg Stm, Context);
1897330f729Sjoerg for (const auto &RefNodes : Refs) {
1907330f729Sjoerg const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
1917330f729Sjoerg if ((this->*Finder)(E))
1927330f729Sjoerg return E;
1937330f729Sjoerg }
1947330f729Sjoerg return nullptr;
1957330f729Sjoerg }
1967330f729Sjoerg
isUnevaluated(const Expr * Exp)1977330f729Sjoerg bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
1987330f729Sjoerg return selectFirst<Expr>(
1997330f729Sjoerg NodeID<Expr>::value,
2007330f729Sjoerg match(
2017330f729Sjoerg findAll(
202*e038c9c4Sjoerg expr(canResolveToExpr(equalsNode(Exp)),
2037330f729Sjoerg anyOf(
2047330f729Sjoerg // `Exp` is part of the underlying expression of
2057330f729Sjoerg // decltype/typeof if it has an ancestor of
2067330f729Sjoerg // typeLoc.
2077330f729Sjoerg hasAncestor(typeLoc(unless(
2087330f729Sjoerg hasAncestor(unaryExprOrTypeTraitExpr())))),
2097330f729Sjoerg hasAncestor(expr(anyOf(
2107330f729Sjoerg // `UnaryExprOrTypeTraitExpr` is unevaluated
2117330f729Sjoerg // unless it's sizeof on VLA.
2127330f729Sjoerg unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
2137330f729Sjoerg hasArgumentOfType(variableArrayType())))),
2147330f729Sjoerg // `CXXTypeidExpr` is unevaluated unless it's
2157330f729Sjoerg // applied to an expression of glvalue of
2167330f729Sjoerg // polymorphic class type.
2177330f729Sjoerg cxxTypeidExpr(
2187330f729Sjoerg unless(isPotentiallyEvaluated())),
2197330f729Sjoerg // The controlling expression of
2207330f729Sjoerg // `GenericSelectionExpr` is unevaluated.
2217330f729Sjoerg genericSelectionExpr(hasControllingExpr(
2227330f729Sjoerg hasDescendant(equalsNode(Exp)))),
2237330f729Sjoerg cxxNoexceptExpr())))))
2247330f729Sjoerg .bind(NodeID<Expr>::value)),
2257330f729Sjoerg Stm, Context)) != nullptr;
2267330f729Sjoerg }
2277330f729Sjoerg
2287330f729Sjoerg const Stmt *
findExprMutation(ArrayRef<BoundNodes> Matches)2297330f729Sjoerg ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
2307330f729Sjoerg return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
2317330f729Sjoerg }
2327330f729Sjoerg
2337330f729Sjoerg const Stmt *
findDeclMutation(ArrayRef<BoundNodes> Matches)2347330f729Sjoerg ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
2357330f729Sjoerg return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
2367330f729Sjoerg }
2377330f729Sjoerg
findExprPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches)2387330f729Sjoerg const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
2397330f729Sjoerg ArrayRef<ast_matchers::BoundNodes> Matches) {
2407330f729Sjoerg return tryEachMatch<Expr>(Matches, this,
2417330f729Sjoerg &ExprMutationAnalyzer::findPointeeMutation);
2427330f729Sjoerg }
2437330f729Sjoerg
findDeclPointeeMutation(ArrayRef<ast_matchers::BoundNodes> Matches)2447330f729Sjoerg const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
2457330f729Sjoerg ArrayRef<ast_matchers::BoundNodes> Matches) {
2467330f729Sjoerg return tryEachMatch<Decl>(Matches, this,
2477330f729Sjoerg &ExprMutationAnalyzer::findPointeeMutation);
2487330f729Sjoerg }
2497330f729Sjoerg
findDirectMutation(const Expr * Exp)2507330f729Sjoerg const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
2517330f729Sjoerg // LHS of any assignment operators.
252*e038c9c4Sjoerg const auto AsAssignmentLhs = binaryOperator(
253*e038c9c4Sjoerg isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp))));
2547330f729Sjoerg
2557330f729Sjoerg // Operand of increment/decrement operators.
2567330f729Sjoerg const auto AsIncDecOperand =
2577330f729Sjoerg unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
258*e038c9c4Sjoerg hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
2597330f729Sjoerg
2607330f729Sjoerg // Invoking non-const member function.
2617330f729Sjoerg // A member function is assumed to be non-const when it is unresolved.
2627330f729Sjoerg const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
263*e038c9c4Sjoerg
264*e038c9c4Sjoerg const auto AsNonConstThis = expr(anyOf(
265*e038c9c4Sjoerg cxxMemberCallExpr(callee(NonConstMethod),
266*e038c9c4Sjoerg on(canResolveToExpr(equalsNode(Exp)))),
2677330f729Sjoerg cxxOperatorCallExpr(callee(NonConstMethod),
268*e038c9c4Sjoerg hasArgument(0, canResolveToExpr(equalsNode(Exp)))),
269*e038c9c4Sjoerg // In case of a templated type, calling overloaded operators is not
270*e038c9c4Sjoerg // resolved and modelled as `binaryOperator` on a dependent type.
271*e038c9c4Sjoerg // Such instances are considered a modification, because they can modify
272*e038c9c4Sjoerg // in different instantiations of the template.
273*e038c9c4Sjoerg binaryOperator(hasEitherOperand(
274*e038c9c4Sjoerg allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))),
275*e038c9c4Sjoerg isTypeDependent()))),
276*e038c9c4Sjoerg // Within class templates and member functions the member expression might
277*e038c9c4Sjoerg // not be resolved. In that case, the `callExpr` is considered to be a
278*e038c9c4Sjoerg // modification.
279*e038c9c4Sjoerg callExpr(
280*e038c9c4Sjoerg callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression(
281*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp)))),
282*e038c9c4Sjoerg cxxDependentScopeMemberExpr(hasObjectExpression(
283*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp)))))))),
284*e038c9c4Sjoerg // Match on a call to a known method, but the call itself is type
285*e038c9c4Sjoerg // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
286*e038c9c4Sjoerg callExpr(allOf(isTypeDependent(),
287*e038c9c4Sjoerg callee(memberExpr(hasDeclaration(NonConstMethod),
288*e038c9c4Sjoerg hasObjectExpression(canResolveToExpr(
289*e038c9c4Sjoerg equalsNode(Exp)))))))));
2907330f729Sjoerg
2917330f729Sjoerg // Taking address of 'Exp'.
2927330f729Sjoerg // We're assuming 'Exp' is mutated as soon as its address is taken, though in
2937330f729Sjoerg // theory we can follow the pointer and see whether it escaped `Stm` or is
2947330f729Sjoerg // dereferenced and then mutated. This is left for future improvements.
2957330f729Sjoerg const auto AsAmpersandOperand =
2967330f729Sjoerg unaryOperator(hasOperatorName("&"),
2977330f729Sjoerg // A NoOp implicit cast is adding const.
2987330f729Sjoerg unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
299*e038c9c4Sjoerg hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
3007330f729Sjoerg const auto AsPointerFromArrayDecay =
3017330f729Sjoerg castExpr(hasCastKind(CK_ArrayToPointerDecay),
3027330f729Sjoerg unless(hasParent(arraySubscriptExpr())),
303*e038c9c4Sjoerg has(canResolveToExpr(equalsNode(Exp))));
3047330f729Sjoerg // Treat calling `operator->()` of move-only classes as taking address.
3057330f729Sjoerg // These are typically smart pointers with unique ownership so we treat
3067330f729Sjoerg // mutation of pointee as mutation of the smart pointer itself.
307*e038c9c4Sjoerg const auto AsOperatorArrowThis = cxxOperatorCallExpr(
308*e038c9c4Sjoerg hasOverloadedOperatorName("->"),
309*e038c9c4Sjoerg callee(
310*e038c9c4Sjoerg cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
311*e038c9c4Sjoerg argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp))));
3127330f729Sjoerg
3137330f729Sjoerg // Used as non-const-ref argument when calling a function.
3147330f729Sjoerg // An argument is assumed to be non-const-ref when the function is unresolved.
3157330f729Sjoerg // Instantiated template functions are not handled here but in
3167330f729Sjoerg // findFunctionArgMutation which has additional smarts for handling forwarding
3177330f729Sjoerg // references.
318*e038c9c4Sjoerg const auto NonConstRefParam = forEachArgumentWithParamType(
319*e038c9c4Sjoerg anyOf(canResolveToExpr(equalsNode(Exp)),
320*e038c9c4Sjoerg memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))),
321*e038c9c4Sjoerg nonConstReferenceType());
3227330f729Sjoerg const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
323*e038c9c4Sjoerg const auto TypeDependentCallee =
324*e038c9c4Sjoerg callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
325*e038c9c4Sjoerg cxxDependentScopeMemberExpr(),
326*e038c9c4Sjoerg hasType(templateTypeParmType()), isTypeDependent())));
327*e038c9c4Sjoerg
3287330f729Sjoerg const auto AsNonConstRefArg = anyOf(
3297330f729Sjoerg callExpr(NonConstRefParam, NotInstantiated),
3307330f729Sjoerg cxxConstructExpr(NonConstRefParam, NotInstantiated),
331*e038c9c4Sjoerg callExpr(TypeDependentCallee,
332*e038c9c4Sjoerg hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
333*e038c9c4Sjoerg cxxUnresolvedConstructExpr(
334*e038c9c4Sjoerg hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
335*e038c9c4Sjoerg // Previous False Positive in the following Code:
336*e038c9c4Sjoerg // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
337*e038c9c4Sjoerg // Where the constructor of `Type` takes its argument as reference.
338*e038c9c4Sjoerg // The AST does not resolve in a `cxxConstructExpr` because it is
339*e038c9c4Sjoerg // type-dependent.
340*e038c9c4Sjoerg parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))),
341*e038c9c4Sjoerg // If the initializer is for a reference type, there is no cast for
342*e038c9c4Sjoerg // the variable. Values are cast to RValue first.
343*e038c9c4Sjoerg initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp))))));
3447330f729Sjoerg
3457330f729Sjoerg // Captured by a lambda by reference.
3467330f729Sjoerg // If we're initializing a capture with 'Exp' directly then we're initializing
3477330f729Sjoerg // a reference capture.
3487330f729Sjoerg // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
3497330f729Sjoerg const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
3507330f729Sjoerg
3517330f729Sjoerg // Returned as non-const-ref.
3527330f729Sjoerg // If we're returning 'Exp' directly then it's returned as non-const-ref.
3537330f729Sjoerg // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
3547330f729Sjoerg // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
3557330f729Sjoerg // adding const.)
356*e038c9c4Sjoerg const auto AsNonConstRefReturn =
357*e038c9c4Sjoerg returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp))));
3587330f729Sjoerg
359*e038c9c4Sjoerg // It is used as a non-const-reference for initalizing a range-for loop.
360*e038c9c4Sjoerg const auto AsNonConstRefRangeInit = cxxForRangeStmt(
361*e038c9c4Sjoerg hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)),
362*e038c9c4Sjoerg hasType(nonConstReferenceType())))));
363*e038c9c4Sjoerg
364*e038c9c4Sjoerg const auto Matches = match(
365*e038c9c4Sjoerg traverse(TK_AsIs,
366*e038c9c4Sjoerg findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand,
367*e038c9c4Sjoerg AsNonConstThis, AsAmpersandOperand,
368*e038c9c4Sjoerg AsPointerFromArrayDecay, AsOperatorArrowThis,
369*e038c9c4Sjoerg AsNonConstRefArg, AsLambdaRefCaptureInit,
370*e038c9c4Sjoerg AsNonConstRefReturn, AsNonConstRefRangeInit))
371*e038c9c4Sjoerg .bind("stmt"))),
3727330f729Sjoerg Stm, Context);
3737330f729Sjoerg return selectFirst<Stmt>("stmt", Matches);
3747330f729Sjoerg }
3757330f729Sjoerg
findMemberMutation(const Expr * Exp)3767330f729Sjoerg const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
3777330f729Sjoerg // Check whether any member of 'Exp' is mutated.
3787330f729Sjoerg const auto MemberExprs =
379*e038c9c4Sjoerg match(findAll(expr(anyOf(memberExpr(hasObjectExpression(
380*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp)))),
381*e038c9c4Sjoerg cxxDependentScopeMemberExpr(hasObjectExpression(
382*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp))))))
3837330f729Sjoerg .bind(NodeID<Expr>::value)),
3847330f729Sjoerg Stm, Context);
3857330f729Sjoerg return findExprMutation(MemberExprs);
3867330f729Sjoerg }
3877330f729Sjoerg
findArrayElementMutation(const Expr * Exp)3887330f729Sjoerg const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
3897330f729Sjoerg // Check whether any element of an array is mutated.
390*e038c9c4Sjoerg const auto SubscriptExprs =
391*e038c9c4Sjoerg match(findAll(arraySubscriptExpr(
392*e038c9c4Sjoerg anyOf(hasBase(canResolveToExpr(equalsNode(Exp))),
393*e038c9c4Sjoerg hasBase(implicitCastExpr(
394*e038c9c4Sjoerg allOf(hasCastKind(CK_ArrayToPointerDecay),
395*e038c9c4Sjoerg hasSourceExpression(canResolveToExpr(
396*e038c9c4Sjoerg equalsNode(Exp))))))))
3977330f729Sjoerg .bind(NodeID<Expr>::value)),
3987330f729Sjoerg Stm, Context);
3997330f729Sjoerg return findExprMutation(SubscriptExprs);
4007330f729Sjoerg }
4017330f729Sjoerg
findCastMutation(const Expr * Exp)4027330f729Sjoerg const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
403*e038c9c4Sjoerg // If the 'Exp' is explicitly casted to a non-const reference type the
404*e038c9c4Sjoerg // 'Exp' is considered to be modified.
405*e038c9c4Sjoerg const auto ExplicitCast = match(
406*e038c9c4Sjoerg findAll(
407*e038c9c4Sjoerg stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
408*e038c9c4Sjoerg explicitCastExpr(
409*e038c9c4Sjoerg hasDestinationType(nonConstReferenceType()))))
410*e038c9c4Sjoerg .bind("stmt")),
411*e038c9c4Sjoerg Stm, Context);
412*e038c9c4Sjoerg
413*e038c9c4Sjoerg if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
414*e038c9c4Sjoerg return CastStmt;
415*e038c9c4Sjoerg
4167330f729Sjoerg // If 'Exp' is casted to any non-const reference type, check the castExpr.
417*e038c9c4Sjoerg const auto Casts = match(
418*e038c9c4Sjoerg findAll(
419*e038c9c4Sjoerg expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
420*e038c9c4Sjoerg anyOf(explicitCastExpr(
421*e038c9c4Sjoerg hasDestinationType(nonConstReferenceType())),
4227330f729Sjoerg implicitCastExpr(hasImplicitDestinationType(
423*e038c9c4Sjoerg nonConstReferenceType())))))
4247330f729Sjoerg .bind(NodeID<Expr>::value)),
4257330f729Sjoerg Stm, Context);
426*e038c9c4Sjoerg
4277330f729Sjoerg if (const Stmt *S = findExprMutation(Casts))
4287330f729Sjoerg return S;
4297330f729Sjoerg // Treat std::{move,forward} as cast.
4307330f729Sjoerg const auto Calls =
4317330f729Sjoerg match(findAll(callExpr(callee(namedDecl(
4327330f729Sjoerg hasAnyName("::std::move", "::std::forward"))),
433*e038c9c4Sjoerg hasArgument(0, canResolveToExpr(equalsNode(Exp))))
4347330f729Sjoerg .bind("expr")),
4357330f729Sjoerg Stm, Context);
4367330f729Sjoerg return findExprMutation(Calls);
4377330f729Sjoerg }
4387330f729Sjoerg
findRangeLoopMutation(const Expr * Exp)4397330f729Sjoerg const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
440*e038c9c4Sjoerg // Keep the ordering for the specific initialization matches to happen first,
441*e038c9c4Sjoerg // because it is cheaper to match all potential modifications of the loop
442*e038c9c4Sjoerg // variable.
443*e038c9c4Sjoerg
444*e038c9c4Sjoerg // The range variable is a reference to a builtin array. In that case the
445*e038c9c4Sjoerg // array is considered modified if the loop-variable is a non-const reference.
446*e038c9c4Sjoerg const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
447*e038c9c4Sjoerg hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
448*e038c9c4Sjoerg const auto RefToArrayRefToElements = match(
449*e038c9c4Sjoerg findAll(stmt(cxxForRangeStmt(
450*e038c9c4Sjoerg hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
451*e038c9c4Sjoerg .bind(NodeID<Decl>::value)),
452*e038c9c4Sjoerg hasRangeStmt(DeclStmtToNonRefToArray),
453*e038c9c4Sjoerg hasRangeInit(canResolveToExpr(equalsNode(Exp)))))
454*e038c9c4Sjoerg .bind("stmt")),
455*e038c9c4Sjoerg Stm, Context);
456*e038c9c4Sjoerg
457*e038c9c4Sjoerg if (const auto *BadRangeInitFromArray =
458*e038c9c4Sjoerg selectFirst<Stmt>("stmt", RefToArrayRefToElements))
459*e038c9c4Sjoerg return BadRangeInitFromArray;
460*e038c9c4Sjoerg
461*e038c9c4Sjoerg // Small helper to match special cases in range-for loops.
462*e038c9c4Sjoerg //
463*e038c9c4Sjoerg // It is possible that containers do not provide a const-overload for their
464*e038c9c4Sjoerg // iterator accessors. If this is the case, the variable is used non-const
465*e038c9c4Sjoerg // no matter what happens in the loop. This requires special detection as it
466*e038c9c4Sjoerg // is then faster to find all mutations of the loop variable.
467*e038c9c4Sjoerg // It aims at a different modification as well.
468*e038c9c4Sjoerg const auto HasAnyNonConstIterator =
469*e038c9c4Sjoerg anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
470*e038c9c4Sjoerg unless(hasMethod(allOf(hasName("begin"), isConst())))),
471*e038c9c4Sjoerg allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
472*e038c9c4Sjoerg unless(hasMethod(allOf(hasName("end"), isConst())))));
473*e038c9c4Sjoerg
474*e038c9c4Sjoerg const auto DeclStmtToNonConstIteratorContainer = declStmt(
475*e038c9c4Sjoerg hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
476*e038c9c4Sjoerg pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
477*e038c9c4Sjoerg
478*e038c9c4Sjoerg const auto RefToContainerBadIterators =
479*e038c9c4Sjoerg match(findAll(stmt(cxxForRangeStmt(allOf(
480*e038c9c4Sjoerg hasRangeStmt(DeclStmtToNonConstIteratorContainer),
481*e038c9c4Sjoerg hasRangeInit(canResolveToExpr(equalsNode(Exp))))))
482*e038c9c4Sjoerg .bind("stmt")),
483*e038c9c4Sjoerg Stm, Context);
484*e038c9c4Sjoerg
485*e038c9c4Sjoerg if (const auto *BadIteratorsContainer =
486*e038c9c4Sjoerg selectFirst<Stmt>("stmt", RefToContainerBadIterators))
487*e038c9c4Sjoerg return BadIteratorsContainer;
488*e038c9c4Sjoerg
4897330f729Sjoerg // If range for looping over 'Exp' with a non-const reference loop variable,
4907330f729Sjoerg // check all declRefExpr of the loop variable.
4917330f729Sjoerg const auto LoopVars =
4927330f729Sjoerg match(findAll(cxxForRangeStmt(
4937330f729Sjoerg hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
4947330f729Sjoerg .bind(NodeID<Decl>::value)),
495*e038c9c4Sjoerg hasRangeInit(canResolveToExpr(equalsNode(Exp))))),
4967330f729Sjoerg Stm, Context);
4977330f729Sjoerg return findDeclMutation(LoopVars);
4987330f729Sjoerg }
4997330f729Sjoerg
findReferenceMutation(const Expr * Exp)5007330f729Sjoerg const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
5017330f729Sjoerg // Follow non-const reference returned by `operator*()` of move-only classes.
5027330f729Sjoerg // These are typically smart pointers with unique ownership so we treat
5037330f729Sjoerg // mutation of pointee as mutation of the smart pointer itself.
5047330f729Sjoerg const auto Ref =
5057330f729Sjoerg match(findAll(cxxOperatorCallExpr(
5067330f729Sjoerg hasOverloadedOperatorName("*"),
5077330f729Sjoerg callee(cxxMethodDecl(ofClass(isMoveOnly()),
5087330f729Sjoerg returns(nonConstReferenceType()))),
509*e038c9c4Sjoerg argumentCountIs(1),
510*e038c9c4Sjoerg hasArgument(0, canResolveToExpr(equalsNode(Exp))))
5117330f729Sjoerg .bind(NodeID<Expr>::value)),
5127330f729Sjoerg Stm, Context);
5137330f729Sjoerg if (const Stmt *S = findExprMutation(Ref))
5147330f729Sjoerg return S;
5157330f729Sjoerg
5167330f729Sjoerg // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
5177330f729Sjoerg const auto Refs = match(
5187330f729Sjoerg stmt(forEachDescendant(
5197330f729Sjoerg varDecl(
5207330f729Sjoerg hasType(nonConstReferenceType()),
521*e038c9c4Sjoerg hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)),
522*e038c9c4Sjoerg memberExpr(hasObjectExpression(
523*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp)))))),
5247330f729Sjoerg hasParent(declStmt().bind("stmt")),
525*e038c9c4Sjoerg // Don't follow the reference in range statement, we've
526*e038c9c4Sjoerg // handled that separately.
5277330f729Sjoerg unless(hasParent(declStmt(hasParent(
5287330f729Sjoerg cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
5297330f729Sjoerg .bind(NodeID<Decl>::value))),
5307330f729Sjoerg Stm, Context);
5317330f729Sjoerg return findDeclMutation(Refs);
5327330f729Sjoerg }
5337330f729Sjoerg
findFunctionArgMutation(const Expr * Exp)5347330f729Sjoerg const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
5357330f729Sjoerg const auto NonConstRefParam = forEachArgumentWithParam(
536*e038c9c4Sjoerg canResolveToExpr(equalsNode(Exp)),
5377330f729Sjoerg parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
5387330f729Sjoerg const auto IsInstantiated = hasDeclaration(isInstantiated());
5397330f729Sjoerg const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
5407330f729Sjoerg const auto Matches = match(
541*e038c9c4Sjoerg traverse(
542*e038c9c4Sjoerg TK_AsIs,
543*e038c9c4Sjoerg findAll(
544*e038c9c4Sjoerg expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
5457330f729Sjoerg unless(callee(namedDecl(hasAnyName(
5467330f729Sjoerg "::std::move", "::std::forward"))))),
5477330f729Sjoerg cxxConstructExpr(NonConstRefParam, IsInstantiated,
5487330f729Sjoerg FuncDecl)))
549*e038c9c4Sjoerg .bind(NodeID<Expr>::value))),
5507330f729Sjoerg Stm, Context);
5517330f729Sjoerg for (const auto &Nodes : Matches) {
5527330f729Sjoerg const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
5537330f729Sjoerg const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
5547330f729Sjoerg if (!Func->getBody() || !Func->getPrimaryTemplate())
5557330f729Sjoerg return Exp;
5567330f729Sjoerg
5577330f729Sjoerg const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
5587330f729Sjoerg const ArrayRef<ParmVarDecl *> AllParams =
5597330f729Sjoerg Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
5607330f729Sjoerg QualType ParmType =
5617330f729Sjoerg AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
5627330f729Sjoerg AllParams.size() - 1)]
5637330f729Sjoerg ->getType();
5647330f729Sjoerg if (const auto *T = ParmType->getAs<PackExpansionType>())
5657330f729Sjoerg ParmType = T->getPattern();
5667330f729Sjoerg
5677330f729Sjoerg // If param type is forwarding reference, follow into the function
5687330f729Sjoerg // definition and see whether the param is mutated inside.
5697330f729Sjoerg if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
5707330f729Sjoerg if (!RefType->getPointeeType().getQualifiers() &&
5717330f729Sjoerg RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
5727330f729Sjoerg std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
5737330f729Sjoerg FuncParmAnalyzer[Func];
5747330f729Sjoerg if (!Analyzer)
5757330f729Sjoerg Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
5767330f729Sjoerg if (Analyzer->findMutation(Parm))
5777330f729Sjoerg return Exp;
5787330f729Sjoerg continue;
5797330f729Sjoerg }
5807330f729Sjoerg }
5817330f729Sjoerg // Not forwarding reference.
5827330f729Sjoerg return Exp;
5837330f729Sjoerg }
5847330f729Sjoerg return nullptr;
5857330f729Sjoerg }
5867330f729Sjoerg
FunctionParmMutationAnalyzer(const FunctionDecl & Func,ASTContext & Context)5877330f729Sjoerg FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
5887330f729Sjoerg const FunctionDecl &Func, ASTContext &Context)
5897330f729Sjoerg : BodyAnalyzer(*Func.getBody(), Context) {
5907330f729Sjoerg if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
5917330f729Sjoerg // CXXCtorInitializer might also mutate Param but they're not part of
5927330f729Sjoerg // function body, check them eagerly here since they're typically trivial.
5937330f729Sjoerg for (const CXXCtorInitializer *Init : Ctor->inits()) {
5947330f729Sjoerg ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
5957330f729Sjoerg for (const ParmVarDecl *Parm : Ctor->parameters()) {
5967330f729Sjoerg if (Results.find(Parm) != Results.end())
5977330f729Sjoerg continue;
5987330f729Sjoerg if (const Stmt *S = InitAnalyzer.findMutation(Parm))
5997330f729Sjoerg Results[Parm] = S;
6007330f729Sjoerg }
6017330f729Sjoerg }
6027330f729Sjoerg }
6037330f729Sjoerg }
6047330f729Sjoerg
6057330f729Sjoerg const Stmt *
findMutation(const ParmVarDecl * Parm)6067330f729Sjoerg FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
6077330f729Sjoerg const auto Memoized = Results.find(Parm);
6087330f729Sjoerg if (Memoized != Results.end())
6097330f729Sjoerg return Memoized->second;
6107330f729Sjoerg
6117330f729Sjoerg if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
6127330f729Sjoerg return Results[Parm] = S;
6137330f729Sjoerg
6147330f729Sjoerg return Results[Parm] = nullptr;
6157330f729Sjoerg }
6167330f729Sjoerg
6177330f729Sjoerg } // namespace clang
618