12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
62 return hasType(cxxRecordDecl(hasAnyName(
63 "::std::vector",
"::std::set",
"::std::unordered_set",
"::std::map",
64 "::std::unordered_map",
"::std::array",
"::std::deque")));
69AST_MATCHER(Expr, hasSideEffects) {
70 return Node.HasSideEffects(Finder->getASTContext());
78 VectorLikeClasses(
utils::options::parseStringList(
79 Options.get(
"VectorLikeClasses",
"::std::vector"))),
80 EnableProto(Options.get(
"EnableProto", false)) {}
84 Options.store(Opts,
"VectorLikeClasses",
86 Options.store(Opts,
"EnableProto", EnableProto);
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)))))
111 const auto RefersToLoopVar = ignoringParenImpCasts(
116 const auto HasInterestingLoopBody = hasBody(
117 anyOf(compoundStmt(statementCountIs(1), has(AppendCall)), AppendCall));
118 const auto InInterestingCompoundStmt =
119 hasParent(compoundStmt(has(TargetVarDefStmt)).bind(
LoopParentName));
129 forStmt(hasLoopInit(LoopVarInit),
130 hasCondition(binaryOperator(
131 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
132 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
134 hasIncrement(unaryOperator(hasOperatorName(
"++"),
135 hasUnaryOperand(RefersToLoopVar))),
136 HasInterestingLoopBody, InInterestingCompoundStmt)
151 memberExpr(hasObjectExpression(unless(hasSideEffects())),
153 HasInterestingLoopBody, InInterestingCompoundStmt)
159 const auto VectorDecl = cxxRecordDecl(hasAnyName(VectorLikeClasses));
160 const auto AppendMethodDecl =
161 cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"));
166 const auto ProtoDecl =
167 cxxRecordDecl(isDerivedFrom(
"::proto2::MessageLite"));
172 const auto AddFieldMethodDecl =
173 cxxMethodDecl(matchesName(
"::add_"), unless(isConst()));
180 const MatchFinder::MatchResult &Result) {
181 auto *Context = Result.Context;
182 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
185 const SourceManager &SM = *Result.SourceManager;
186 const auto *VectorVarDecl =
188 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(
LoopCounterName);
189 const auto *RangeLoop =
191 const auto *VectorAppendCall =
193 const auto *ProtoVarDecl = Result.Nodes.getNodeAs<VarDecl>(
ProtoVarDeclName);
194 const auto *ProtoAddFieldCall =
196 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(
LoopEndExprName);
197 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(
LoopParentName);
199 const CXXMemberCallExpr *AppendCall =
200 VectorAppendCall ? VectorAppendCall : ProtoAddFieldCall;
201 assert(AppendCall &&
"no append call expression");
203 const Stmt *LoopStmt = ForLoop;
205 LoopStmt = RangeLoop;
207 const auto *TargetVarDecl = VectorVarDecl;
209 TargetVarDecl = ProtoVarDecl;
211 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVarRefs =
214 for (
const auto *Ref : AllVarRefs) {
222 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
223 LoopStmt->getBeginLoc())) {
228 std::string PartialReserveStmt;
229 if (VectorAppendCall !=
nullptr) {
230 PartialReserveStmt =
".reserve";
232 llvm::StringRef FieldName = ProtoAddFieldCall->getMethodDecl()->getName();
233 FieldName.consume_front(
"add_");
234 std::string MutableFieldName = (
"mutable_" + FieldName).str();
235 PartialReserveStmt =
"." + MutableFieldName +
239 llvm::StringRef VarName = Lexer::getSourceText(
240 CharSourceRange::getTokenRange(
241 AppendCall->getImplicitObjectArgument()->getSourceRange()),
242 SM, Context->getLangOpts());
244 std::string ReserveSize;
249 StringRef RangeInitExpName =
250 Lexer::getSourceText(CharSourceRange::getTokenRange(
251 RangeLoop->getRangeInit()->getSourceRange()),
252 SM, Context->getLangOpts());
253 ReserveSize = (RangeInitExpName +
".size()").str();
254 }
else if (ForLoop) {
256 StringRef LoopEndSource = Lexer::getSourceText(
257 CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
258 Context->getLangOpts());
259 ReserveSize = std::string(LoopEndSource);
262 auto Diag = diag(AppendCall->getBeginLoc(),
263 "%0 is called inside a loop; consider pre-allocating the "
264 "container capacity before the loop")
265 << AppendCall->getMethodDecl()->getDeclName();
266 if (!ReserveSize.empty()) {
267 std::string ReserveStmt =
268 (VarName + PartialReserveStmt +
"(" + ReserveSize +
");\n").str();
269 Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), ReserveStmt);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
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