//===--- LoopConvertCheck.cpp - clang-tidy---------------------------------===// // // 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 "LoopConvertCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstring> #include <optional> #include <tuple> #include <utility> usingnamespaceclang::ast_matchers; usingnamespacellvm; namespace clang::tidy { template <> struct OptionEnumMapping<modernize::Confidence::Level> { … }; template <> struct OptionEnumMapping<modernize::VariableNamer::NamingStyle> { … }; namespace modernize { static const char LoopNameArray[] = …; static const char LoopNameIterator[] = …; static const char LoopNameReverseIterator[] = …; static const char LoopNamePseudoArray[] = …; static const char ConditionBoundName[] = …; static const char InitVarName[] = …; static const char BeginCallName[] = …; static const char EndCallName[] = …; static const char EndVarName[] = …; static const char DerefByValueResultName[] = …; static const char DerefByRefResultName[] = …; static const llvm::StringSet<> MemberNames{ … }; static const llvm::StringSet<> ADLNames{ … }; static const llvm::StringSet<> StdNames{ … }; static StatementMatcher integerComparisonMatcher() { … } static DeclarationMatcher initToZeroMatcher() { … } static StatementMatcher incrementVarMatcher() { … } static StatementMatcher arrayConditionMatcher(internal::Matcher<Expr> LimitExpr) { … } /// The matcher for loops over arrays. /// \code /// for (int i = 0; i < 3 + 2; ++i) { ... } /// \endcode /// The following string identifiers are bound to these parts of the AST: /// ConditionBoundName: '3 + 2' (as an Expr) /// InitVarName: 'i' (as a VarDecl) /// LoopName: The entire for loop (as a ForStmt) /// /// Client code will need to make sure that: /// - The index variable is only used as an array index. /// - All arrays indexed by the loop are the same. StatementMatcher makeArrayLoopMatcher() { … } /// The matcher used for iterator-based for loops. /// /// This matcher is more flexible than array-based loops. It will match /// catch loops of the following textual forms (regardless of whether the /// iterator type is actually a pointer type or a class type): /// /// \code /// for (containerType::iterator it = container.begin(), /// e = createIterator(); it != e; ++it) { ... } /// for (containerType::iterator it = container.begin(); /// it != anotherContainer.end(); ++it) { ... } /// for (containerType::iterator it = begin(container), /// e = end(container); it != e; ++it) { ... } /// for (containerType::iterator it = std::begin(container), /// e = std::end(container); it != e; ++it) { ... } /// \endcode /// The following string identifiers are bound to the parts of the AST: /// InitVarName: 'it' (as a VarDecl) /// LoopName: The entire for loop (as a ForStmt) /// In the first example only: /// EndVarName: 'e' (as a VarDecl) /// In the second example only: /// EndCallName: 'container.end()' (as a CXXMemberCallExpr) /// In the third/fourth examples: /// 'end(container)' or 'std::end(container)' (as a CallExpr) /// /// Client code will need to make sure that: /// - The two containers on which 'begin' and 'end' are called are the same. StatementMatcher makeIteratorLoopMatcher(bool IsReverse) { … } /// The matcher used for array-like containers (pseudoarrays). /// /// This matcher is more flexible than array-based loops. It will match /// loops of the following textual forms (regardless of whether the /// iterator type is actually a pointer type or a class type): /// /// \code /// for (int i = 0, j = container.size(); i < j; ++i) { ... } /// for (int i = 0; i < container.size(); ++i) { ... } /// for (int i = 0; i < size(container); ++i) { ... } /// \endcode /// The following string identifiers are bound to the parts of the AST: /// InitVarName: 'i' (as a VarDecl) /// LoopName: The entire for loop (as a ForStmt) /// In the first example only: /// EndVarName: 'j' (as a VarDecl) /// In the second example only: /// EndCallName: 'container.size()' (as a CXXMemberCallExpr) or /// 'size(container)' (as a CallExpr) /// /// Client code will need to make sure that: /// - The containers on which 'size()' is called is the container indexed. /// - The index variable is only used in overloaded operator[] or /// container.at(). /// - The container's iterators would not be invalidated during the loop. StatementMatcher makePseudoArrayLoopMatcher() { … } enum class IteratorCallKind { … }; struct ContainerCall { … }; // Find the Expr likely initializing an iterator. // // Call is either a CXXMemberCallExpr ('c.begin()') or CallExpr of a free // function with the first argument as a container ('begin(c)'), or nullptr. // Returns at a 3-tuple with the container expr, function name (begin/end/etc), // and whether the call is made through an arrow (->) for CXXMemberCallExprs. // The returned Expr* is nullptr if any of the assumptions are not met. // static std::tuple<const Expr *, StringRef, bool, IteratorCallKind> static std::optional<ContainerCall> getContainerExpr(const Expr *Call) { … } /// Determine whether Init appears to be an initializing an iterator. /// /// If it is, returns the object whose begin() or end() method is called, and /// the output parameter isArrow is set to indicate whether the initialization /// is called via . or ->. static std::pair<const Expr *, IteratorCallKind> getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, bool *IsArrow, bool IsReverse) { … } /// Determines the container whose begin() and end() functions are called /// for an iterator-based loop. /// /// BeginExpr must be a member call to a function named "begin()", and EndExpr /// must be a member. static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr, const Expr *EndExpr, bool *ContainerNeedsDereference, bool IsReverse) { … } /// Obtain the original source code text from a SourceRange. static StringRef getStringFromRange(SourceManager &SourceMgr, const LangOptions &LangOpts, SourceRange Range) { … } /// If the given expression is actually a DeclRefExpr or a MemberExpr, /// find and return the underlying ValueDecl; otherwise, return NULL. static const ValueDecl *getReferencedVariable(const Expr *E) { … } /// Returns true when the given expression is a member expression /// whose base is `this` (implicitly or not). static bool isDirectMemberExpr(const Expr *E) { … } /// Given an expression that represents an usage of an element from the /// container that we are iterating over, returns false when it can be /// guaranteed this element cannot be modified as a result of this usage. static bool canBeModified(ASTContext *Context, const Expr *E) { … } /// Returns true when it can be guaranteed that the elements of the /// container are not being modified. static bool usagesAreConst(ASTContext *Context, const UsageResult &Usages) { … } /// Returns true if the elements of the container are never accessed /// by reference. static bool usagesReturnRValues(const UsageResult &Usages) { … } /// Returns true if the container is const-qualified. static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) { … } LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context) : … { … } void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { … } void LoopConvertCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { … } void LoopConvertCheck::registerMatchers(MatchFinder *Finder) { … } /// Given the range of a single declaration, such as: /// \code /// unsigned &ThisIsADeclarationThatCanSpanSeveralLinesOfCode = /// InitializationValues[I]; /// next_instruction; /// \endcode /// Finds the range that has to be erased to remove this declaration without /// leaving empty lines, by extending the range until the beginning of the /// next instruction. /// /// We need to delete a potential newline after the deleted alias, as /// clang-format will leave empty lines untouched. For all other formatting we /// rely on clang-format to fix it. void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &Range) { … } /// Computes the changes needed to convert a given for loop, and /// applies them. void LoopConvertCheck::doConversion( ASTContext *Context, const VarDecl *IndexVar, const ValueDecl *MaybeContainer, const UsageResult &Usages, const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit, const ForStmt *Loop, RangeDescriptor Descriptor) { … } /// Returns a string which refers to the container iterated over. StringRef LoopConvertCheck::getContainerString(ASTContext *Context, const ForStmt *Loop, const Expr *ContainerExpr) { … } /// Determines what kind of 'auto' must be used after converting a for /// loop that iterates over an array or pseudoarray. void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context, const BoundNodes &Nodes, const Expr *ContainerExpr, const UsageResult &Usages, RangeDescriptor &Descriptor) { … } /// Determines what kind of 'auto' must be used after converting an /// iterator based for loop. void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context, const BoundNodes &Nodes, RangeDescriptor &Descriptor) { … } /// Determines the parameters needed to build the range replacement. void LoopConvertCheck::determineRangeDescriptor( ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop, LoopFixerKind FixerKind, const Expr *ContainerExpr, const UsageResult &Usages, RangeDescriptor &Descriptor) { … } /// Check some of the conditions that must be met for the loop to be /// convertible. bool LoopConvertCheck::isConvertible(ASTContext *Context, const ast_matchers::BoundNodes &Nodes, const ForStmt *Loop, LoopFixerKind FixerKind) { … } void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) { … } llvm::StringRef LoopConvertCheck::getReverseFunction() const { … } llvm::StringRef LoopConvertCheck::getReverseHeader() const { … } } // namespace modernize } // namespace clang::tidy