123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- //===--- LoopConvertUtils.h - clang-tidy ------------------------*- C++ -*-===//
- //
- // 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
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
- #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/RecursiveASTVisitor.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Basic/SourceLocation.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/SmallSet.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringRef.h"
- #include <algorithm>
- #include <memory>
- #include <string>
- #include <utility>
- namespace clang::tidy::modernize {
- enum LoopFixerKind {
- LFK_Array,
- LFK_Iterator,
- LFK_ReverseIterator,
- LFK_PseudoArray
- };
- /// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
- typedef llvm::DenseMap<const clang::Stmt *, const clang::Stmt *> StmtParentMap;
- /// A map used to walk the AST in reverse:
- /// maps VarDecl to the to parent DeclStmt.
- typedef llvm::DenseMap<const clang::VarDecl *, const clang::DeclStmt *>
- DeclParentMap;
- /// A map used to track which variables have been removed by a refactoring pass.
- /// It maps the parent ForStmt to the removed index variable's VarDecl.
- typedef llvm::DenseMap<const clang::ForStmt *, const clang::VarDecl *>
- ReplacedVarsMap;
- /// A map used to remember the variable names generated in a Stmt
- typedef llvm::DenseMap<const clang::Stmt *, std::string>
- StmtGeneratedVarNameMap;
- /// A vector used to store the AST subtrees of an Expr.
- typedef llvm::SmallVector<const clang::Expr *, 16> ComponentVector;
- /// Class used build the reverse AST properties needed to detect
- /// name conflicts and free variables.
- class StmtAncestorASTVisitor
- : public clang::RecursiveASTVisitor<StmtAncestorASTVisitor> {
- public:
- StmtAncestorASTVisitor() { StmtStack.push_back(nullptr); }
- /// Run the analysis on the AST.
- ///
- /// In case we're running this analysis multiple times, don't repeat the work.
- void gatherAncestors(ASTContext &Ctx) {
- if (StmtAncestors.empty())
- TraverseAST(Ctx);
- }
- /// Accessor for StmtAncestors.
- const StmtParentMap &getStmtToParentStmtMap() { return StmtAncestors; }
- /// Accessor for DeclParents.
- const DeclParentMap &getDeclToParentStmtMap() { return DeclParents; }
- friend class clang::RecursiveASTVisitor<StmtAncestorASTVisitor>;
- private:
- StmtParentMap StmtAncestors;
- DeclParentMap DeclParents;
- llvm::SmallVector<const clang::Stmt *, 16> StmtStack;
- bool TraverseStmt(clang::Stmt *Statement);
- bool VisitDeclStmt(clang::DeclStmt *Statement);
- };
- /// Class used to find the variables and member expressions on which an
- /// arbitrary expression depends.
- class ComponentFinderASTVisitor
- : public clang::RecursiveASTVisitor<ComponentFinderASTVisitor> {
- public:
- ComponentFinderASTVisitor() = default;
- /// Find the components of an expression and place them in a ComponentVector.
- void findExprComponents(const clang::Expr *SourceExpr) {
- TraverseStmt(const_cast<clang::Expr *>(SourceExpr));
- }
- /// Accessor for Components.
- const ComponentVector &getComponents() { return Components; }
- friend class clang::RecursiveASTVisitor<ComponentFinderASTVisitor>;
- private:
- ComponentVector Components;
- bool VisitDeclRefExpr(clang::DeclRefExpr *E);
- bool VisitMemberExpr(clang::MemberExpr *Member);
- };
- /// Class used to determine if an expression is dependent on a variable declared
- /// inside of the loop where it would be used.
- class DependencyFinderASTVisitor
- : public clang::RecursiveASTVisitor<DependencyFinderASTVisitor> {
- public:
- DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
- const DeclParentMap *DeclParents,
- const ReplacedVarsMap *ReplacedVars,
- const clang::Stmt *ContainingStmt)
- : StmtParents(StmtParents), DeclParents(DeclParents),
- ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) {}
- /// Run the analysis on Body, and return true iff the expression
- /// depends on some variable declared within ContainingStmt.
- ///
- /// This is intended to protect against hoisting the container expression
- /// outside of an inner context if part of that expression is declared in that
- /// inner context.
- ///
- /// For example,
- /// \code
- /// const int N = 10, M = 20;
- /// int arr[N][M];
- /// int getRow();
- ///
- /// for (int i = 0; i < M; ++i) {
- /// int k = getRow();
- /// printf("%d:", arr[k][i]);
- /// }
- /// \endcode
- /// At first glance, this loop looks like it could be changed to
- /// \code
- /// for (int elem : arr[k]) {
- /// int k = getIndex();
- /// printf("%d:", elem);
- /// }
- /// \endcode
- /// But this is malformed, since `k` is used before it is defined!
- ///
- /// In order to avoid this, this class looks at the container expression
- /// `arr[k]` and decides whether or not it contains a sub-expression declared
- /// within the loop body.
- bool dependsOnInsideVariable(const clang::Stmt *Body) {
- DependsOnInsideVariable = false;
- TraverseStmt(const_cast<clang::Stmt *>(Body));
- return DependsOnInsideVariable;
- }
- friend class clang::RecursiveASTVisitor<DependencyFinderASTVisitor>;
- private:
- const StmtParentMap *StmtParents;
- const DeclParentMap *DeclParents;
- const clang::Stmt *ContainingStmt;
- const ReplacedVarsMap *ReplacedVars;
- bool DependsOnInsideVariable;
- bool VisitVarDecl(clang::VarDecl *V);
- bool VisitDeclRefExpr(clang::DeclRefExpr *D);
- };
- /// Class used to determine if any declarations used in a Stmt would conflict
- /// with a particular identifier. This search includes the names that don't
- /// actually appear in the AST (i.e. created by a refactoring tool) by including
- /// a map from Stmts to generated names associated with those stmts.
- class DeclFinderASTVisitor
- : public clang::RecursiveASTVisitor<DeclFinderASTVisitor> {
- public:
- DeclFinderASTVisitor(const StringRef &Name,
- const StmtGeneratedVarNameMap *GeneratedDecls)
- : Name(Name), GeneratedDecls(GeneratedDecls), Found(false) {}
- /// Attempts to find any usages of variables name Name in Body, returning
- /// true when it is used in Body. This includes the generated loop variables
- /// of ForStmts which have already been transformed.
- bool findUsages(const clang::Stmt *Body) {
- Found = false;
- TraverseStmt(const_cast<clang::Stmt *>(Body));
- return Found;
- }
- friend class clang::RecursiveASTVisitor<DeclFinderASTVisitor>;
- private:
- std::string Name;
- /// GeneratedDecls keeps track of ForStmts which have been transformed,
- /// mapping each modified ForStmt to the variable generated in the loop.
- const StmtGeneratedVarNameMap *GeneratedDecls;
- bool Found;
- bool VisitForStmt(clang::ForStmt *F);
- bool VisitNamedDecl(clang::NamedDecl *D);
- bool VisitDeclRefExpr(clang::DeclRefExpr *D);
- bool VisitTypeLoc(clang::TypeLoc TL);
- };
- /// The information needed to describe a valid convertible usage
- /// of an array index or iterator.
- struct Usage {
- enum UsageKind {
- // Regular usages of the loop index (the ones not specified below). Some
- // examples:
- // \code
- // int X = 8 * Arr[i];
- // ^~~~~~
- // f(param1, param2, *It);
- // ^~~
- // if (Vec[i].SomeBool) {}
- // ^~~~~~
- // \endcode
- UK_Default,
- // Indicates whether this is an access to a member through the arrow
- // operator on pointers or iterators.
- UK_MemberThroughArrow,
- // If the variable is being captured by a lambda, indicates whether the
- // capture was done by value or by reference.
- UK_CaptureByCopy,
- UK_CaptureByRef
- };
- // The expression that is going to be converted. Null in case of lambda
- // captures.
- const Expr *Expression;
- UsageKind Kind;
- // Range that covers this usage.
- SourceRange Range;
- explicit Usage(const Expr *E)
- : Expression(E), Kind(UK_Default), Range(Expression->getSourceRange()) {}
- Usage(const Expr *E, UsageKind Kind, SourceRange Range)
- : Expression(E), Kind(Kind), Range(std::move(Range)) {}
- };
- /// A class to encapsulate lowering of the tool's confidence level.
- class Confidence {
- public:
- enum Level {
- // Transformations that are likely to change semantics.
- CL_Risky,
- // Transformations that might change semantics.
- CL_Reasonable,
- // Transformations that will not change semantics.
- CL_Safe
- };
- /// Initialize confidence level.
- explicit Confidence(Confidence::Level Level) : CurrentLevel(Level) {}
- /// Lower the internal confidence level to Level, but do not raise it.
- void lowerTo(Confidence::Level Level) {
- CurrentLevel = std::min(Level, CurrentLevel);
- }
- /// Return the internal confidence level.
- Level getLevel() const { return CurrentLevel; }
- private:
- Level CurrentLevel;
- };
- // The main computational result of ForLoopIndexVisitor.
- typedef llvm::SmallVector<Usage, 8> UsageResult;
- // General functions used by ForLoopIndexUseVisitor and LoopConvertCheck.
- const Expr *digThroughConstructorsConversions(const Expr *E);
- bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second);
- const DeclRefExpr *getDeclRef(const Expr *E);
- bool areSameVariable(const ValueDecl *First, const ValueDecl *Second);
- /// Discover usages of expressions consisting of index or iterator
- /// access.
- ///
- /// Given an index variable, recursively crawls a for loop to discover if the
- /// index variable is used in a way consistent with range-based for loop access.
- class ForLoopIndexUseVisitor
- : public RecursiveASTVisitor<ForLoopIndexUseVisitor> {
- public:
- ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar,
- const VarDecl *EndVar, const Expr *ContainerExpr,
- const Expr *ArrayBoundExpr,
- bool ContainerNeedsDereference);
- /// Finds all uses of IndexVar in Body, placing all usages in Usages,
- /// and returns true if IndexVar was only used in a way consistent with a
- /// range-based for loop.
- ///
- /// The general strategy is to reject any DeclRefExprs referencing IndexVar,
- /// with the exception of certain acceptable patterns.
- /// For arrays, the DeclRefExpr for IndexVar must appear as the index of an
- /// ArraySubscriptExpression. Iterator-based loops may dereference
- /// IndexVar or call methods through operator-> (builtin or overloaded).
- /// Array-like containers may use IndexVar as a parameter to the at() member
- /// function and in overloaded operator[].
- bool findAndVerifyUsages(const Stmt *Body);
- /// Add a set of components that we should consider relevant to the
- /// container.
- void addComponents(const ComponentVector &Components);
- /// Accessor for Usages.
- const UsageResult &getUsages() const { return Usages; }
- /// Adds the Usage if it was not added before.
- void addUsage(const Usage &U);
- /// Get the container indexed by IndexVar, if any.
- const Expr *getContainerIndexed() const { return ContainerExpr; }
- /// Returns the statement declaring the variable created as an alias
- /// for the loop element, if any.
- const DeclStmt *getAliasDecl() const { return AliasDecl; }
- /// Accessor for ConfidenceLevel.
- Confidence::Level getConfidenceLevel() const {
- return ConfidenceLevel.getLevel();
- }
- /// Indicates if the alias declaration was in a place where it cannot
- /// simply be removed but rather replaced with a use of the alias variable.
- /// For example, variables declared in the condition of an if, switch, or for
- /// stmt.
- bool aliasUseRequired() const { return ReplaceWithAliasUse; }
- /// Indicates if the alias declaration came from the init clause of a
- /// nested for loop. SourceRanges provided by Clang for DeclStmts in this
- /// case need to be adjusted.
- bool aliasFromForInit() const { return AliasFromForInit; }
- private:
- /// Typedef used in CRTP functions.
- typedef RecursiveASTVisitor<ForLoopIndexUseVisitor> VisitorBase;
- friend class RecursiveASTVisitor<ForLoopIndexUseVisitor>;
- /// Overriden methods for RecursiveASTVisitor's traversal.
- bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E);
- bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
- bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
- bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
- Expr *Init);
- bool TraverseMemberExpr(MemberExpr *Member);
- bool TraverseUnaryOperator(UnaryOperator *Uop);
- bool VisitDeclRefExpr(DeclRefExpr *E);
- bool VisitDeclStmt(DeclStmt *S);
- bool TraverseStmt(Stmt *S);
- /// Add an expression to the list of expressions on which the container
- /// expression depends.
- void addComponent(const Expr *E);
- // Input member variables:
- ASTContext *Context;
- /// The index variable's VarDecl.
- const VarDecl *IndexVar;
- /// The loop's 'end' variable, which cannot be mentioned at all.
- const VarDecl *EndVar;
- /// The Expr which refers to the container.
- const Expr *ContainerExpr;
- /// The Expr which refers to the terminating condition for array-based loops.
- const Expr *ArrayBoundExpr;
- bool ContainerNeedsDereference;
- // Output member variables:
- /// A container which holds all usages of IndexVar as the index of
- /// ArraySubscriptExpressions.
- UsageResult Usages;
- llvm::SmallSet<SourceLocation, 8> UsageLocations;
- bool OnlyUsedAsIndex;
- /// The DeclStmt for an alias to the container element.
- const DeclStmt *AliasDecl;
- Confidence ConfidenceLevel;
- /// A list of expressions on which ContainerExpr depends.
- ///
- /// If any of these expressions are encountered outside of an acceptable usage
- /// of the loop element, lower our confidence level.
- llvm::SmallVector<std::pair<const Expr *, llvm::FoldingSetNodeID>, 16>
- DependentExprs;
- /// The parent-in-waiting. Will become the real parent once we traverse down
- /// one level in the AST.
- const Stmt *NextStmtParent;
- /// The actual parent of a node when Visit*() calls are made. Only the
- /// parentage of DeclStmt's to possible iteration/selection statements is of
- /// importance.
- const Stmt *CurrStmtParent;
- /// \see aliasUseRequired().
- bool ReplaceWithAliasUse;
- /// \see aliasFromForInit().
- bool AliasFromForInit;
- };
- struct TUTrackingInfo {
- /// Reset and initialize per-TU tracking information.
- ///
- /// Must be called before using container accessors.
- TUTrackingInfo() : ParentFinder(new StmtAncestorASTVisitor) {}
- StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
- StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
- ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
- private:
- std::unique_ptr<StmtAncestorASTVisitor> ParentFinder;
- StmtGeneratedVarNameMap GeneratedDecls;
- ReplacedVarsMap ReplacedVars;
- };
- /// Create names for generated variables within a particular statement.
- ///
- /// VariableNamer uses a DeclContext as a reference point, checking for any
- /// conflicting declarations higher up in the context or within SourceStmt.
- /// It creates a variable name using hints from a source container and the old
- /// index, if they exist.
- class VariableNamer {
- public:
- // Supported naming styles.
- enum NamingStyle {
- NS_CamelBack,
- NS_CamelCase,
- NS_LowerCase,
- NS_UpperCase,
- };
- VariableNamer(StmtGeneratedVarNameMap *GeneratedDecls,
- const StmtParentMap *ReverseAST, const clang::Stmt *SourceStmt,
- const clang::VarDecl *OldIndex,
- const clang::ValueDecl *TheContainer,
- const clang::ASTContext *Context, NamingStyle Style)
- : GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
- SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
- Context(Context), Style(Style) {}
- /// Generate a new index name.
- ///
- /// Generates the name to be used for an inserted iterator. It relies on
- /// declarationExists() to determine that there are no naming conflicts, and
- /// tries to use some hints from the container name and the old index name.
- std::string createIndexName();
- private:
- StmtGeneratedVarNameMap *GeneratedDecls;
- const StmtParentMap *ReverseAST;
- const clang::Stmt *SourceStmt;
- const clang::VarDecl *OldIndex;
- const clang::ValueDecl *TheContainer;
- const clang::ASTContext *Context;
- const NamingStyle Style;
- // Determine whether or not a declaration that would conflict with Symbol
- // exists in an outer context or in any statement contained in SourceStmt.
- bool declarationExists(llvm::StringRef Symbol);
- };
- } // namespace clang::tidy::modernize
- #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
|