[clang-tools-extra] r301440 - [clang-tidy] Support detecting for-range loop in inefficient-vector-operation check.
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 26 11:13:06 PDT 2017
Author: hokein
Date: Wed Apr 26 13:13:05 2017
New Revision: 301440
URL: http://llvm.org/viewvc/llvm-project?rev=301440&view=rev
Log:
[clang-tidy] Support detecting for-range loop in inefficient-vector-operation check.
Summary:
Also add an option "VectorLikeClasses" allowing user specify customized
vectors.
Reviewers: alexfh, aaron.ballman
Reviewed By: alexfh
Subscribers: Eugene.Zelenko, cfe-commits
Differential Revision: https://reviews.llvm.org/D32436
Modified:
clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.cpp
clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.h
clang-tools-extra/trunk/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst
clang-tools-extra/trunk/test/clang-tidy/performance-inefficient-vector-operation.cpp
Modified: clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.cpp?rev=301440&r1=301439&r2=301440&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.cpp Wed Apr 26 13:13:05 2017
@@ -12,6 +12,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "../utils/DeclRefExprUtils.h"
+#include "../utils/OptionsUtils.h"
using namespace clang::ast_matchers;
@@ -33,7 +34,7 @@ namespace {
// \endcode
//
// The matcher names are bound to following parts of the AST:
-// - LoopName: The entire for loop (as ForStmt).
+// - LoopCounterName: The entire for loop (as ForStmt).
// - LoopParentName: The body of function f (as CompoundStmt).
// - VectorVarDeclName: 'v' in (as VarDecl).
// - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
@@ -46,14 +47,34 @@ static const char LoopParentName[] = "lo
static const char VectorVarDeclName[] = "vector_var_decl";
static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
static const char PushBackCallName[] = "push_back_call";
-
static const char LoopInitVarName[] = "loop_init_var";
static const char LoopEndExprName[] = "loop_end_expr";
+static const char RangeLoopName[] = "for_range_loop";
+
+ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
+ return hasType(cxxRecordDecl(hasAnyName(
+ "::std::vector", "::std::set", "::std::unordered_set", "::std::map",
+ "::std::unordered_map", "::std::array", "::std::dequeue")));
+}
+
} // namespace
+InefficientVectorOperationCheck::InefficientVectorOperationCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ VectorLikeClasses(utils::options::parseStringList(
+ Options.get("VectorLikeClasses", "::std::vector"))) {}
+
+void InefficientVectorOperationCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "VectorLikeClasses",
+ utils::options::serializeStringList(VectorLikeClasses));
+}
+
void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
- const auto VectorDecl = cxxRecordDecl(hasName("::std::vector"));
+ const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
+ VectorLikeClasses.begin(), VectorLikeClasses.end())));
const auto VectorDefaultConstructorCall = cxxConstructExpr(
hasType(VectorDecl),
hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
@@ -77,6 +98,12 @@ void InefficientVectorOperationCheck::re
const auto RefersToLoopVar = ignoringParenImpCasts(
declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
+ // Matchers for the loop whose body has only 1 push_back calling statement.
+ const auto HasInterestingLoopBody = hasBody(anyOf(
+ compoundStmt(statementCountIs(1), has(PushBackCall)), PushBackCall));
+ const auto InInterestingCompoundStmt =
+ hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
+
// Match counter-based for loops:
// for (int i = 0; i < n; ++i) { v.push_back(...); }
//
@@ -90,11 +117,20 @@ void InefficientVectorOperationCheck::re
.bind(LoopEndExprName)))),
hasIncrement(unaryOperator(hasOperatorName("++"),
hasUnaryOperand(RefersToLoopVar))),
- hasBody(anyOf(compoundStmt(statementCountIs(1), has(PushBackCall)),
- PushBackCall)),
- hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName)))
+ HasInterestingLoopBody, InInterestingCompoundStmt)
.bind(LoopCounterName),
this);
+
+ // Match for-range loops:
+ // for (const auto& E : data) { v.push_back(...); }
+ //
+ // FIXME: Support more complex range-expressions.
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
+ HasInterestingLoopBody, InInterestingCompoundStmt)
+ .bind(RangeLoopName),
+ this);
}
void InefficientVectorOperationCheck::check(
@@ -104,13 +140,19 @@ void InefficientVectorOperationCheck::ch
return;
const SourceManager &SM = *Result.SourceManager;
+ const auto *VectorVarDecl =
+ Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
+ const auto *RangeLoop =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
const auto *PushBackCall =
Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackCallName);
const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
- const auto *VectorVarDecl =
- Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
+
+ const Stmt *LoopStmt = ForLoop;
+ if (!LoopStmt)
+ LoopStmt = RangeLoop;
llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
@@ -124,25 +166,43 @@ void InefficientVectorOperationCheck::ch
// FIXME: make it more intelligent to identify the pre-allocating operations
// before the for loop.
if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
- ForLoop->getLocStart())) {
+ LoopStmt->getLocStart())) {
return;
}
}
- llvm::StringRef LoopEndSource = Lexer::getSourceText(
- CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
- Context->getLangOpts());
llvm::StringRef VectorVarName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
PushBackCall->getImplicitObjectArgument()->getSourceRange()),
SM, Context->getLangOpts());
- std::string ReserveStmt =
- (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
- diag(PushBackCall->getLocStart(),
+ std::string ReserveStmt;
+ // Handle for-range loop cases.
+ if (RangeLoop) {
+ // Get the range-expression in a for-range statement represented as
+ // `for (range-declarator: range-expression)`.
+ StringRef RangeInitExpName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ RangeLoop->getRangeInit()->getSourceRange()),
+ SM, Context->getLangOpts());
+
+ ReserveStmt =
+ (VectorVarName + ".reserve(" + RangeInitExpName + ".size()" + ");\n")
+ .str();
+ } else if (ForLoop) {
+ // Handle counter-based loop cases.
+ StringRef LoopEndSource = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
+ Context->getLangOpts());
+ ReserveStmt = (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
+ }
+
+ auto Diag = diag(PushBackCall->getLocStart(),
"'push_back' is called inside a loop; "
- "consider pre-allocating the vector capacity before the loop")
- << FixItHint::CreateInsertion(ForLoop->getLocStart(), ReserveStmt);
+ "consider pre-allocating the vector capacity before the loop");
+
+ if (!ReserveStmt.empty())
+ Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt);
}
} // namespace performance
Modified: clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.h?rev=301440&r1=301439&r2=301440&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.h (original)
+++ clang-tools-extra/trunk/clang-tidy/performance/InefficientVectorOperationCheck.h Wed Apr 26 13:13:05 2017
@@ -23,10 +23,13 @@ namespace performance {
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html
class InefficientVectorOperationCheck : public ClangTidyCheck {
public:
- InefficientVectorOperationCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ InefficientVectorOperationCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ const std::vector<std::string> VectorLikeClasses;
};
} // namespace performance
Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst?rev=301440&r1=301439&r2=301440&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst Wed Apr 26 13:13:05 2017
@@ -3,11 +3,13 @@
performance-inefficient-vector-operation
========================================
-Finds possible inefficient `std::vector` operations (e.g. `push_back`) that may
-cause unnecessary memory reallocations.
+Finds possible inefficient ``std::vector`` operations (e.g. ``push_back``) that
+may cause unnecessary memory reallocations.
-Currently, the check only detects a typical counter-based for loop with a single
-statement in it, see below:
+Currently, the check only detects following kinds of loops with a single
+statement body:
+
+* Counter-based for loops start with 0:
.. code-block:: c++
@@ -18,3 +20,30 @@ statement in it, see below:
// memory reallocations in v. This can be avoid by inserting a 'reserve(n)'
// statment before the for statment.
}
+
+
+* For-range loops like ``for (range-declaration : range_expression)``, the type
+ of ``range_expression`` can be ``std::vector``, ``std::array``,
+ ``std::dequeue``, ``std::set``, ``std::unordered_set``, ``std::map``,
+ ``std::unordered_set``:
+
+.. code-block:: c++
+
+ std::vector<int> data;
+ std::vector<int> v;
+
+ for (auto element : data) {
+ v.push_back(element);
+ // This will trigger the warning since the 'push_back' may cause multiple
+ // memory reallocations in v. This can be avoid by inserting a
+ // 'reserve(data.size())' statment before the for statment.
+ }
+
+
+Options
+-------
+
+.. option:: VectorLikeClasses
+
+ Semicolon-separated list of names of vector-like classes. By default only
+ ``::std::vector`` is considered.
Modified: clang-tools-extra/trunk/test/clang-tidy/performance-inefficient-vector-operation.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/performance-inefficient-vector-operation.cpp?rev=301440&r1=301439&r2=301440&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/performance-inefficient-vector-operation.cpp (original)
+++ clang-tools-extra/trunk/test/clang-tidy/performance-inefficient-vector-operation.cpp Wed Apr 26 13:13:05 2017
@@ -1,9 +1,27 @@
-// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm --
+// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm -- --std=c++11
namespace std {
typedef int size_t;
+template<class E> class initializer_list {
+public:
+ using value_type = E;
+ using reference = E&;
+ using const_reference = const E&;
+ using size_type = size_t;
+ using iterator = const E*;
+ using const_iterator = const E*;
+ initializer_list();
+ size_t size() const; // number of elements
+ const E* begin() const; // first element
+ const E* end() const; // one past the last element
+};
+
+// initializer list range access
+template<class E> const E* begin(initializer_list<E> il);
+template<class E> const E* end(initializer_list<E> il);
+
template <class T>
class vector {
public:
@@ -23,9 +41,24 @@ class vector {
size_t size();
const_reference operator[] (size_type) const;
reference operator[] (size_type);
+
+ const_iterator begin() const;
+ const_iterator end() const;
};
} // namespace std
+class Foo {
+ public:
+ explicit Foo(int);
+};
+
+class Bar {
+ public:
+ Bar(int);
+};
+
+int Op(int);
+
void f(std::vector<int>& t) {
{
std::vector<int> v;
@@ -85,7 +118,38 @@ void f(std::vector<int>& t) {
v.push_back(t[i]);
} // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
}
-
+ {
+ std::vector<int> v;
+ // CHECK-FIXES: v.reserve(t.size());
+ for (const auto &e : t) {
+ v.push_back(e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<int> v;
+ // CHECK-FIXES: v.reserve(t.size());
+ for (const auto &e : t) {
+ v.push_back(Op(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<Foo> v;
+ // CHECK-FIXES: v.reserve(t.size());
+ for (const auto &e : t) {
+ v.push_back(Foo(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<Bar> v;
+ // CHECK-FIXES: v.reserve(t.size());
+ for (const auto &e : t) {
+ v.push_back(e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
// ---- Non-fixed Cases ----
{
std::vector<int> v;
@@ -181,4 +245,21 @@ void f(std::vector<int>& t) {
v.push_back(t[i]);
}
}
+ {
+ std::vector<int> v;
+ // initializer_list should not trigger the check.
+ for (int e : {1, 2, 3, 4, 5}) {
+ v.push_back(e);
+ }
+ }
+ {
+ std::vector<int> v;
+ std::vector<int>* v2 = &t;
+ // We only support detecting the range init expression which references
+ // container directly.
+ // Complex range init expressions like `*v2` is not supported.
+ for (const auto &e : *v2) {
+ v.push_back(e);
+ }
+ }
}
More information about the cfe-commits
mailing list