10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Lex/Lexer.h" 13 #include "../utils/DeclRefExprUtils.h" 14 #include "../utils/OptionsUtils.h" 20 namespace performance {
44 static const char LoopCounterName[] =
"for_loop_counter";
45 static const char LoopParentName[] =
"loop_parent";
46 static const char VectorVarDeclName[] =
"vector_var_decl";
47 static const char VectorVarDeclStmtName[] =
"vector_var_decl_stmt";
48 static const char PushBackOrEmplaceBackCallName[] =
"append_call";
49 static const char LoopInitVarName[] =
"loop_init_var";
50 static const char LoopEndExprName[] =
"loop_end_expr";
52 static const char RangeLoopName[] =
"for_range_loop";
54 ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
55 return hasType(cxxRecordDecl(hasAnyName(
56 "::std::vector",
"::std::set",
"::std::unordered_set",
"::std::map",
57 "::std::unordered_map",
"::std::array",
"::std::deque")));
62 InefficientVectorOperationCheck::InefficientVectorOperationCheck(
66 Options.get(
"VectorLikeClasses",
"::std::vector"))) {}
75 const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
76 VectorLikeClasses.begin(), VectorLikeClasses.end())));
77 const auto VectorDefaultConstructorCall = cxxConstructExpr(
79 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
80 const auto VectorVarDecl =
81 varDecl(hasInitializer(VectorDefaultConstructorCall))
82 .bind(VectorVarDeclName);
83 const auto VectorAppendCallExpr =
85 callee(cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"))),
86 on(hasType(VectorDecl)),
87 onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
88 .bind(PushBackOrEmplaceBackCallName);
89 const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr));
90 const auto VectorVarDefStmt =
91 declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
92 .bind(VectorVarDeclStmtName);
94 const auto LoopVarInit =
95 declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
96 .bind(LoopInitVarName)));
97 const auto RefersToLoopVar = ignoringParenImpCasts(
98 declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
102 const auto HasInterestingLoopBody =
103 hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)),
105 const auto InInterestingCompoundStmt =
106 hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
114 hasLoopInit(LoopVarInit),
115 hasCondition(binaryOperator(
116 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
117 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
118 .bind(LoopEndExprName)))),
119 hasIncrement(unaryOperator(hasOperatorName(
"++"),
120 hasUnaryOperand(RefersToLoopVar))),
121 HasInterestingLoopBody, InInterestingCompoundStmt)
122 .bind(LoopCounterName),
131 hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
132 HasInterestingLoopBody, InInterestingCompoundStmt)
133 .bind(RangeLoopName),
138 const MatchFinder::MatchResult &
Result) {
139 auto* Context = Result.Context;
140 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
143 const SourceManager &SM = *Result.SourceManager;
144 const auto *VectorVarDecl =
145 Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
146 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
147 const auto *RangeLoop =
148 Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
149 const auto *VectorAppendCall =
150 Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
151 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
152 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
154 const Stmt *LoopStmt = ForLoop;
156 LoopStmt = RangeLoop;
158 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
161 for (
const auto *Ref : AllVectorVarRefs) {
169 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
170 LoopStmt->getBeginLoc())) {
175 llvm::StringRef VectorVarName = Lexer::getSourceText(
177 VectorAppendCall->getImplicitObjectArgument()->getSourceRange()),
178 SM, Context->getLangOpts());
180 std::string ReserveStmt;
185 StringRef RangeInitExpName = Lexer::getSourceText(
187 RangeLoop->getRangeInit()->getSourceRange()),
188 SM, Context->getLangOpts());
191 (VectorVarName +
".reserve(" + RangeInitExpName +
".size()" +
");\n")
193 }
else if (ForLoop) {
195 StringRef LoopEndSource = Lexer::getSourceText(
197 Context->getLangOpts());
198 ReserveStmt = (VectorVarName +
".reserve(" + LoopEndSource +
");\n").str();
202 diag(VectorAppendCall->getBeginLoc(),
203 "%0 is called inside a loop; " 204 "consider pre-allocating the vector capacity before the loop")
205 << VectorAppendCall->getMethodDecl()->getDeclName();
207 if (!ReserveStmt.empty())
208 Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), 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.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.