forked from OSchip/llvm-project
[clang-tidy] ForRangeCopyCheck that warns on and fixes unnecessary copies of loop variables.
Patch by Felix Berger! Differential revision: http://reviews.llvm.org/D13849 llvm-svn: 259199
This commit is contained in:
parent
bfee5f7352
commit
5aebfe2e4a
|
@ -214,8 +214,8 @@ void awesome_f2();
|
||||||
""" % {"check_name_dashes" : check_name_dashes})
|
""" % {"check_name_dashes" : check_name_dashes})
|
||||||
|
|
||||||
# Recreates the list of checks in the docs/clang-tidy/checks directory.
|
# Recreates the list of checks in the docs/clang-tidy/checks directory.
|
||||||
def update_checks_list(module_path):
|
def update_checks_list(clang_tidy_path):
|
||||||
docs_dir = os.path.join(module_path, '../../docs/clang-tidy/checks')
|
docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
|
||||||
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
|
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
|
||||||
with open(filename, 'r') as f:
|
with open(filename, 'r') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
@ -262,9 +262,17 @@ FIXME: Describe what patterns does the check detect and why. Give examples.
|
||||||
"underline" : "=" * len(check_name_dashes)})
|
"underline" : "=" * len(check_name_dashes)})
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
|
||||||
|
update_checks_list(os.path.dirname(sys.argv[0]))
|
||||||
|
return
|
||||||
|
|
||||||
if len(sys.argv) != 3:
|
if len(sys.argv) != 3:
|
||||||
print 'Usage: add_new_check.py <module> <check>, e.g.\n'
|
print """\
|
||||||
print 'add_new_check.py misc awesome-functions\n'
|
Usage: add_new_check.py <module> <check>, e.g.
|
||||||
|
add_new_check.py misc awesome-functions
|
||||||
|
|
||||||
|
Alternatively, run 'add_new_check.py --update-docs' to just update the list of
|
||||||
|
documentation files."""
|
||||||
return
|
return
|
||||||
|
|
||||||
module = sys.argv[1]
|
module = sys.argv[1]
|
||||||
|
@ -281,7 +289,7 @@ def main():
|
||||||
adapt_module(module_path, module, check_name, check_name_camel)
|
adapt_module(module_path, module, check_name, check_name_camel)
|
||||||
write_test(module_path, module, check_name)
|
write_test(module_path, module, check_name)
|
||||||
write_docs(module_path, module, check_name)
|
write_docs(module_path, module, check_name)
|
||||||
update_checks_list(module_path)
|
update_checks_list(clang_tidy_path)
|
||||||
print('Done. Now it\'s your turn!')
|
print('Done. Now it\'s your turn!')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
set(LLVM_LINK_COMPONENTS support)
|
set(LLVM_LINK_COMPONENTS support)
|
||||||
|
|
||||||
add_clang_library(clangTidyPerformanceModule
|
add_clang_library(clangTidyPerformanceModule
|
||||||
|
ForRangeCopyCheck.cpp
|
||||||
ImplicitCastInLoopCheck.cpp
|
ImplicitCastInLoopCheck.cpp
|
||||||
PerformanceTidyModule.cpp
|
PerformanceTidyModule.cpp
|
||||||
UnnecessaryCopyInitialization.cpp
|
UnnecessaryCopyInitialization.cpp
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
//===--- ForRangeCopyCheck.cpp - clang-tidy--------------------------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "ForRangeCopyCheck.h"
|
||||||
|
#include "../utils/LexerUtils.h"
|
||||||
|
#include "../utils/TypeTraits.h"
|
||||||
|
#include "clang/Lex/Lexer.h"
|
||||||
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tidy {
|
||||||
|
namespace performance {
|
||||||
|
|
||||||
|
using namespace ::clang::ast_matchers;
|
||||||
|
using llvm::SmallPtrSet;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
|
||||||
|
for (const auto &E : S1)
|
||||||
|
if (S2.count(E) == 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
|
||||||
|
template <typename Node>
|
||||||
|
void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
|
||||||
|
SmallPtrSet<const Node *, 16> &Nodes) {
|
||||||
|
for (const auto &Match : Matches)
|
||||||
|
Nodes.insert(Match.getNodeAs<Node>(ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds all DeclRefExprs to VarDecl in Stmt.
|
||||||
|
SmallPtrSet<const DeclRefExpr *, 16>
|
||||||
|
declRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
|
||||||
|
auto Matches = match(
|
||||||
|
findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
|
||||||
|
Stmt, Context);
|
||||||
|
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
|
||||||
|
extractNodesByIdTo(Matches, "declRef", DeclRefs);
|
||||||
|
return DeclRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
|
||||||
|
// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
|
||||||
|
SmallPtrSet<const DeclRefExpr *, 16>
|
||||||
|
constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
|
||||||
|
ASTContext &Context) {
|
||||||
|
auto DeclRefToVar =
|
||||||
|
declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
|
||||||
|
auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
|
||||||
|
// Match method call expressions where the variable is referenced as the this
|
||||||
|
// implicit object argument and opertor call expression for member operators
|
||||||
|
// where the variable is the 0-th argument.
|
||||||
|
auto Matches = match(
|
||||||
|
findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
|
||||||
|
cxxOperatorCallExpr(ConstMethodCallee,
|
||||||
|
hasArgument(0, DeclRefToVar))))),
|
||||||
|
Stmt, Context);
|
||||||
|
SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
|
||||||
|
extractNodesByIdTo(Matches, "declRef", DeclRefs);
|
||||||
|
auto ConstReferenceOrValue =
|
||||||
|
qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
|
||||||
|
unless(anyOf(referenceType(), pointerType()))));
|
||||||
|
auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
|
||||||
|
DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
|
||||||
|
Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
|
||||||
|
extractNodesByIdTo(Matches, "declRef", DeclRefs);
|
||||||
|
Matches =
|
||||||
|
match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
|
||||||
|
extractNodesByIdTo(Matches, "declRef", DeclRefs);
|
||||||
|
return DeclRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifies VarDecl to be a reference.
|
||||||
|
FixItHint createAmpersandFix(const VarDecl &VarDecl, ASTContext &Context) {
|
||||||
|
SourceLocation AmpLocation = VarDecl.getLocation();
|
||||||
|
auto Token = lexer_utils::getPreviousNonCommentToken(Context, AmpLocation);
|
||||||
|
if (!Token.is(tok::unknown))
|
||||||
|
AmpLocation = Token.getLocation().getLocWithOffset(Token.getLength());
|
||||||
|
return FixItHint::CreateInsertion(AmpLocation, "&");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifies VarDecl to be const.
|
||||||
|
FixItHint createConstFix(const VarDecl &VarDecl) {
|
||||||
|
return FixItHint::CreateInsertion(VarDecl.getTypeSpecStartLoc(), "const ");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
|
||||||
|
: ClangTidyCheck(Name, Context),
|
||||||
|
WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
|
||||||
|
|
||||||
|
void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||||
|
Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
|
||||||
|
// Match loop variables that are not references or pointers or are already
|
||||||
|
// initialized through MaterializeTemporaryExpr which indicates a type
|
||||||
|
// conversion.
|
||||||
|
auto LoopVar = varDecl(
|
||||||
|
hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
|
||||||
|
unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
|
||||||
|
Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
|
||||||
|
.bind("forRange"),
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
|
||||||
|
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
|
||||||
|
// Ignore code in macros since we can't place the fixes correctly.
|
||||||
|
if (Var->getLocStart().isMacroID())
|
||||||
|
return;
|
||||||
|
if (handleConstValueCopy(*Var, *Result.Context))
|
||||||
|
return;
|
||||||
|
const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
|
||||||
|
handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
|
||||||
|
ASTContext &Context) {
|
||||||
|
if (WarnOnAllAutoCopies) {
|
||||||
|
// For aggressive check just test that loop variable has auto type.
|
||||||
|
if (!isa<AutoType>(LoopVar.getType()))
|
||||||
|
return false;
|
||||||
|
} else if (!LoopVar.getType().isConstQualified()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!type_traits::isExpensiveToCopy(LoopVar.getType(), Context))
|
||||||
|
return false;
|
||||||
|
auto Diagnostic =
|
||||||
|
diag(LoopVar.getLocation(),
|
||||||
|
"the loop variable's type is not a reference type; this creates a "
|
||||||
|
"copy in each iteration; consider making this a reference")
|
||||||
|
<< createAmpersandFix(LoopVar, Context);
|
||||||
|
if (!LoopVar.getType().isConstQualified())
|
||||||
|
Diagnostic << createConstFix(LoopVar);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
|
||||||
|
const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
|
||||||
|
ASTContext &Context) {
|
||||||
|
if (LoopVar.getType().isConstQualified() ||
|
||||||
|
!type_traits::isExpensiveToCopy(LoopVar.getType(), Context)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Collect all DeclRefExprs to the loop variable and all CallExprs and
|
||||||
|
// CXXConstructExprs where the loop variable is used as argument to a const
|
||||||
|
// reference parameter.
|
||||||
|
// If the difference is empty it is safe for the loop variable to be a const
|
||||||
|
// reference.
|
||||||
|
auto AllDeclRefs = declRefExprs(LoopVar, *ForRange.getBody(), Context);
|
||||||
|
auto ConstReferenceDeclRefs =
|
||||||
|
constReferenceDeclRefExprs(LoopVar, *ForRange.getBody(), Context);
|
||||||
|
if (AllDeclRefs.empty() ||
|
||||||
|
!isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs))
|
||||||
|
return false;
|
||||||
|
diag(LoopVar.getLocation(),
|
||||||
|
"loop variable is copied but only used as const reference; consider "
|
||||||
|
"making it a const reference")
|
||||||
|
<< createConstFix(LoopVar) << createAmpersandFix(LoopVar, Context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace performance
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
|
@ -0,0 +1,49 @@
|
||||||
|
//===--- ForRangeCopyCheck.h - clang-tidy------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
|
||||||
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
|
||||||
|
|
||||||
|
#include "../ClangTidy.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace tidy {
|
||||||
|
namespace performance {
|
||||||
|
|
||||||
|
/// A check that detects copied loop variables and suggests using const
|
||||||
|
/// references.
|
||||||
|
/// For the user-facing documentation see:
|
||||||
|
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-for-range-copy.html
|
||||||
|
class ForRangeCopyCheck : public ClangTidyCheck {
|
||||||
|
public:
|
||||||
|
ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context);
|
||||||
|
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||||
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Checks if the loop variable is a const value and expensive to copy. If so
|
||||||
|
// suggests it be converted to a const reference.
|
||||||
|
bool handleConstValueCopy(const VarDecl &LoopVar, ASTContext &Context);
|
||||||
|
|
||||||
|
// Checks if the loop variable is a non-const value and whether only
|
||||||
|
// const methods are invoked on it or whether it is only used as a const
|
||||||
|
// reference argument. If so it suggests it be made a const reference.
|
||||||
|
bool handleCopyIsOnlyConstReferenced(const VarDecl &LoopVar,
|
||||||
|
const CXXForRangeStmt &ForRange,
|
||||||
|
ASTContext &Context);
|
||||||
|
|
||||||
|
const bool WarnOnAllAutoCopies;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace performance
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
|
|
@ -11,6 +11,7 @@
|
||||||
#include "../ClangTidyModule.h"
|
#include "../ClangTidyModule.h"
|
||||||
#include "../ClangTidyModuleRegistry.h"
|
#include "../ClangTidyModuleRegistry.h"
|
||||||
|
|
||||||
|
#include "ForRangeCopyCheck.h"
|
||||||
#include "ImplicitCastInLoopCheck.h"
|
#include "ImplicitCastInLoopCheck.h"
|
||||||
#include "UnnecessaryCopyInitialization.h"
|
#include "UnnecessaryCopyInitialization.h"
|
||||||
|
|
||||||
|
@ -21,6 +22,8 @@ namespace performance {
|
||||||
class PerformanceModule : public ClangTidyModule {
|
class PerformanceModule : public ClangTidyModule {
|
||||||
public:
|
public:
|
||||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||||
|
CheckFactories.registerCheck<ForRangeCopyCheck>(
|
||||||
|
"performance-for-range-copy");
|
||||||
CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
|
CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
|
||||||
"performance-implicit-cast-in-loop");
|
"performance-implicit-cast-in-loop");
|
||||||
CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
|
CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
|
||||||
|
|
|
@ -76,6 +76,7 @@ Clang-Tidy Checks
|
||||||
modernize-use-default
|
modernize-use-default
|
||||||
modernize-use-nullptr
|
modernize-use-nullptr
|
||||||
modernize-use-override
|
modernize-use-override
|
||||||
|
performance-for-range-copy
|
||||||
performance-implicit-cast-in-loop
|
performance-implicit-cast-in-loop
|
||||||
readability-braces-around-statements
|
readability-braces-around-statements
|
||||||
readability-container-size-empty
|
readability-container-size-empty
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.. title:: clang-tidy - performance-for-range-copy
|
||||||
|
|
||||||
|
performance-for-range-copy
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Finds C++11 for ranges where the loop variable is copied in each iteration but
|
||||||
|
it would suffice to obtain it by const reference.
|
||||||
|
|
||||||
|
The check is only applied to loop variables of types that are expensive to copy
|
||||||
|
which means they are not trivially copyable or have a non-trivial copy
|
||||||
|
constructor or destructor.
|
||||||
|
|
||||||
|
To ensure that it is safe to replace the copy with const reference the following
|
||||||
|
heuristic is employed:
|
||||||
|
|
||||||
|
1. The loop variable is const qualified.
|
||||||
|
2. The loop variable is not const, but only const methods or operators are
|
||||||
|
invoked on it, or it is used as const reference or value argument in
|
||||||
|
constructors or function calls.
|
|
@ -0,0 +1,39 @@
|
||||||
|
// RUN: %check_clang_tidy %s performance-for-range-copy %t -config="{CheckOptions: [{key: "performance-for-range-copy.WarnOnAllAutoCopies", value: 1}]}" -- -std=c++11
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Iterator {
|
||||||
|
void operator++() {}
|
||||||
|
const T& operator*() {
|
||||||
|
static T* TT = new T();
|
||||||
|
return *TT;
|
||||||
|
}
|
||||||
|
bool operator!=(const Iterator &) { return false; }
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct View {
|
||||||
|
T begin() { return T(); }
|
||||||
|
T begin() const { return T(); }
|
||||||
|
T end() { return T(); }
|
||||||
|
T end() const { return T(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
S();
|
||||||
|
S(const S &);
|
||||||
|
~S();
|
||||||
|
S &operator=(const S &);
|
||||||
|
};
|
||||||
|
|
||||||
|
void NegativeLoopVariableNotAuto() {
|
||||||
|
for (S S1 : View<Iterator<S>>()) {
|
||||||
|
S* S2 = &S1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PositiveTriggeredForAutoLoopVariable() {
|
||||||
|
for (auto S1 : View<Iterator<S>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& S1 : View<Iterator<S>>()) {
|
||||||
|
S* S2 = &S1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
// RUN: %check_clang_tidy %s performance-for-range-copy %t
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
struct remove_reference { typedef _Tp type; };
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) {
|
||||||
|
return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // std
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Iterator {
|
||||||
|
void operator++() {}
|
||||||
|
const T& operator*() {
|
||||||
|
static T* TT = new T();
|
||||||
|
return *TT;
|
||||||
|
}
|
||||||
|
bool operator!=(const Iterator &) { return false; }
|
||||||
|
typedef const T& const_reference;
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct View {
|
||||||
|
T begin() { return T(); }
|
||||||
|
T begin() const { return T(); }
|
||||||
|
T end() { return T(); }
|
||||||
|
T end() const { return T(); }
|
||||||
|
typedef typename T::const_reference const_reference;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstructorConvertible {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
S();
|
||||||
|
S(const S &);
|
||||||
|
S(const ConstructorConvertible&) {}
|
||||||
|
~S();
|
||||||
|
S &operator=(const S &);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Convertible {
|
||||||
|
operator S() const {
|
||||||
|
return S();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void negativeConstReference() {
|
||||||
|
for (const S &S1 : View<Iterator<S>>()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeUserDefinedConversion() {
|
||||||
|
Convertible C[0];
|
||||||
|
for (const S &S1 : C) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeImplicitConstructorConversion() {
|
||||||
|
ConstructorConvertible C[0];
|
||||||
|
for (const S &S1 : C) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void uninstantiated() {
|
||||||
|
for (const S S1 : View<Iterator<S>>()) {}
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: {{^}} for (const S& S1 : View<Iterator<S>>()) {}
|
||||||
|
|
||||||
|
// Don't warn on dependent types.
|
||||||
|
for (const T t1 : View<Iterator<T>>()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void instantiated() {
|
||||||
|
for (const S S2 : View<Iterator<S>>()) {}
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
|
||||||
|
// CHECK-FIXES: {{^}} for (const S& S2 : View<Iterator<S>>()) {}
|
||||||
|
|
||||||
|
for (const T T2 : View<Iterator<T>>()) {}
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
|
||||||
|
// CHECK-FIXES: {{^}} for (const T& T2 : View<Iterator<T>>()) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void instantiatedNegativeTypedefConstReference() {
|
||||||
|
for (typename T::const_reference T2 : T()) {
|
||||||
|
S S1 = T2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
instantiated<int>();
|
||||||
|
instantiated<S>();
|
||||||
|
instantiatedNegativeTypedefConstReference<View<Iterator<S>>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mutable {
|
||||||
|
Mutable() {}
|
||||||
|
Mutable(const Mutable &) = default;
|
||||||
|
Mutable(const Mutable &, const Mutable &) {}
|
||||||
|
void setBool(bool B) {}
|
||||||
|
bool constMethod() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Mutable& operator[](int I) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(const Mutable &Other) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
~Mutable() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Mutable& operator<<(Mutable &Out, bool B) {
|
||||||
|
Out.setBool(B);
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Mutable& M1, const Mutable& M2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void use(const Mutable &M);
|
||||||
|
void useTwice(const Mutable &M1, const Mutable &M2);
|
||||||
|
void useByValue(Mutable M);
|
||||||
|
void useByConstValue(const Mutable M);
|
||||||
|
void mutate(Mutable *M);
|
||||||
|
void mutate(Mutable &M);
|
||||||
|
void onceConstOnceMutated(const Mutable &M1, Mutable &M2);
|
||||||
|
|
||||||
|
void negativeVariableIsMutated() {
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
mutate(M);
|
||||||
|
}
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
mutate(&M);
|
||||||
|
}
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
M.setBool(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeOnceConstOnceMutated() {
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
onceConstOnceMutated(M, M);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeVarIsMoved() {
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
auto Moved = std::move(M);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeNonConstOperatorIsInvoked() {
|
||||||
|
for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
auto& N = NonConstOperatorInvokee[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeNonConstNonMemberOperatorInvoked() {
|
||||||
|
for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
NonConstOperatorInvokee << true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void positiveOnlyConstMethodInvoked() {
|
||||||
|
for (auto M : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& M : View<Iterator<Mutable>>()) {
|
||||||
|
M.constMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void positiveOnlyUsedAsConstArguments() {
|
||||||
|
for (auto UsedAsConst : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& UsedAsConst : View<Iterator<Mutable>>()) {
|
||||||
|
use(UsedAsConst);
|
||||||
|
useTwice(UsedAsConst, UsedAsConst);
|
||||||
|
useByValue(UsedAsConst);
|
||||||
|
useByConstValue(UsedAsConst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void positiveOnlyUsedInCopyConstructor() {
|
||||||
|
for (auto A : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
|
||||||
|
Mutable Copy = A;
|
||||||
|
Mutable Copy2(A);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void positiveTwoConstConstructorArgs() {
|
||||||
|
for (auto A : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
|
||||||
|
Mutable Copy(A, A);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PositiveConstMemberOperatorInvoked() {
|
||||||
|
for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
bool result = ConstOperatorInvokee == Mutable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PositiveConstNonMemberOperatorInvoked() {
|
||||||
|
for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
|
||||||
|
// CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
|
||||||
|
bool result = ConstOperatorInvokee != Mutable();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue