10#include "../utils/DeclRefExprUtils.h"
11#include "../utils/OptionsUtils.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
51static const char LoopCounterName[] =
"for_loop_counter";
52static const char LoopParentName[] =
"loop_parent";
53static const char VectorVarDeclName[] =
"vector_var_decl";
54static const char VectorVarDeclStmtName[] =
"vector_var_decl_stmt";
55static const char PushBackOrEmplaceBackCallName[] =
"append_call";
56static const char ProtoVarDeclName[] =
"proto_var_decl";
57static const char ProtoVarDeclStmtName[] =
"proto_var_decl_stmt";
58static const char ProtoAddFieldCallName[] =
"proto_add_field";
59static const char LoopInitVarName[] =
"loop_init_var";
60static const char LoopEndExprName[] =
"loop_end_expr";
61static const char RangeLoopName[] =
"for_range_loop";
63ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
64 return hasType(cxxRecordDecl(hasAnyName(
65 "::std::vector",
"::std::set",
"::std::unordered_set",
"::std::map",
66 "::std::unordered_map",
"::std::array",
"::std::deque")));
70 return Node.HasSideEffects(Finder->getASTContext());
78 VectorLikeClasses(utils::options::parseStringList(
79 Options.get(
"VectorLikeClasses",
"::std::vector"))),
80 EnableProto(Options.getLocalOrGlobal(
"EnableProto", false)) {}
89void InefficientVectorOperationCheck::addMatcher(
90 const DeclarationMatcher &TargetRecordDecl, StringRef VarDeclName,
91 StringRef VarDeclStmtName,
const DeclarationMatcher &AppendMethodDecl,
92 StringRef AppendCallName, MatchFinder *Finder) {
93 const auto DefaultConstructorCall = cxxConstructExpr(
94 hasType(TargetRecordDecl),
95 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
96 const auto TargetVarDecl =
97 varDecl(hasInitializer(DefaultConstructorCall)).bind(VarDeclName);
98 const auto TargetVarDefStmt =
99 declStmt(hasSingleDecl(equalsBoundNode(std::string(VarDeclName))))
100 .bind(VarDeclStmtName);
102 const auto AppendCallExpr =
104 callee(AppendMethodDecl), on(hasType(TargetRecordDecl)),
105 onImplicitObjectArgument(declRefExpr(to(TargetVarDecl))))
106 .bind(AppendCallName);
107 const auto AppendCall = expr(ignoringImplicit(AppendCallExpr));
108 const auto LoopVarInit = declStmt(hasSingleDecl(
109 varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
110 .bind(LoopInitVarName)));
111 const auto RefersToLoopVar = ignoringParenImpCasts(
112 declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
116 const auto HasInterestingLoopBody = hasBody(
117 anyOf(compoundStmt(statementCountIs(1), has(AppendCall)), AppendCall));
118 const auto InInterestingCompoundStmt =
119 hasParent(compoundStmt(has(TargetVarDefStmt)).bind(LoopParentName));
130 hasLoopInit(LoopVarInit),
131 hasCondition(binaryOperator(
132 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
133 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
134 .bind(LoopEndExprName)))),
135 hasIncrement(unaryOperator(hasOperatorName(
"++"),
136 hasUnaryOperand(RefersToLoopVar))),
137 HasInterestingLoopBody, InInterestingCompoundStmt)
138 .bind(LoopCounterName),
151 anyOf(declRefExpr(supportedContainerTypesMatcher()),
152 memberExpr(hasObjectExpression(unless(hasSideEffects())),
153 supportedContainerTypesMatcher()))),
154 HasInterestingLoopBody, InInterestingCompoundStmt)
155 .bind(RangeLoopName),
160 const auto VectorDecl = cxxRecordDecl(hasAnyName(VectorLikeClasses));
161 const auto AppendMethodDecl =
162 cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"));
163 addMatcher(VectorDecl, VectorVarDeclName, VectorVarDeclStmtName,
164 AppendMethodDecl, PushBackOrEmplaceBackCallName, Finder);
167 const auto ProtoDecl =
168 cxxRecordDecl(isDerivedFrom(
"::proto2::MessageLite"));
173 const auto AddFieldMethodDecl =
174 cxxMethodDecl(matchesName(
"::add_"), unless(isConst()));
175 addMatcher(ProtoDecl, ProtoVarDeclName, ProtoVarDeclStmtName,
176 AddFieldMethodDecl, ProtoAddFieldCallName, Finder);
181 const MatchFinder::MatchResult &Result) {
182 auto* Context = Result.Context;
183 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
186 const SourceManager &SM = *Result.SourceManager;
187 const auto *VectorVarDecl =
188 Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
189 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
190 const auto *RangeLoop =
191 Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
192 const auto *VectorAppendCall =
193 Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
194 const auto *ProtoVarDecl = Result.Nodes.getNodeAs<VarDecl>(ProtoVarDeclName);
195 const auto *ProtoAddFieldCall =
196 Result.Nodes.getNodeAs<CXXMemberCallExpr>(ProtoAddFieldCallName);
197 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
198 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
200 const CXXMemberCallExpr *AppendCall =
201 VectorAppendCall ? VectorAppendCall : ProtoAddFieldCall;
202 assert(AppendCall &&
"no append call expression");
204 const Stmt *LoopStmt = ForLoop;
206 LoopStmt = RangeLoop;
208 const auto *TargetVarDecl = VectorVarDecl;
210 TargetVarDecl = ProtoVarDecl;
212 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVarRefs =
215 for (
const auto *Ref : AllVarRefs) {
223 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
224 LoopStmt->getBeginLoc())) {
229 std::string PartialReserveStmt;
230 if (VectorAppendCall !=
nullptr) {
231 PartialReserveStmt =
".reserve";
233 llvm::StringRef FieldName = ProtoAddFieldCall->getMethodDecl()->getName();
234 FieldName.consume_front(
"add_");
235 std::string MutableFieldName = (
"mutable_" + FieldName).str();
236 PartialReserveStmt =
"." + MutableFieldName +
240 llvm::StringRef VarName = Lexer::getSourceText(
241 CharSourceRange::getTokenRange(
242 AppendCall->getImplicitObjectArgument()->getSourceRange()),
243 SM, Context->getLangOpts());
245 std::string ReserveSize;
250 StringRef RangeInitExpName =
251 Lexer::getSourceText(CharSourceRange::getTokenRange(
252 RangeLoop->getRangeInit()->getSourceRange()),
253 SM, Context->getLangOpts());
254 ReserveSize = (RangeInitExpName +
".size()").str();
255 }
else if (ForLoop) {
257 StringRef LoopEndSource = Lexer::getSourceText(
258 CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
259 Context->getLangOpts());
260 ReserveSize = std::string(LoopEndSource);
263 auto Diag =
diag(AppendCall->getBeginLoc(),
264 "%0 is called inside a loop; consider pre-allocating the "
265 "container capacity before the loop")
266 << AppendCall->getMethodDecl()->getDeclName();
267 if (!ReserveSize.empty()) {
268 std::string ReserveStmt =
269 (VarName + PartialReserveStmt +
"(" + ReserveSize +
");\n").str();
270 Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), ReserveStmt);
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to VarDecl within Stmt.
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap