blob: a2207d395a6ab63896be5d467daeab00f037d2a7 [file] [log] [blame]
Haojian Wuc5cc0332017-04-18 07:46:39 +00001//===--- InefficientVectorOperationCheck.cpp - clang-tidy------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "InefficientVectorOperationCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14#include "../utils/DeclRefExprUtils.h"
15
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
20namespace performance {
21
22namespace {
23
24// Matcher names. Given the code:
25//
26// \code
27// void f() {
28// vector<T> v;
29// for (int i = 0; i < 10 + 1; ++i) {
30// v.push_back(i);
31// }
32// }
33// \endcode
34//
35// The matcher names are bound to following parts of the AST:
36// - LoopName: The entire for loop (as ForStmt).
37// - LoopParentName: The body of function f (as CompoundStmt).
38// - VectorVarDeclName: 'v' in (as VarDecl).
39// - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
40// DeclStmt).
41// - PushBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr).
42// - LoopInitVarName: 'i' (as VarDecl).
43// - LoopEndExpr: '10+1' (as Expr).
44static const char LoopCounterName[] = "for_loop_counter";
45static const char LoopParentName[] = "loop_parent";
46static const char VectorVarDeclName[] = "vector_var_decl";
47static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
48static const char PushBackCallName[] = "push_back_call";
49
50static const char LoopInitVarName[] = "loop_init_var";
51static const char LoopEndExprName[] = "loop_end_expr";
52
53} // namespace
54
55void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
56 const auto VectorDecl = cxxRecordDecl(hasName("::std::vector"));
57 const auto VectorDefaultConstructorCall = cxxConstructExpr(
58 hasType(VectorDecl),
59 hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
60 const auto VectorVarDecl =
61 varDecl(hasInitializer(VectorDefaultConstructorCall))
62 .bind(VectorVarDeclName);
63 const auto PushBackCallExpr =
64 cxxMemberCallExpr(
65 callee(cxxMethodDecl(hasName("push_back"))), on(hasType(VectorDecl)),
66 onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
67 .bind(PushBackCallName);
68 const auto PushBackCall =
69 expr(anyOf(PushBackCallExpr, exprWithCleanups(has(PushBackCallExpr))));
70 const auto VectorVarDefStmt =
71 declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
72 .bind(VectorVarDeclStmtName);
73
74 const auto LoopVarInit =
75 declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
76 .bind(LoopInitVarName)));
77 const auto RefersToLoopVar = ignoringParenImpCasts(
78 declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
79
80 // Match counter-based for loops:
81 // for (int i = 0; i < n; ++i) { v.push_back(...); }
82 //
83 // FIXME: Support more types of counter-based loops like decrement loops.
84 Finder->addMatcher(
85 forStmt(
86 hasLoopInit(LoopVarInit),
87 hasCondition(binaryOperator(
88 hasOperatorName("<"), hasLHS(RefersToLoopVar),
89 hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
90 .bind(LoopEndExprName)))),
91 hasIncrement(unaryOperator(hasOperatorName("++"),
92 hasUnaryOperand(RefersToLoopVar))),
93 hasBody(anyOf(compoundStmt(statementCountIs(1), has(PushBackCall)),
94 PushBackCall)),
95 hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName)))
96 .bind(LoopCounterName),
97 this);
98}
99
100void InefficientVectorOperationCheck::check(
101 const MatchFinder::MatchResult &Result) {
Haojian Wuc2c22832017-04-18 20:47:34 +0000102 auto* Context = Result.Context;
103 if (Context->getDiagnostics().hasUncompilableErrorOccurred())
Haojian Wuc5cc0332017-04-18 07:46:39 +0000104 return;
105
106 const SourceManager &SM = *Result.SourceManager;
107 const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
108 const auto *PushBackCall =
109 Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackCallName);
110 const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
111 const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
112 const auto *VectorVarDecl =
113 Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
114
115 llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
116 utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
Haojian Wuc2c22832017-04-18 20:47:34 +0000117 *Context);
Haojian Wuc5cc0332017-04-18 07:46:39 +0000118 for (const auto *Ref : AllVectorVarRefs) {
119 // Skip cases where there are usages (defined as DeclRefExpr that refers to
120 // "v") of vector variable `v` before the for loop. We consider these usages
121 // are operations causing memory preallocation (e.g. "v.resize(n)",
122 // "v.reserve(n)").
123 //
124 // FIXME: make it more intelligent to identify the pre-allocating operations
125 // before the for loop.
126 if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
127 ForLoop->getLocStart())) {
128 return;
129 }
130 }
131
Haojian Wuc2c22832017-04-18 20:47:34 +0000132 llvm::StringRef LoopEndSource = Lexer::getSourceText(
Haojian Wuc5cc0332017-04-18 07:46:39 +0000133 CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
Haojian Wuc2c22832017-04-18 20:47:34 +0000134 Context->getLangOpts());
135 llvm::StringRef VectorVarName = Lexer::getSourceText(
Haojian Wuc5cc0332017-04-18 07:46:39 +0000136 CharSourceRange::getTokenRange(
137 PushBackCall->getImplicitObjectArgument()->getSourceRange()),
Haojian Wuc2c22832017-04-18 20:47:34 +0000138 SM, Context->getLangOpts());
Haojian Wuc5cc0332017-04-18 07:46:39 +0000139 std::string ReserveStmt =
140 (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
141
142 diag(PushBackCall->getLocStart(),
143 "'push_back' is called inside a loop; "
Haojian Wuc2c22832017-04-18 20:47:34 +0000144 "consider pre-allocating the vector capacity before the loop")
Haojian Wuc5cc0332017-04-18 07:46:39 +0000145 << FixItHint::CreateInsertion(ForLoop->getLocStart(), ReserveStmt);
146}
147
148} // namespace performance
149} // namespace tidy
150} // namespace clang