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