12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
61static ast_matchers::internal::Matcher<Expr>
63 return hasType(cxxRecordDecl(hasAnyName(ContainerTypes)));
68AST_MATCHER(Expr, hasSideEffects) {
69 return Node.HasSideEffects(Finder->getASTContext());
77 VectorLikeClasses(
utils::options::parseStringList(
78 Options.get(
"VectorLikeClasses",
"::std::vector"))),
79 ForRangeLoopClasses(
utils::options::parseStringList(Options.get(
80 "ForRangeLoopClasses",
81 "::std::array;::std::deque;::std::map;::std::set;"
82 "::std::unordered_map;::std::unordered_set;::std::vector"))),
83 EnableProto(Options.get(
"EnableProto", false)) {}
87 Options.store(Opts,
"VectorLikeClasses",
89 Options.store(Opts,
"ForRangeLoopClasses",
91 Options.store(Opts,
"EnableProto", EnableProto);
94void InefficientVectorOperationCheck::addMatcher(
95 const DeclarationMatcher &TargetRecordDecl, StringRef VarDeclName,
96 StringRef VarDeclStmtName,
const DeclarationMatcher &AppendMethodDecl,
97 StringRef AppendCallName, MatchFinder *Finder) {
98 const auto DefaultConstructorCall = cxxConstructExpr(
99 hasType(TargetRecordDecl),
100 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
101 const auto TargetVarDecl =
102 varDecl(hasInitializer(DefaultConstructorCall)).bind(VarDeclName);
103 const auto TargetVarDefStmt =
104 declStmt(hasSingleDecl(equalsBoundNode(std::string(VarDeclName))))
105 .bind(VarDeclStmtName);
107 const auto AppendCallExpr =
108 cxxMemberCallExpr(callee(AppendMethodDecl), on(hasType(TargetRecordDecl)),
109 onImplicitObjectArgument(ignoringParenImpCasts(
110 declRefExpr(to(TargetVarDecl)))))
111 .bind(AppendCallName);
112 const auto AppendCall = expr(ignoringImplicit(AppendCallExpr));
113 const auto LoopVarInit = declStmt(hasSingleDecl(
114 varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
116 const auto RefersToLoopVar = ignoringParenImpCasts(
121 const auto HasInterestingLoopBody = hasBody(
122 anyOf(compoundStmt(statementCountIs(1), has(AppendCall)), AppendCall));
123 const auto InInterestingCompoundStmt =
124 hasParent(compoundStmt(has(TargetVarDefStmt)).bind(
LoopParentName));
134 forStmt(hasLoopInit(LoopVarInit),
135 hasCondition(binaryOperator(
136 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
137 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
139 hasIncrement(unaryOperator(hasOperatorName(
"++"),
140 hasUnaryOperand(RefersToLoopVar))),
141 HasInterestingLoopBody, InInterestingCompoundStmt)
156 memberExpr(hasObjectExpression(unless(hasSideEffects())),
158 HasInterestingLoopBody, InInterestingCompoundStmt)
164 const auto VectorDecl = cxxRecordDecl(hasAnyName(VectorLikeClasses));
165 const auto AppendMethodDecl =
166 cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"));
171 const auto ProtoDecl =
172 cxxRecordDecl(isDerivedFrom(
"::proto2::MessageLite"));
177 const auto AddFieldMethodDecl =
178 cxxMethodDecl(matchesName(
"::add_"), unless(isConst()));
185 const MatchFinder::MatchResult &Result) {
186 auto *Context = Result.Context;
187 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
190 const SourceManager &SM = *Result.SourceManager;
191 const auto *VectorVarDecl =
193 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(
LoopCounterName);
194 const auto *RangeLoop =
196 const auto *VectorAppendCall =
198 const auto *ProtoVarDecl = Result.Nodes.getNodeAs<VarDecl>(
ProtoVarDeclName);
199 const auto *ProtoAddFieldCall =
201 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(
LoopEndExprName);
202 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(
LoopParentName);
204 const CXXMemberCallExpr *AppendCall =
205 VectorAppendCall ? VectorAppendCall : ProtoAddFieldCall;
206 assert(AppendCall &&
"no append call expression");
208 const Stmt *LoopStmt = ForLoop;
210 LoopStmt = RangeLoop;
212 const auto *TargetVarDecl = VectorVarDecl;
214 TargetVarDecl = ProtoVarDecl;
216 const llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVarRefs =
219 for (
const auto *Ref : AllVarRefs) {
227 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
228 LoopStmt->getBeginLoc())) {
233 std::string PartialReserveStmt;
234 if (VectorAppendCall !=
nullptr) {
235 PartialReserveStmt =
".reserve";
237 StringRef FieldName = ProtoAddFieldCall->getMethodDecl()->getName();
238 FieldName.consume_front(
"add_");
239 const std::string MutableFieldName = (
"mutable_" + FieldName).str();
240 PartialReserveStmt =
"." + MutableFieldName +
244 const StringRef VarName = Lexer::getSourceText(
245 CharSourceRange::getTokenRange(
246 AppendCall->getImplicitObjectArgument()->getSourceRange()),
247 SM, Context->getLangOpts());
249 std::string ReserveSize;
254 const StringRef RangeInitExpName =
255 Lexer::getSourceText(CharSourceRange::getTokenRange(
256 RangeLoop->getRangeInit()->getSourceRange()),
257 SM, Context->getLangOpts());
258 ReserveSize = (RangeInitExpName +
".size()").str();
259 }
else if (ForLoop) {
261 const StringRef LoopEndSource = Lexer::getSourceText(
262 CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
263 Context->getLangOpts());
264 ReserveSize = std::string(LoopEndSource);
268 diag(AppendCall->getBeginLoc(),
269 "%0 is called inside a loop; consider pre-allocating the "
270 "container capacity before the loop")
271 << AppendCall->getMethodDecl()->getDeclName();
272 if (!ReserveSize.empty()) {
273 const std::string ReserveStmt =
274 (VarName + PartialReserveStmt +
"(" + ReserveSize +
");\n").str();
275 Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), ReserveStmt);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
SmallPtrSet< const DeclRefExpr *, 16 > allDeclRefExprs(const ValueDecl &ValueDecl, const Stmt &Stmt, ASTContext &Context)
Returns set of all DeclRefExprs to ValueDecl within Stmt.
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap