11#include "clang/Basic/IdentifierTable.h"
12#include "clang/Basic/LLVM.h"
13#include "clang/Basic/Lambda.h"
14#include "clang/Basic/SourceLocation.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Basic/TokenKinds.h"
17#include "clang/Lex/Lexer.h"
18#include "llvm/ADT/APSInt.h"
19#include "llvm/ADT/FoldingSet.h"
20#include "llvm/ADT/StringRef.h"
37bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
38 StmtAncestors.try_emplace(Statement, StmtStack.back());
39 StmtStack.push_back(Statement);
40 RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
50bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Statement) {
51 for (
const auto *Decl : Statement->decls())
52 if (
const auto *V = dyn_cast<VarDecl>(Decl))
53 DeclParents.try_emplace(V, Statement);
58bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
59 Components.push_back(E);
64bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
65 Components.push_back(Member);
71bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
72 if (
auto *V = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
73 return VisitVarDecl(V);
78bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *V) {
79 const Stmt *Curr = DeclParents->lookup(V);
81 while (Curr !=
nullptr) {
82 if (Curr == ContainingStmt) {
83 DependsOnInsideVariable =
true;
86 Curr = StmtParents->lookup(Curr);
91 if (llvm::none_of(*ReplacedVars,
92 [&](
const auto &I) {
return I.second == V; }))
94 DependsOnInsideVariable =
true;
100bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
101 const StmtGeneratedVarNameMap::const_iterator I =
102 GeneratedDecls->find(TheLoop);
103 if (I != GeneratedDecls->end() && I->second == Name) {
112bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *D) {
113 const IdentifierInfo *Ident =
D->getIdentifier();
114 if (Ident && Ident->getName() == Name) {
123bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
124 if (
auto *D = dyn_cast<NamedDecl>(DeclRef->getDecl()))
125 return VisitNamedDecl(D);
131bool DeclFinderASTVisitor::VisitTypeLoc(TypeLoc TL) {
132 const QualType QType = TL.getType();
135 if (QType.getAsString() == Name) {
142 if (
const IdentifierInfo *Ident = QType.getBaseTypeIdentifier()) {
143 if (Ident->getName() == Name) {
168 E = E->IgnoreImplicit();
169 if (
const auto *ConstructExpr = dyn_cast<CXXConstructExpr>(E)) {
172 if (ConstructExpr->getNumArgs() != 1 ||
173 ConstructExpr->getConstructionKind() != CXXConstructionKind::Complete)
175 E = ConstructExpr->getArg(0);
176 if (
const auto *Temp = dyn_cast<MaterializeTemporaryExpr>(E))
177 E = Temp->getSubExpr();
182 if (
const auto *ME = dyn_cast<CXXMemberCallExpr>(E))
183 if (isa<CXXConversionDecl>(ME->getMethodDecl()))
190 const Expr *Second) {
196 return dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
201 return First && Second &&
202 First->getCanonicalDecl() == Second->getCanonicalDecl();
217 if (
const auto *Uop = dyn_cast<UnaryOperator>(E))
218 return Uop->getOpcode() == UO_Deref ? Uop->getSubExpr() :
nullptr;
220 if (
const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
221 return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1
230template <
typename ContainerT>
231static bool containsExpr(ASTContext *Context,
const ContainerT *Container,
233 llvm::FoldingSetNodeID ID;
234 E->Profile(ID, *Context,
true);
235 return llvm::any_of(*Container,
236 [&](
const auto &I) {
return ID == I.second; });
248 const VarDecl *IndexVar) {
249 const DeclRefExpr *Idx =
getDeclRef(IndexExpr);
250 return Idx && Idx->getType()->isIntegerType() &&
282 const Expr *IndexExpr,
283 const VarDecl *IndexVar,
const Expr *Obj,
284 const Expr *SourceExpr,
bool PermitDeref) {
288 if (
areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
289 Obj->IgnoreParenImpCasts()))
293 if (PermitDeref &&
areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
294 InnerObj->IgnoreParenImpCasts()))
309 const VarDecl *IndexVar) {
310 return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1 &&
322 const VarDecl *IndexVar) {
323 return Uop->getOpcode() == UO_Deref &&
344static bool isAliasDecl(
const ASTContext *Context,
const Decl *TheDecl,
345 const VarDecl *IndexVar) {
346 const auto *VDecl = dyn_cast<VarDecl>(TheDecl);
349 if (!VDecl->hasInit())
352 bool OnlyCasts =
true;
353 const Expr *Init = VDecl->getInit()->IgnoreParenImpCasts();
354 if (isa_and_nonnull<CXXConstructExpr>(Init)) {
364 const QualType InitType = Init->getType();
365 QualType DeclarationType = VDecl->getType();
366 if (!DeclarationType.isNull() && DeclarationType->isReferenceType())
367 DeclarationType = DeclarationType.getNonReferenceType();
369 if (InitType.isNull() || DeclarationType.isNull() ||
370 !ASTContext::hasSameUnqualifiedType(DeclarationType, InitType))
374 switch (Init->getStmtClass()) {
375 case Stmt::ArraySubscriptExprClass: {
376 const auto *E = cast<ArraySubscriptExpr>(Init);
382 case Stmt::UnaryOperatorClass:
385 case Stmt::CXXOperatorCallExprClass: {
386 const auto *OpCall = cast<CXXOperatorCallExpr>(Init);
387 if (OpCall->getOperator() == OO_Star)
389 if (OpCall->getOperator() == OO_Subscript) {
390 return OpCall->getNumArgs() == 2 &&
396 case Stmt::CXXMemberCallExprClass: {
397 const auto *MemCall = cast<CXXMemberCallExpr>(Init);
400 const auto *MDecl = MemCall->getMethodDecl();
401 if (MDecl && !isa<CXXConversionDecl>(MDecl) &&
402 MDecl->getNameAsString() ==
"at" && MemCall->getNumArgs() == 1) {
428 const QualType &ArrayType,
429 const Expr *ConditionExpr) {
430 if (!ConditionExpr || ConditionExpr->isValueDependent())
432 const ConstantArrayType *ConstType =
433 Context->getAsConstantArrayType(ArrayType);
436 std::optional<llvm::APSInt> ConditionSize =
437 ConditionExpr->getIntegerConstantExpr(*Context);
440 const llvm::APSInt ArraySize(ConstType->getSize());
441 return llvm::APSInt::isSameValue(*ConditionSize, ArraySize);
445 const VarDecl *IndexVar,
446 const VarDecl *EndVar,
447 const Expr *ContainerExpr,
448 const Expr *ArrayBoundExpr,
449 bool ContainerNeedsDereference)
450 : Context(Context), IndexVar(IndexVar), EndVar(EndVar),
451 ContainerExpr(ContainerExpr), ArrayBoundExpr(ArrayBoundExpr),
452 ContainerNeedsDereference(ContainerNeedsDereference),
456 addComponent(ContainerExpr);
460 TraverseStmt(
const_cast<Stmt *
>(Body));
461 return OnlyUsedAsIndex && ContainerExpr;
466 for (
const auto &I : Components)
470void ForLoopIndexUseVisitor::addComponent(
const Expr *E) {
471 llvm::FoldingSetNodeID ID;
472 const Expr *Node = E->IgnoreParenImpCasts();
473 Node->Profile(ID, *Context,
true);
474 DependentExprs.emplace_back(Node, ID);
478 SourceLocation Begin = U.
Range.getBegin();
479 if (Begin.isMacroID())
480 Begin = Context->getSourceManager().getSpellingLoc(Begin);
482 if (UsageLocations.insert(Begin).second)
497bool ForLoopIndexUseVisitor::TraverseUnaryOperator(UnaryOperator *Uop) {
505 return VisitorBase::TraverseUnaryOperator(Uop);
533bool ForLoopIndexUseVisitor::TraverseMemberExpr(MemberExpr *Member) {
534 const Expr *Base = Member->getBase();
536 const Expr *ResultExpr = Member;
538 if (
const auto *Call =
539 dyn_cast<CXXOperatorCallExpr>(Base->IgnoreParenImpCasts())) {
547 if (Call->getOperator() == OO_Arrow) {
548 assert(Call->getNumArgs() == 1 &&
549 "Operator-> takes more than one argument");
552 ExprType = Call->getCallReturnType(*Context);
558 if (!Member->isArrow()) {
559 OnlyUsedAsIndex =
false;
563 if (ExprType.isNull())
564 ExprType =
Obj->getType();
566 if (!ExprType->isPointerType())
571 const SourceLocation ArrowLoc = Lexer::getLocForEndOfToken(
572 Base->getExprLoc(), 0, Context->getSourceManager(),
573 Context->getLangOpts());
576 if (ArrowLoc.isValid()) {
578 SourceRange(Base->getExprLoc(), ArrowLoc)));
582 return VisitorBase::TraverseMemberExpr(Member);
592bool ForLoopIndexUseVisitor::TraverseCXXMemberCallExpr(
593 CXXMemberCallExpr *MemberCall) {
595 dyn_cast<MemberExpr>(MemberCall->getCallee()->IgnoreParenImpCasts());
597 return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
602 const IdentifierInfo *Ident = Member->getMemberDecl()->getIdentifier();
603 if (Ident && Ident->isStr(
"at") && MemberCall->getNumArgs() == 1) {
605 Member->getBase(), ContainerExpr,
606 ContainerNeedsDereference)) {
612 if (
containsExpr(Context, &DependentExprs, Member->getBase()))
615 return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
637bool ForLoopIndexUseVisitor::TraverseCXXOperatorCallExpr(
638 CXXOperatorCallExpr *OpCall) {
639 switch (OpCall->getOperator()) {
648 if (OpCall->getNumArgs() != 2)
651 OpCall->getArg(0), ContainerExpr,
652 ContainerNeedsDereference)) {
661 return VisitorBase::TraverseCXXOperatorCallExpr(OpCall);
683bool ForLoopIndexUseVisitor::TraverseArraySubscriptExpr(ArraySubscriptExpr *E) {
684 Expr *Arr = E->getBase();
686 return VisitorBase::TraverseArraySubscriptExpr(E);
688 if ((ContainerExpr && !
areSameExpr(Context, Arr->IgnoreParenImpCasts(),
689 ContainerExpr->IgnoreParenImpCasts())) ||
694 OnlyUsedAsIndex =
false;
695 return VisitorBase::TraverseArraySubscriptExpr(E);
737bool ForLoopIndexUseVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
738 const ValueDecl *TheDecl = E->getDecl();
742 OnlyUsedAsIndex =
false;
770bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
771 const LambdaCapture *C,
773 if (
C->capturesVariable()) {
774 ValueDecl *VDecl =
C->getCapturedVar();
784 if (VDecl->isInitCapture())
785 traverseStmtImpl(cast<VarDecl>(VDecl)->getInit());
787 return VisitorBase::TraverseLambdaCapture(LE, C, Init);
794bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) {
795 if (!AliasDecl && S->isSingleDecl() &&
796 isAliasDecl(Context, S->getSingleDecl(), IndexVar)) {
798 if (CurrStmtParent) {
799 if (isa<IfStmt>(CurrStmtParent) || isa<WhileStmt>(CurrStmtParent) ||
800 isa<SwitchStmt>(CurrStmtParent)) {
801 ReplaceWithAliasUse =
true;
802 }
else if (isa<ForStmt>(CurrStmtParent)) {
803 if (cast<ForStmt>(CurrStmtParent)->getConditionVariableDeclStmt() == S)
804 ReplaceWithAliasUse =
true;
807 AliasFromForInit =
true;
815bool ForLoopIndexUseVisitor::traverseStmtImpl(Stmt *S) {
818 const Stmt *OldNextParent = NextStmtParent;
819 CurrStmtParent = NextStmtParent;
821 const bool Result = VisitorBase::TraverseStmt(S);
822 NextStmtParent = OldNextParent;
826bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) {
831 if (
const auto *LE = dyn_cast_or_null<LambdaExpr>(NextStmtParent)) {
834 if (S != LE->getBody())
837 return traverseStmtImpl(S);
844 std::string IteratorName;
845 StringRef ContainerName;
847 ContainerName = TheContainer->getName();
849 const size_t Len = ContainerName.size();
850 if (Len > 1 && ContainerName.ends_with(Style ==
NS_UpperCase ?
"S" :
"s")) {
851 IteratorName = std::string(ContainerName.substr(0, Len - 1));
853 if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
857 if (Len > 2 && ContainerName.ends_with(Style ==
NS_UpperCase ?
"S_" :
"s_")) {
858 IteratorName = std::string(ContainerName.substr(0, Len - 2));
860 if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
864 return std::string(OldIndex->getName());
873bool VariableNamer::declarationExists(StringRef Symbol) {
874 assert(Context !=
nullptr &&
"Expected an ASTContext");
875 const IdentifierInfo &Ident = Context->Idents.get(Symbol);
878 if (!isAnyIdentifier(Ident.getTokenID()))
882 if (Ident.hasMacroDefinition())
886 for (
const Stmt *S = SourceStmt; S !=
nullptr; S = ReverseAST->lookup(S)) {
887 const StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
888 if (I != GeneratedDecls->end() && I->second == Symbol)
899 DeclFinderASTVisitor DeclFinder(std::string(Symbol), GeneratedDecls);
900 return DeclFinder.findUsages(SourceStmt);
A class to encapsulate lowering of the tool's confidence level.
ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar, const VarDecl *EndVar, const Expr *ContainerExpr, const Expr *ArrayBoundExpr, bool ContainerNeedsDereference)
bool findAndVerifyUsages(const Stmt *Body)
Finds all uses of IndexVar in Body, placing all usages in Usages, and returns true if IndexVar was on...
void addUsage(const Usage &U)
Adds the Usage if it was not added before.
void addComponents(const ComponentVector &Components)
Add a set of components that we should consider relevant to the container.
std::string createIndexName()
Generate a new index name.
SmallVector< const Expr *, 16 > ComponentVector
A vector used to store the AST subtrees of an Expr.
static bool containsExpr(ASTContext *Context, const ContainerT *Container, const Expr *E)
Returns true when the Container contains an Expr equivalent to E.
static bool isAliasDecl(const ASTContext *Context, const Decl *TheDecl, const VarDecl *IndexVar)
Determines whether the given Decl defines a variable initialized to the loop object.
const DeclRefExpr * getDeclRef(const Expr *E)
Returns the DeclRefExpr represented by E, or NULL if there isn't one.
static bool exprReferencesVariable(const ValueDecl *Target, const Expr *E)
Determines if an expression is a declaration reference to a particular variable.
bool areSameVariable(const ValueDecl *First, const ValueDecl *Second)
Returns true when two ValueDecls are the same variable.
static const Expr * getDereferenceOperand(const Expr *E)
If the expression is a dereference or call to operator*(), return the operand.
bool areSameExpr(const ASTContext *Context, const Expr *First, const Expr *Second)
Returns true when two Exprs are equivalent.
const Expr * digThroughConstructorsConversions(const Expr *E)
Look through conversion/copy constructors and member functions to find the explicit initialization ex...
static bool arrayMatchesBoundExpr(const ASTContext *Context, const QualType &ArrayType, const Expr *ConditionExpr)
Determines whether the bound of a for loop condition expression is the same as the statically computa...
static bool isIndexInSubscriptExpr(const Expr *IndexExpr, const VarDecl *IndexVar)
Returns true when the index expression is a declaration reference to IndexVar.
static bool isDereferenceOfOpCall(const CXXOperatorCallExpr *OpCall, const VarDecl *IndexVar)
Returns true when Opcall is a call a one-parameter dereference of IndexVar.
static bool isDereferenceOfUop(const UnaryOperator *Uop, const VarDecl *IndexVar)
Returns true when Uop is a dereference of IndexVar.
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt, const ASTContext &Context, bool Canonical)
The information needed to describe a valid convertible usage of an array index or iterator.