11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "../utils/DeclRefExprUtils.h"
15 #include "../utils/OptionsUtils.h"
17 using namespace clang::ast_matchers;
21 namespace performance {
45 static const char LoopCounterName[] =
"for_loop_counter";
46 static const char LoopParentName[] =
"loop_parent";
47 static const char VectorVarDeclName[] =
"vector_var_decl";
48 static const char VectorVarDeclStmtName[] =
"vector_var_decl_stmt";
49 static const char PushBackOrEmplaceBackCallName[] =
"append_call";
50 static const char LoopInitVarName[] =
"loop_init_var";
51 static const char LoopEndExprName[] =
"loop_end_expr";
53 static const char RangeLoopName[] =
"for_range_loop";
55 ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
56 return hasType(cxxRecordDecl(hasAnyName(
57 "::std::vector",
"::std::set",
"::std::unordered_set",
"::std::map",
58 "::std::unordered_map",
"::std::array",
"::std::deque")));
63 InefficientVectorOperationCheck::InefficientVectorOperationCheck(
67 Options.get(
"VectorLikeClasses",
"::std::vector"))) {}
76 const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
77 VectorLikeClasses.begin(), VectorLikeClasses.end())));
78 const auto VectorDefaultConstructorCall = cxxConstructExpr(
80 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
81 const auto VectorVarDecl =
82 varDecl(hasInitializer(VectorDefaultConstructorCall))
83 .bind(VectorVarDeclName);
84 const auto VectorAppendCallExpr =
86 callee(cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"))),
87 on(hasType(VectorDecl)),
88 onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
89 .bind(PushBackOrEmplaceBackCallName);
90 const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr));
91 const auto VectorVarDefStmt =
92 declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
93 .bind(VectorVarDeclStmtName);
95 const auto LoopVarInit =
96 declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
97 .bind(LoopInitVarName)));
98 const auto RefersToLoopVar = ignoringParenImpCasts(
99 declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
103 const auto HasInterestingLoopBody =
104 hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)),
106 const auto InInterestingCompoundStmt =
107 hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
115 hasLoopInit(LoopVarInit),
116 hasCondition(binaryOperator(
117 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
118 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
119 .bind(LoopEndExprName)))),
120 hasIncrement(unaryOperator(hasOperatorName(
"++"),
121 hasUnaryOperand(RefersToLoopVar))),
122 HasInterestingLoopBody, InInterestingCompoundStmt)
123 .bind(LoopCounterName),
132 hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
133 HasInterestingLoopBody, InInterestingCompoundStmt)
134 .bind(RangeLoopName),
139 const MatchFinder::MatchResult &Result) {
140 auto*
Context = Result.Context;
141 if (
Context->getDiagnostics().hasUncompilableErrorOccurred())
144 const SourceManager &
SM = *Result.SourceManager;
145 const auto *VectorVarDecl =
146 Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
147 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
148 const auto *RangeLoop =
149 Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
150 const auto *VectorAppendCall =
151 Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
152 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
153 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
155 const Stmt *LoopStmt = ForLoop;
157 LoopStmt = RangeLoop;
159 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
162 for (
const auto *Ref : AllVectorVarRefs) {
170 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
171 LoopStmt->getLocStart())) {
176 llvm::StringRef VectorVarName = Lexer::getSourceText(
177 CharSourceRange::getTokenRange(
178 VectorAppendCall->getImplicitObjectArgument()->getSourceRange()),
181 std::string ReserveStmt;
186 StringRef RangeInitExpName = Lexer::getSourceText(
187 CharSourceRange::getTokenRange(
188 RangeLoop->getRangeInit()->getSourceRange()),
192 (VectorVarName +
".reserve(" + RangeInitExpName +
".size()" +
");\n")
194 }
else if (ForLoop) {
196 StringRef LoopEndSource = Lexer::getSourceText(
197 CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
199 ReserveStmt = (VectorVarName +
".reserve(" + LoopEndSource +
");\n").str();
203 diag(VectorAppendCall->getLocStart(),
204 "%0 is called inside a loop; "
205 "consider pre-allocating the vector capacity before the loop")
206 << VectorAppendCall->getMethodDecl()->getDeclName();
208 if (!ReserveStmt.empty())
209 Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt);
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to VarDecl within Stmt.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Base class for all clang-tidy checks.
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.