123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- //===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
- #include "clang/AST/Expr.h"
- #include "clang/AST/OperationKinds.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/ASTMatchers/ASTMatchers.h"
- #include "llvm/ADT/STLExtras.h"
- namespace clang {
- using namespace ast_matchers;
- namespace {
- AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
- return llvm::is_contained(Node.capture_inits(), E);
- }
- AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
- ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
- const DeclStmt *const Range = Node.getRangeStmt();
- return InnerMatcher.matches(*Range, Finder, Builder);
- }
- AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>,
- InnerMatcher) {
- const Expr *Result = &Node;
- while (const auto *BOComma =
- dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
- if (!BOComma->isCommaOp())
- break;
- Result = BOComma->getRHS();
- }
- return InnerMatcher.matches(*Result, Finder, Builder);
- }
- AST_MATCHER_P(Expr, canResolveToExpr, ast_matchers::internal::Matcher<Expr>,
- InnerMatcher) {
- auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) {
- return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase),
- hasCastKind(CK_UncheckedDerivedToBase)),
- hasSourceExpression(Inner));
- };
- auto IgnoreDerivedToBase =
- [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) {
- return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner))));
- };
- // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`.
- // This matching must be recursive because `<expr>` can be anything resolving
- // to the `InnerMatcher`, for example another conditional operator.
- // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;`
- // is handled, too. The implicit cast happens outside of the conditional.
- // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))`
- // below.
- auto const ConditionalOperator = conditionalOperator(anyOf(
- hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
- hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
- auto const ElvisOperator = binaryConditionalOperator(anyOf(
- hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))),
- hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher)))));
- auto const ComplexMatcher = ignoringParens(
- expr(anyOf(IgnoreDerivedToBase(InnerMatcher),
- maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)),
- IgnoreDerivedToBase(ConditionalOperator),
- IgnoreDerivedToBase(ElvisOperator))));
- return ComplexMatcher.matches(Node, Finder, Builder);
- }
- // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
- // not have the 'arguments()' method.
- AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
- InnerMatcher) {
- for (const Expr *Arg : Node.inits()) {
- ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
- if (InnerMatcher.matches(*Arg, Finder, &Result)) {
- *Builder = std::move(Result);
- return true;
- }
- }
- return false;
- }
- const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
- cxxTypeidExpr;
- AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
- return Node.isPotentiallyEvaluated();
- }
- AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
- ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
- return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
- }
- const auto nonConstReferenceType = [] {
- return hasUnqualifiedDesugaredType(
- referenceType(pointee(unless(isConstQualified()))));
- };
- const auto nonConstPointerType = [] {
- return hasUnqualifiedDesugaredType(
- pointerType(pointee(unless(isConstQualified()))));
- };
- const auto isMoveOnly = [] {
- return cxxRecordDecl(
- hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
- hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
- unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
- unless(isDeleted()))),
- hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
- unless(isDeleted()))))));
- };
- template <class T> struct NodeID;
- template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; };
- template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; };
- constexpr StringRef NodeID<Expr>::value;
- constexpr StringRef NodeID<Decl>::value;
- template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
- const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
- ExprMutationAnalyzer *Analyzer, F Finder) {
- const StringRef ID = NodeID<T>::value;
- for (const auto &Nodes : Matches) {
- if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
- return S;
- }
- return nullptr;
- }
- } // namespace
- const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
- return findMutationMemoized(Exp,
- {&ExprMutationAnalyzer::findDirectMutation,
- &ExprMutationAnalyzer::findMemberMutation,
- &ExprMutationAnalyzer::findArrayElementMutation,
- &ExprMutationAnalyzer::findCastMutation,
- &ExprMutationAnalyzer::findRangeLoopMutation,
- &ExprMutationAnalyzer::findReferenceMutation,
- &ExprMutationAnalyzer::findFunctionArgMutation},
- Results);
- }
- const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
- return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
- }
- const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
- return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
- }
- const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
- return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
- }
- const Stmt *ExprMutationAnalyzer::findMutationMemoized(
- const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
- ResultMap &MemoizedResults) {
- const auto Memoized = MemoizedResults.find(Exp);
- if (Memoized != MemoizedResults.end())
- return Memoized->second;
- if (isUnevaluated(Exp))
- return MemoizedResults[Exp] = nullptr;
- for (const auto &Finder : Finders) {
- if (const Stmt *S = (this->*Finder)(Exp))
- return MemoizedResults[Exp] = S;
- }
- return MemoizedResults[Exp] = nullptr;
- }
- const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
- MutationFinder Finder) {
- const auto Refs =
- match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
- Stm, Context);
- for (const auto &RefNodes : Refs) {
- const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
- if ((this->*Finder)(E))
- return E;
- }
- return nullptr;
- }
- bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
- return selectFirst<Expr>(
- NodeID<Expr>::value,
- match(
- findAll(
- expr(canResolveToExpr(equalsNode(Exp)),
- anyOf(
- // `Exp` is part of the underlying expression of
- // decltype/typeof if it has an ancestor of
- // typeLoc.
- hasAncestor(typeLoc(unless(
- hasAncestor(unaryExprOrTypeTraitExpr())))),
- hasAncestor(expr(anyOf(
- // `UnaryExprOrTypeTraitExpr` is unevaluated
- // unless it's sizeof on VLA.
- unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
- hasArgumentOfType(variableArrayType())))),
- // `CXXTypeidExpr` is unevaluated unless it's
- // applied to an expression of glvalue of
- // polymorphic class type.
- cxxTypeidExpr(
- unless(isPotentiallyEvaluated())),
- // The controlling expression of
- // `GenericSelectionExpr` is unevaluated.
- genericSelectionExpr(hasControllingExpr(
- hasDescendant(equalsNode(Exp)))),
- cxxNoexceptExpr())))))
- .bind(NodeID<Expr>::value)),
- Stm, Context)) != nullptr;
- }
- const Stmt *
- ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
- return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
- }
- const Stmt *
- ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
- return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
- }
- const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
- ArrayRef<ast_matchers::BoundNodes> Matches) {
- return tryEachMatch<Expr>(Matches, this,
- &ExprMutationAnalyzer::findPointeeMutation);
- }
- const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
- ArrayRef<ast_matchers::BoundNodes> Matches) {
- return tryEachMatch<Decl>(Matches, this,
- &ExprMutationAnalyzer::findPointeeMutation);
- }
- const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
- // LHS of any assignment operators.
- const auto AsAssignmentLhs = binaryOperator(
- isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp))));
- // Operand of increment/decrement operators.
- const auto AsIncDecOperand =
- unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
- hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
- // Invoking non-const member function.
- // A member function is assumed to be non-const when it is unresolved.
- const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
- const auto AsNonConstThis = expr(anyOf(
- cxxMemberCallExpr(callee(NonConstMethod),
- on(canResolveToExpr(equalsNode(Exp)))),
- cxxOperatorCallExpr(callee(NonConstMethod),
- hasArgument(0, canResolveToExpr(equalsNode(Exp)))),
- // In case of a templated type, calling overloaded operators is not
- // resolved and modelled as `binaryOperator` on a dependent type.
- // Such instances are considered a modification, because they can modify
- // in different instantiations of the template.
- binaryOperator(hasEitherOperand(
- allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))),
- isTypeDependent()))),
- // Within class templates and member functions the member expression might
- // not be resolved. In that case, the `callExpr` is considered to be a
- // modification.
- callExpr(
- callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression(
- canResolveToExpr(equalsNode(Exp)))),
- cxxDependentScopeMemberExpr(hasObjectExpression(
- canResolveToExpr(equalsNode(Exp)))))))),
- // Match on a call to a known method, but the call itself is type
- // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function).
- callExpr(allOf(isTypeDependent(),
- callee(memberExpr(hasDeclaration(NonConstMethod),
- hasObjectExpression(canResolveToExpr(
- equalsNode(Exp)))))))));
- // Taking address of 'Exp'.
- // We're assuming 'Exp' is mutated as soon as its address is taken, though in
- // theory we can follow the pointer and see whether it escaped `Stm` or is
- // dereferenced and then mutated. This is left for future improvements.
- const auto AsAmpersandOperand =
- unaryOperator(hasOperatorName("&"),
- // A NoOp implicit cast is adding const.
- unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
- hasUnaryOperand(canResolveToExpr(equalsNode(Exp))));
- const auto AsPointerFromArrayDecay =
- castExpr(hasCastKind(CK_ArrayToPointerDecay),
- unless(hasParent(arraySubscriptExpr())),
- has(canResolveToExpr(equalsNode(Exp))));
- // Treat calling `operator->()` of move-only classes as taking address.
- // These are typically smart pointers with unique ownership so we treat
- // mutation of pointee as mutation of the smart pointer itself.
- const auto AsOperatorArrowThis = cxxOperatorCallExpr(
- hasOverloadedOperatorName("->"),
- callee(
- cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))),
- argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp))));
- // Used as non-const-ref argument when calling a function.
- // An argument is assumed to be non-const-ref when the function is unresolved.
- // Instantiated template functions are not handled here but in
- // findFunctionArgMutation which has additional smarts for handling forwarding
- // references.
- const auto NonConstRefParam = forEachArgumentWithParamType(
- anyOf(canResolveToExpr(equalsNode(Exp)),
- memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))),
- nonConstReferenceType());
- const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
- const auto TypeDependentCallee =
- callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
- cxxDependentScopeMemberExpr(),
- hasType(templateTypeParmType()), isTypeDependent())));
- const auto AsNonConstRefArg = anyOf(
- callExpr(NonConstRefParam, NotInstantiated),
- cxxConstructExpr(NonConstRefParam, NotInstantiated),
- callExpr(TypeDependentCallee,
- hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
- cxxUnresolvedConstructExpr(
- hasAnyArgument(canResolveToExpr(equalsNode(Exp)))),
- // Previous False Positive in the following Code:
- // `template <typename T> void f() { int i = 42; new Type<T>(i); }`
- // Where the constructor of `Type` takes its argument as reference.
- // The AST does not resolve in a `cxxConstructExpr` because it is
- // type-dependent.
- parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))),
- // If the initializer is for a reference type, there is no cast for
- // the variable. Values are cast to RValue first.
- initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp))))));
- // Captured by a lambda by reference.
- // If we're initializing a capture with 'Exp' directly then we're initializing
- // a reference capture.
- // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
- const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
- // Returned as non-const-ref.
- // If we're returning 'Exp' directly then it's returned as non-const-ref.
- // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
- // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
- // adding const.)
- const auto AsNonConstRefReturn =
- returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp))));
- // It is used as a non-const-reference for initalizing a range-for loop.
- const auto AsNonConstRefRangeInit = cxxForRangeStmt(
- hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)),
- hasType(nonConstReferenceType())))));
- const auto Matches = match(
- traverse(TK_AsIs,
- findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand,
- AsNonConstThis, AsAmpersandOperand,
- AsPointerFromArrayDecay, AsOperatorArrowThis,
- AsNonConstRefArg, AsLambdaRefCaptureInit,
- AsNonConstRefReturn, AsNonConstRefRangeInit))
- .bind("stmt"))),
- Stm, Context);
- return selectFirst<Stmt>("stmt", Matches);
- }
- const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
- // Check whether any member of 'Exp' is mutated.
- const auto MemberExprs =
- match(findAll(expr(anyOf(memberExpr(hasObjectExpression(
- canResolveToExpr(equalsNode(Exp)))),
- cxxDependentScopeMemberExpr(hasObjectExpression(
- canResolveToExpr(equalsNode(Exp))))))
- .bind(NodeID<Expr>::value)),
- Stm, Context);
- return findExprMutation(MemberExprs);
- }
- const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
- // Check whether any element of an array is mutated.
- const auto SubscriptExprs =
- match(findAll(arraySubscriptExpr(
- anyOf(hasBase(canResolveToExpr(equalsNode(Exp))),
- hasBase(implicitCastExpr(
- allOf(hasCastKind(CK_ArrayToPointerDecay),
- hasSourceExpression(canResolveToExpr(
- equalsNode(Exp))))))))
- .bind(NodeID<Expr>::value)),
- Stm, Context);
- return findExprMutation(SubscriptExprs);
- }
- const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
- // If the 'Exp' is explicitly casted to a non-const reference type the
- // 'Exp' is considered to be modified.
- const auto ExplicitCast = match(
- findAll(
- stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
- explicitCastExpr(
- hasDestinationType(nonConstReferenceType()))))
- .bind("stmt")),
- Stm, Context);
- if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast))
- return CastStmt;
- // If 'Exp' is casted to any non-const reference type, check the castExpr.
- const auto Casts = match(
- findAll(
- expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))),
- anyOf(explicitCastExpr(
- hasDestinationType(nonConstReferenceType())),
- implicitCastExpr(hasImplicitDestinationType(
- nonConstReferenceType())))))
- .bind(NodeID<Expr>::value)),
- Stm, Context);
- if (const Stmt *S = findExprMutation(Casts))
- return S;
- // Treat std::{move,forward} as cast.
- const auto Calls =
- match(findAll(callExpr(callee(namedDecl(
- hasAnyName("::std::move", "::std::forward"))),
- hasArgument(0, canResolveToExpr(equalsNode(Exp))))
- .bind("expr")),
- Stm, Context);
- return findExprMutation(Calls);
- }
- const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
- // Keep the ordering for the specific initialization matches to happen first,
- // because it is cheaper to match all potential modifications of the loop
- // variable.
- // The range variable is a reference to a builtin array. In that case the
- // array is considered modified if the loop-variable is a non-const reference.
- const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType(
- hasUnqualifiedDesugaredType(referenceType(pointee(arrayType())))))));
- const auto RefToArrayRefToElements = match(
- findAll(stmt(cxxForRangeStmt(
- hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
- .bind(NodeID<Decl>::value)),
- hasRangeStmt(DeclStmtToNonRefToArray),
- hasRangeInit(canResolveToExpr(equalsNode(Exp)))))
- .bind("stmt")),
- Stm, Context);
- if (const auto *BadRangeInitFromArray =
- selectFirst<Stmt>("stmt", RefToArrayRefToElements))
- return BadRangeInitFromArray;
- // Small helper to match special cases in range-for loops.
- //
- // It is possible that containers do not provide a const-overload for their
- // iterator accessors. If this is the case, the variable is used non-const
- // no matter what happens in the loop. This requires special detection as it
- // is then faster to find all mutations of the loop variable.
- // It aims at a different modification as well.
- const auto HasAnyNonConstIterator =
- anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))),
- unless(hasMethod(allOf(hasName("begin"), isConst())))),
- allOf(hasMethod(allOf(hasName("end"), unless(isConst()))),
- unless(hasMethod(allOf(hasName("end"), isConst())))));
- const auto DeclStmtToNonConstIteratorContainer = declStmt(
- hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType(
- pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator)))))))));
- const auto RefToContainerBadIterators =
- match(findAll(stmt(cxxForRangeStmt(allOf(
- hasRangeStmt(DeclStmtToNonConstIteratorContainer),
- hasRangeInit(canResolveToExpr(equalsNode(Exp))))))
- .bind("stmt")),
- Stm, Context);
- if (const auto *BadIteratorsContainer =
- selectFirst<Stmt>("stmt", RefToContainerBadIterators))
- return BadIteratorsContainer;
- // If range for looping over 'Exp' with a non-const reference loop variable,
- // check all declRefExpr of the loop variable.
- const auto LoopVars =
- match(findAll(cxxForRangeStmt(
- hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
- .bind(NodeID<Decl>::value)),
- hasRangeInit(canResolveToExpr(equalsNode(Exp))))),
- Stm, Context);
- return findDeclMutation(LoopVars);
- }
- const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
- // Follow non-const reference returned by `operator*()` of move-only classes.
- // These are typically smart pointers with unique ownership so we treat
- // mutation of pointee as mutation of the smart pointer itself.
- const auto Ref =
- match(findAll(cxxOperatorCallExpr(
- hasOverloadedOperatorName("*"),
- callee(cxxMethodDecl(ofClass(isMoveOnly()),
- returns(nonConstReferenceType()))),
- argumentCountIs(1),
- hasArgument(0, canResolveToExpr(equalsNode(Exp))))
- .bind(NodeID<Expr>::value)),
- Stm, Context);
- if (const Stmt *S = findExprMutation(Ref))
- return S;
- // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
- const auto Refs = match(
- stmt(forEachDescendant(
- varDecl(
- hasType(nonConstReferenceType()),
- hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)),
- memberExpr(hasObjectExpression(
- canResolveToExpr(equalsNode(Exp)))))),
- hasParent(declStmt().bind("stmt")),
- // Don't follow the reference in range statement, we've
- // handled that separately.
- unless(hasParent(declStmt(hasParent(
- cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
- .bind(NodeID<Decl>::value))),
- Stm, Context);
- return findDeclMutation(Refs);
- }
- const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
- const auto NonConstRefParam = forEachArgumentWithParam(
- canResolveToExpr(equalsNode(Exp)),
- parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
- const auto IsInstantiated = hasDeclaration(isInstantiated());
- const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
- const auto Matches = match(
- traverse(
- TK_AsIs,
- findAll(
- expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
- unless(callee(namedDecl(hasAnyName(
- "::std::move", "::std::forward"))))),
- cxxConstructExpr(NonConstRefParam, IsInstantiated,
- FuncDecl)))
- .bind(NodeID<Expr>::value))),
- Stm, Context);
- for (const auto &Nodes : Matches) {
- const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
- const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
- if (!Func->getBody() || !Func->getPrimaryTemplate())
- return Exp;
- const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
- const ArrayRef<ParmVarDecl *> AllParams =
- Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
- QualType ParmType =
- AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
- AllParams.size() - 1)]
- ->getType();
- if (const auto *T = ParmType->getAs<PackExpansionType>())
- ParmType = T->getPattern();
- // If param type is forwarding reference, follow into the function
- // definition and see whether the param is mutated inside.
- if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
- if (!RefType->getPointeeType().getQualifiers() &&
- RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
- std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
- FuncParmAnalyzer[Func];
- if (!Analyzer)
- Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
- if (Analyzer->findMutation(Parm))
- return Exp;
- continue;
- }
- }
- // Not forwarding reference.
- return Exp;
- }
- return nullptr;
- }
- FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
- const FunctionDecl &Func, ASTContext &Context)
- : BodyAnalyzer(*Func.getBody(), Context) {
- if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
- // CXXCtorInitializer might also mutate Param but they're not part of
- // function body, check them eagerly here since they're typically trivial.
- for (const CXXCtorInitializer *Init : Ctor->inits()) {
- ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
- for (const ParmVarDecl *Parm : Ctor->parameters()) {
- if (Results.find(Parm) != Results.end())
- continue;
- if (const Stmt *S = InitAnalyzer.findMutation(Parm))
- Results[Parm] = S;
- }
- }
- }
- }
- const Stmt *
- FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
- const auto Memoized = Results.find(Parm);
- if (Memoized != Results.end())
- return Memoized->second;
- if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
- return Results[Parm] = S;
- return Results[Parm] = nullptr;
- }
- } // namespace clang
|