/// \brief If an overloaded operator call is a dereference of IndexVar or /// a subscript of a the container with IndexVar as the single argument, /// include it as a valid usage and prune the traversal. /// /// For example, given /// \code /// struct Foo { int bar(); int x; }; /// vector<Foo> v; /// void f(Foo); /// \endcode /// the following uses will be considered convertible: /// \code /// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) { /// f(*i); /// } /// for (int i = 0; i < v.size(); ++i) { /// int i = v[i] + 1; /// } /// \endcode bool ForLoopIndexUseVisitor::TraverseCXXOperatorCallExpr( CXXOperatorCallExpr *OpCall) { switch (OpCall->getOperator()) { case OO_Star: if (isDereferenceOfOpCall(OpCall, IndexVar)) { Usages.push_back(Usage(OpCall)); return true; } break; case OO_Subscript: if (OpCall->getNumArgs() != 2) break; if (isIndexInSubscriptExpr(Context, OpCall->getArg(1), IndexVar, OpCall->getArg(0), ContainerExpr, ContainerNeedsDereference)) { Usages.push_back(Usage(OpCall)); return true; } break; default: break; } return VisitorBase::TraverseCXXOperatorCallExpr(OpCall); }
/// \brief If the unary operator is a dereference of IndexVar, include it /// as a valid usage and prune the traversal. /// /// For example, if container.begin() and container.end() both return pointers /// to int, this makes sure that the initialization for `k` is not counted as an /// unconvertible use of the iterator `i`. /// \code /// for (int *i = container.begin(), *e = container.end(); i != e; ++i) { /// int k = *i + 2; /// } /// \endcode bool ForLoopIndexUseVisitor::TraverseUnaryDeref(UnaryOperator *Uop) { // If we dereference an iterator that's actually a pointer, count the // occurrence. if (isDereferenceOfUop(Uop, IndexVar)) { Usages.push_back(Usage(Uop)); return true; } return VisitorBase::TraverseUnaryOperator(Uop); }
/// \brief If the member expression is operator-> (overloaded or not) on /// IndexVar, include it as a valid usage and prune the traversal. /// /// For example, given /// \code /// struct Foo { int bar(); int x; }; /// vector<Foo> v; /// \endcode /// the following uses will be considered convertible: /// \code /// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) { /// int b = i->bar(); /// int k = i->x + 1; /// } /// \endcode /// though /// \code /// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) { /// int k = i.insert(1); /// } /// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) { /// int b = e->bar(); /// } /// \endcode /// will not. bool ForLoopIndexUseVisitor::TraverseMemberExpr(MemberExpr *Member) { const Expr *Base = Member->getBase(); const DeclRefExpr *Obj = getDeclRef(Base); const Expr *ResultExpr = Member; QualType ExprType; if (const CXXOperatorCallExpr *Call = dyn_cast<CXXOperatorCallExpr>(Base->IgnoreParenImpCasts())) { // If operator->() is a MemberExpr containing a CXXOperatorCallExpr, then // the MemberExpr does not have the expression we want. We therefore catch // that instance here. // For example, if vector<Foo>::iterator defines operator->(), then the // example `i->bar()` at the top of this function is a CXXMemberCallExpr // referring to `i->` as the member function called. We want just `i`, so // we take the argument to operator->() as the base object. if(Call->getOperator() == OO_Arrow) { assert(Call->getNumArgs() == 1 && "Operator-> takes more than one argument"); Obj = getDeclRef(Call->getArg(0)); ResultExpr = Obj; ExprType = Call->getCallReturnType(); } } if (Member->isArrow() && Obj && exprReferencesVariable(IndexVar, Obj)) { if (ExprType.isNull()) ExprType = Obj->getType(); assert(ExprType->isPointerType() && "Operator-> returned non-pointer type"); // FIXME: This works around not having the location of the arrow operator. // Consider adding OperatorLoc to MemberExpr? SourceLocation ArrowLoc = Lexer::getLocForEndOfToken(Base->getExprLoc(), 0, Context->getSourceManager(), Context->getLangOpts()); // If something complicated is happening (i.e. the next token isn't an // arrow), give up on making this work. if (!ArrowLoc.isInvalid()) { Usages.push_back(Usage(ResultExpr, /*IsArrow=*/true, SourceRange(Base->getExprLoc(), ArrowLoc))); return true; } } return TraverseStmt(Member->getBase()); }
/// \brief If we encounter an array with IndexVar as the index of an /// ArraySubsriptExpression, note it as a consistent usage and prune the /// AST traversal. /// /// For example, given /// \code /// const int N = 5; /// int arr[N]; /// \endcode /// This is intended to permit /// \code /// for (int i = 0; i < N; ++i) { /* use arr[i] */ } /// \endcode /// but not /// \code /// for (int i = 0; i < N; ++i) { /* use notArr[i] */ } /// \endcode /// and further checking needs to be done later to ensure that exactly one array /// is referenced. bool ForLoopIndexUseVisitor::TraverseArraySubscriptExpr( ArraySubscriptExpr *ASE) { Expr *Arr = ASE->getBase(); if (!isIndexInSubscriptExpr(ASE->getIdx(), IndexVar)) return VisitorBase::TraverseArraySubscriptExpr(ASE); if ((ContainerExpr && !areSameExpr(Context, Arr->IgnoreParenImpCasts(), ContainerExpr->IgnoreParenImpCasts())) || !arrayMatchesBoundExpr(Context, Arr->IgnoreImpCasts()->getType(), ArrayBoundExpr)) { // If we have already discovered the array being indexed and this isn't it // or this array doesn't match, mark this loop as unconvertible. OnlyUsedAsIndex = false; return VisitorBase::TraverseArraySubscriptExpr(ASE); } if (!ContainerExpr) ContainerExpr = Arr; Usages.push_back(Usage(ASE)); return true; }
/// \brief If a member function call is the at() accessor on the container with /// IndexVar as the single argument, include it as a valid usage and prune /// the traversal. /// /// Member calls on other objects will not be permitted. /// Calls on the iterator object are not permitted, unless done through /// operator->(). The one exception is allowing vector::at() for pseudoarrays. bool ForLoopIndexUseVisitor::TraverseCXXMemberCallExpr( CXXMemberCallExpr *MemberCall) { MemberExpr *Member = dyn_cast<MemberExpr>(MemberCall->getCallee()->IgnoreParenImpCasts()); if (!Member) return VisitorBase::TraverseCXXMemberCallExpr(MemberCall); // We specifically allow an accessor named "at" to let STL in, though // this is restricted to pseudo-arrays by requiring a single, integer // argument. const IdentifierInfo *Ident = Member->getMemberDecl()->getIdentifier(); if (Ident && Ident->isStr("at") && MemberCall->getNumArgs() == 1) { if (isIndexInSubscriptExpr(Context, MemberCall->getArg(0), IndexVar, Member->getBase(), ContainerExpr, ContainerNeedsDereference)) { Usages.push_back(Usage(MemberCall)); return true; } } if (containsExpr(Context, &DependentExprs, Member->getBase())) ConfidenceLevel.lowerTo(TCK_Risky); return VisitorBase::TraverseCXXMemberCallExpr(MemberCall); }