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 {
53 static const char LoopCounterName[] =
"for_loop_counter";
54 static const char LoopParentName[] =
"loop_parent";
55 static const char VectorVarDeclName[] =
"vector_var_decl";
56 static const char VectorVarDeclStmtName[] =
"vector_var_decl_stmt";
57 static const char PushBackOrEmplaceBackCallName[] =
"append_call";
58 static const char ProtoVarDeclName[] =
"proto_var_decl";
59 static const char ProtoVarDeclStmtName[] =
"proto_var_decl_stmt";
60 static const char ProtoAddFieldCallName[] =
"proto_add_field";
61 static const char LoopInitVarName[] =
"loop_init_var";
62 static const char LoopEndExprName[] =
"loop_end_expr";
63 static const char RangeLoopName[] =
"for_range_loop";
65 ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
66 return hasType(cxxRecordDecl(hasAnyName(
67 "::std::vector",
"::std::set",
"::std::unordered_set",
"::std::map",
68 "::std::unordered_map",
"::std::array",
"::std::deque")));
73 InefficientVectorOperationCheck::InefficientVectorOperationCheck(
77 Options.get(
"VectorLikeClasses",
"::std::vector"))),
78 EnableProto(Options.getLocalOrGlobal(
"EnableProto", false)) {}
87 void InefficientVectorOperationCheck::AddMatcher(
88 const DeclarationMatcher &TargetRecordDecl, StringRef VarDeclName,
89 StringRef VarDeclStmtName,
const DeclarationMatcher &AppendMethodDecl,
90 StringRef AppendCallName, MatchFinder *Finder) {
91 const auto DefaultConstructorCall = cxxConstructExpr(
92 hasType(TargetRecordDecl),
93 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
94 const auto TargetVarDecl =
95 varDecl(hasInitializer(DefaultConstructorCall)).bind(VarDeclName);
96 const auto TargetVarDefStmt =
97 declStmt(hasSingleDecl(equalsBoundNode(VarDeclName)))
98 .bind(VarDeclStmtName);
100 const auto AppendCallExpr =
102 callee(AppendMethodDecl), on(hasType(TargetRecordDecl)),
103 onImplicitObjectArgument(declRefExpr(to(TargetVarDecl))))
104 .bind(AppendCallName);
105 const auto AppendCall = expr(ignoringImplicit(AppendCallExpr));
106 const auto LoopVarInit =
107 declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
108 .bind(LoopInitVarName)));
109 const auto RefersToLoopVar = ignoringParenImpCasts(
110 declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
114 const auto HasInterestingLoopBody = hasBody(
115 anyOf(compoundStmt(statementCountIs(1), has(AppendCall)), AppendCall));
116 const auto InInterestingCompoundStmt =
117 hasParent(compoundStmt(has(TargetVarDefStmt)).bind(LoopParentName));
128 hasLoopInit(LoopVarInit),
129 hasCondition(binaryOperator(
130 hasOperatorName(
"<"), hasLHS(RefersToLoopVar),
131 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
132 .bind(LoopEndExprName)))),
133 hasIncrement(unaryOperator(hasOperatorName(
"++"),
134 hasUnaryOperand(RefersToLoopVar))),
135 HasInterestingLoopBody, InInterestingCompoundStmt)
136 .bind(LoopCounterName),
148 hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
149 HasInterestingLoopBody, InInterestingCompoundStmt)
150 .bind(RangeLoopName),
155 const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
156 VectorLikeClasses.begin(), VectorLikeClasses.end())));
157 const auto AppendMethodDecl =
158 cxxMethodDecl(hasAnyName(
"push_back",
"emplace_back"));
159 AddMatcher(VectorDecl, VectorVarDeclName, VectorVarDeclStmtName,
160 AppendMethodDecl, PushBackOrEmplaceBackCallName, Finder);
163 const auto ProtoDecl =
164 cxxRecordDecl(isDerivedFrom(
"::proto2::MessageLite"));
169 const auto AddFieldMethodDecl =
170 cxxMethodDecl(matchesName(
"::add_"), unless(isConst()));
171 AddMatcher(ProtoDecl, ProtoVarDeclName, ProtoVarDeclStmtName,
172 AddFieldMethodDecl, ProtoAddFieldCallName, Finder);
177 const MatchFinder::MatchResult &Result) {
178 auto* Context = Result.Context;
179 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
182 const SourceManager &SM = *Result.SourceManager;
183 const auto *VectorVarDecl =
184 Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
185 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
186 const auto *RangeLoop =
187 Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
188 const auto *VectorAppendCall =
189 Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
190 const auto *ProtoVarDecl = Result.Nodes.getNodeAs<VarDecl>(ProtoVarDeclName);
191 const auto *ProtoAddFieldCall =
192 Result.Nodes.getNodeAs<CXXMemberCallExpr>(ProtoAddFieldCallName);
193 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
194 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
196 const CXXMemberCallExpr *AppendCall =
197 VectorAppendCall ? VectorAppendCall : ProtoAddFieldCall;
198 assert(AppendCall &&
"no append call expression");
200 const Stmt *LoopStmt = ForLoop;
202 LoopStmt = RangeLoop;
204 const auto *TargetVarDecl = VectorVarDecl;
206 TargetVarDecl = ProtoVarDecl;
208 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVarRefs =
211 for (
const auto *Ref : AllVarRefs) {
219 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
220 LoopStmt->getBeginLoc())) {
225 std::string PartialReserveStmt;
226 if (VectorAppendCall !=
nullptr) {
227 PartialReserveStmt =
".reserve";
229 llvm::StringRef FieldName = ProtoAddFieldCall->getMethodDecl()->getName();
230 FieldName.consume_front(
"add_");
231 std::string MutableFieldName = (
"mutable_" + FieldName).str();
232 PartialReserveStmt =
"." + MutableFieldName +
236 llvm::StringRef VarName = Lexer::getSourceText(
238 AppendCall->getImplicitObjectArgument()->getSourceRange()),
239 SM, Context->getLangOpts());
241 std::string ReserveSize;
246 StringRef RangeInitExpName =
248 RangeLoop->getRangeInit()->getSourceRange()),
249 SM, Context->getLangOpts());
250 ReserveSize = (RangeInitExpName +
".size()").str();
251 }
else if (ForLoop) {
253 StringRef LoopEndSource = Lexer::getSourceText(
255 Context->getLangOpts());
256 ReserveSize = LoopEndSource;
259 auto Diag =
diag(AppendCall->getBeginLoc(),
260 "%0 is called inside a loop; consider pre-allocating the " 261 "container capacity before the loop")
262 << AppendCall->getMethodDecl()->getDeclName();
263 if (!ReserveSize.empty()) {
264 std::string ReserveStmt =
265 (VarName + PartialReserveStmt +
"(" + ReserveSize +
");\n").str();
266 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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.