Initial check for recommending algorithms (#1352)
Add initial check for loop algorithms
This commit is contained in:
parent
83cb0b3394
commit
1e347f6cde
|
@ -5,6 +5,7 @@ nullPointer:lib/checkother.cpp
|
||||||
nullPointer:build/checkother.cpp
|
nullPointer:build/checkother.cpp
|
||||||
missingOverride
|
missingOverride
|
||||||
noValidConfiguration
|
noValidConfiguration
|
||||||
|
useStlAlgorithm
|
||||||
|
|
||||||
*:gui/test/*
|
*:gui/test/*
|
||||||
*:test/test.cxx
|
*:test/test.cxx
|
||||||
|
|
340
lib/checkstl.cpp
340
lib/checkstl.cpp
|
@ -1746,3 +1746,343 @@ void CheckStl::readingEmptyStlContainerError(const Token *tok, const ValueFlow::
|
||||||
|
|
||||||
reportError(errorPath, value ? (value->errorSeverity() ? Severity::error : Severity::warning) : Severity::style, "reademptycontainer", "$symbol:" + varname +"\n" + errmsg, CWE398, !value);
|
reportError(errorPath, value ? (value->errorSeverity() ? Severity::error : Severity::warning) : Severity::style, "reademptycontainer", "$symbol:" + varname +"\n" + errmsg, CWE398, !value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckStl::useStlAlgorithmError(const Token *tok, const std::string &algoName)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::style, "useStlAlgorithm",
|
||||||
|
"Consider using " + algoName + " algorithm instead of a raw loop.", CWE398, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isEarlyExit(const Token *start)
|
||||||
|
{
|
||||||
|
if (start->str() != "{")
|
||||||
|
return false;
|
||||||
|
const Token *endToken = start->link();
|
||||||
|
const Token *tok = Token::findmatch(start, "return|throw|break", endToken);
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
const Token *endStatement = Token::findsimplematch(tok, "; }", endToken);
|
||||||
|
if (!endStatement)
|
||||||
|
return false;
|
||||||
|
if (endStatement->next() != endToken)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token *singleStatement(const Token *start)
|
||||||
|
{
|
||||||
|
if (start->str() != "{")
|
||||||
|
return nullptr;
|
||||||
|
const Token *endToken = start->link();
|
||||||
|
const Token *endStatement = Token::findsimplematch(start->next(), ";");
|
||||||
|
if (!Token::simpleMatch(endStatement, "; }"))
|
||||||
|
return nullptr;
|
||||||
|
if (endStatement->next() != endToken)
|
||||||
|
return nullptr;
|
||||||
|
return endStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token *singleAssignInScope(const Token *start, unsigned int varid, bool &input)
|
||||||
|
{
|
||||||
|
const Token *endStatement = singleStatement(start);
|
||||||
|
if (!endStatement)
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::Match(start->next(), "%var% %assign%"))
|
||||||
|
return nullptr;
|
||||||
|
const Token *assignTok = start->tokAt(2);
|
||||||
|
if (isVariableChanged(assignTok->next(), endStatement, assignTok->astOperand1()->varId(), false, nullptr, true))
|
||||||
|
return nullptr;
|
||||||
|
if (isVariableChanged(assignTok->next(), endStatement, varid, false, nullptr, true))
|
||||||
|
return nullptr;
|
||||||
|
input = Token::findmatch(assignTok->next(), "%varid%", endStatement, varid);
|
||||||
|
return assignTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token *singleMemberCallInScope(const Token *start, unsigned int varid, bool &input)
|
||||||
|
{
|
||||||
|
if (start->str() != "{")
|
||||||
|
return nullptr;
|
||||||
|
const Token *endToken = start->link();
|
||||||
|
if (!Token::Match(start->next(), "%var% . %name% ("))
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::simpleMatch(start->linkAt(4), ") ; }"))
|
||||||
|
return nullptr;
|
||||||
|
const Token *endStatement = start->linkAt(4)->next();
|
||||||
|
if (endStatement->next() != endToken)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const Token *dotTok = start->tokAt(2);
|
||||||
|
if (!Token::findmatch(dotTok->tokAt(2), "%varid%", endStatement, varid))
|
||||||
|
return nullptr;
|
||||||
|
input = Token::Match(start->next(), "%var% . %name% ( %varid% )", varid);
|
||||||
|
if (isVariableChanged(dotTok->next(), endStatement, dotTok->astOperand1()->varId(), false, nullptr, true))
|
||||||
|
return nullptr;
|
||||||
|
return dotTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token *singleIncrementInScope(const Token *start, unsigned int varid, bool &input)
|
||||||
|
{
|
||||||
|
if (start->str() != "{")
|
||||||
|
return nullptr;
|
||||||
|
const Token *varTok = nullptr;
|
||||||
|
if (Token::Match(start->next(), "++ %var% ; }"))
|
||||||
|
varTok = start->tokAt(2);
|
||||||
|
else if (Token::Match(start->next(), "%var% ++ ; }"))
|
||||||
|
varTok = start->tokAt(1);
|
||||||
|
if (!varTok)
|
||||||
|
return nullptr;
|
||||||
|
input = varTok->varId() == varid;
|
||||||
|
return varTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token *singleConditionalInScope(const Token *start, unsigned int varid)
|
||||||
|
{
|
||||||
|
if (start->str() != "{")
|
||||||
|
return nullptr;
|
||||||
|
const Token *endToken = start->link();
|
||||||
|
if (!Token::simpleMatch(start->next(), "if ("))
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::simpleMatch(start->linkAt(2), ") {"))
|
||||||
|
return nullptr;
|
||||||
|
const Token *bodyTok = start->linkAt(2)->next();
|
||||||
|
const Token *endBodyTok = bodyTok->link();
|
||||||
|
if (!Token::simpleMatch(endBodyTok, "} }"))
|
||||||
|
return nullptr;
|
||||||
|
if (endBodyTok->next() != endToken)
|
||||||
|
return nullptr;
|
||||||
|
if (!Token::findmatch(start, "%varid%", bodyTok, varid))
|
||||||
|
return nullptr;
|
||||||
|
if (isVariableChanged(start, bodyTok, varid, false, nullptr, true))
|
||||||
|
return nullptr;
|
||||||
|
return bodyTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool addByOne(const Token *tok, unsigned int varid)
|
||||||
|
{
|
||||||
|
if (Token::Match(tok, "+= %any% ;") &&
|
||||||
|
tok->tokAt(1)->hasKnownIntValue() &&
|
||||||
|
tok->tokAt(1)->getValue(1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok, "= %varid% + %any% ;", varid) &&
|
||||||
|
tok->tokAt(3)->hasKnownIntValue() &&
|
||||||
|
tok->tokAt(3)->getValue(1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool accumulateBoolLiteral(const Token *tok, unsigned int varid)
|
||||||
|
{
|
||||||
|
// TODO: Missing %oreq%
|
||||||
|
if (Token::Match(tok, "=|&= %bool% ;") &&
|
||||||
|
tok->tokAt(1)->hasKnownIntValue()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok, "= %varid% %oror%|%or%|&&|& %bool% ;", varid) &&
|
||||||
|
tok->tokAt(3)->hasKnownIntValue()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool accumulateBool(const Token *tok, unsigned int varid)
|
||||||
|
{
|
||||||
|
// TODO: Missing %oreq%
|
||||||
|
if (Token::simpleMatch(tok, "&=")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok, "= %varid% %oror%|%or%|&&|&", varid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasVarIds(const Token *tok, unsigned int var1, unsigned int var2)
|
||||||
|
{
|
||||||
|
if (tok->astOperand1()->varId() == tok->astOperand2()->varId())
|
||||||
|
return false;
|
||||||
|
if (tok->astOperand1()->varId() == var1 || tok->astOperand1()->varId() == var2) {
|
||||||
|
if (tok->astOperand2()->varId() == var1 || tok->astOperand2()->varId() == var2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string flipMinMax(const std::string &algo)
|
||||||
|
{
|
||||||
|
if (algo == "std::max_element")
|
||||||
|
return "std::min_element";
|
||||||
|
if (algo == "std::min_element")
|
||||||
|
return "std::max_element";
|
||||||
|
return algo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string minmaxCompare(const Token *condTok, unsigned int loopVar, unsigned int assignVar, bool invert = false)
|
||||||
|
{
|
||||||
|
if (!Token::Match(condTok, "<|<=|>=|>"))
|
||||||
|
return "std::accumulate";
|
||||||
|
if (!hasVarIds(condTok, loopVar, assignVar))
|
||||||
|
return "std::accumulate";
|
||||||
|
std::string algo = "std::max_element";
|
||||||
|
if (Token::Match(condTok, "<|<="))
|
||||||
|
algo = "std::min_element";
|
||||||
|
if (condTok->astOperand1()->varId() == assignVar)
|
||||||
|
algo = flipMinMax(algo);
|
||||||
|
if (invert)
|
||||||
|
algo = flipMinMax(algo);
|
||||||
|
return algo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckStl::useStlAlgorithm()
|
||||||
|
{
|
||||||
|
if (!mSettings->isEnabled(Settings::STYLE))
|
||||||
|
return;
|
||||||
|
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||||
|
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
|
||||||
|
// Parse range-based for loop
|
||||||
|
if (!Token::simpleMatch(tok, "for ("))
|
||||||
|
continue;
|
||||||
|
if (!Token::simpleMatch(tok->next()->link(), ") {"))
|
||||||
|
continue;
|
||||||
|
const Token *bodyTok = tok->next()->link()->next();
|
||||||
|
const Token *splitTok = tok->next()->astOperand2();
|
||||||
|
if (!Token::simpleMatch(splitTok, ":"))
|
||||||
|
continue;
|
||||||
|
const Token *loopVar = splitTok->previous();
|
||||||
|
if (!Token::Match(loopVar, "%var%"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check for single assignment
|
||||||
|
bool useLoopVarInAssign;
|
||||||
|
const Token *assignTok = singleAssignInScope(bodyTok, loopVar->varId(), useLoopVarInAssign);
|
||||||
|
if (assignTok) {
|
||||||
|
unsigned int assignVarId = assignTok->astOperand1()->varId();
|
||||||
|
std::string algo;
|
||||||
|
if (assignVarId == loopVar->varId()) {
|
||||||
|
if (useLoopVarInAssign)
|
||||||
|
algo = "std::transform";
|
||||||
|
else if (Token::Match(assignTok->next(), "%var%|%bool%|%num%|%char% ;"))
|
||||||
|
algo = "std::fill";
|
||||||
|
else if (Token::Match(assignTok->next(), "%name% ( )"))
|
||||||
|
algo = "std::generate";
|
||||||
|
else
|
||||||
|
algo = "std::fill or std::generate";
|
||||||
|
} else {
|
||||||
|
if (addByOne(assignTok, assignVarId))
|
||||||
|
algo = "std::distance";
|
||||||
|
else if (accumulateBool(assignTok, assignVarId))
|
||||||
|
algo = "std::any_of, std::all_of, std::none_of, or std::accumulate";
|
||||||
|
else if (Token::Match(assignTok, "= %var% <|<=|>=|> %var% ? %var% : %var%") && hasVarIds(assignTok->tokAt(6), loopVar->varId(), assignVarId))
|
||||||
|
algo = minmaxCompare(assignTok->tokAt(2), loopVar->varId(), assignVarId, assignTok->tokAt(5)->varId() == assignVarId);
|
||||||
|
else
|
||||||
|
algo = "std::accumulate";
|
||||||
|
}
|
||||||
|
useStlAlgorithmError(assignTok, algo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check for container calls
|
||||||
|
bool useLoopVarInMemCall;
|
||||||
|
const Token *memberAccessTok = singleMemberCallInScope(bodyTok, loopVar->varId(), useLoopVarInMemCall);
|
||||||
|
if (memberAccessTok) {
|
||||||
|
const Token *memberCallTok = memberAccessTok->astOperand2();
|
||||||
|
unsigned int contVarId = memberAccessTok->astOperand1()->varId();
|
||||||
|
if (contVarId == loopVar->varId())
|
||||||
|
continue;
|
||||||
|
if (memberCallTok->str() == "push_back" ||
|
||||||
|
memberCallTok->str() == "push_front" ||
|
||||||
|
memberCallTok->str() == "emplace_back") {
|
||||||
|
std::string algo;
|
||||||
|
if (useLoopVarInMemCall)
|
||||||
|
algo = "std::copy";
|
||||||
|
else
|
||||||
|
algo = "std::transform";
|
||||||
|
useStlAlgorithmError(assignTok, algo);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for increment in loop
|
||||||
|
bool useLoopVarInIncrement;
|
||||||
|
const Token *incrementTok = singleIncrementInScope(bodyTok, loopVar->varId(), useLoopVarInIncrement);
|
||||||
|
if (incrementTok) {
|
||||||
|
std::string algo;
|
||||||
|
if (useLoopVarInIncrement)
|
||||||
|
algo = "std::transform";
|
||||||
|
else
|
||||||
|
algo = "std::distance";
|
||||||
|
useStlAlgorithmError(incrementTok, algo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for conditionals
|
||||||
|
const Token *condBodyTok = singleConditionalInScope(bodyTok, loopVar->varId());
|
||||||
|
if (condBodyTok) {
|
||||||
|
const Token *beginCondTok = condBodyTok->previous()->link();
|
||||||
|
// Check for single assign
|
||||||
|
assignTok = singleAssignInScope(condBodyTok, loopVar->varId(), useLoopVarInAssign);
|
||||||
|
if (assignTok) {
|
||||||
|
unsigned int assignVarId = assignTok->astOperand1()->varId();
|
||||||
|
std::string algo;
|
||||||
|
if (assignVarId == loopVar->varId()) {
|
||||||
|
if (useLoopVarInAssign)
|
||||||
|
algo = "std::transform";
|
||||||
|
else
|
||||||
|
algo = "std::replace_if";
|
||||||
|
} else {
|
||||||
|
if (addByOne(assignTok, assignVarId))
|
||||||
|
algo = "std::count_if";
|
||||||
|
else if (accumulateBoolLiteral(assignTok, assignVarId))
|
||||||
|
algo = "std::any_of, std::all_of, std::none_of, or std::accumulate";
|
||||||
|
else
|
||||||
|
algo = "std::accumulate";
|
||||||
|
}
|
||||||
|
useStlAlgorithmError(assignTok, algo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for container call
|
||||||
|
memberAccessTok = singleMemberCallInScope(condBodyTok, loopVar->varId(), useLoopVarInMemCall);
|
||||||
|
if (memberAccessTok) {
|
||||||
|
const Token *memberCallTok = memberAccessTok->astOperand2();
|
||||||
|
unsigned int contVarId = memberAccessTok->astOperand1()->varId();
|
||||||
|
if (contVarId == loopVar->varId())
|
||||||
|
continue;
|
||||||
|
if (memberCallTok->str() == "push_back" ||
|
||||||
|
memberCallTok->str() == "push_front" ||
|
||||||
|
memberCallTok->str() == "emplace_back") {
|
||||||
|
if (useLoopVarInMemCall)
|
||||||
|
useStlAlgorithmError(memberAccessTok, "std::copy_if");
|
||||||
|
// There is no transform_if to suggest
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for increment in loop
|
||||||
|
incrementTok = singleIncrementInScope(condBodyTok, loopVar->varId(), useLoopVarInIncrement);
|
||||||
|
if (incrementTok) {
|
||||||
|
std::string algo;
|
||||||
|
if (useLoopVarInIncrement)
|
||||||
|
algo = "std::transform";
|
||||||
|
else
|
||||||
|
algo = "std::count_if";
|
||||||
|
useStlAlgorithmError(incrementTok, algo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check early return
|
||||||
|
if (isEarlyExit(condBodyTok)) {
|
||||||
|
const Token *loopVar2 = Token::findmatch(condBodyTok, "%varid%", condBodyTok->link(), loopVar->varId());
|
||||||
|
std::string algo;
|
||||||
|
if (loopVar2)
|
||||||
|
algo = "std::find_if";
|
||||||
|
else
|
||||||
|
algo = "std::any_of";
|
||||||
|
useStlAlgorithmError(condBodyTok, algo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ public:
|
||||||
checkStl.size();
|
checkStl.size();
|
||||||
checkStl.redundantCondition();
|
checkStl.redundantCondition();
|
||||||
checkStl.missingComparison();
|
checkStl.missingComparison();
|
||||||
|
checkStl.useStlAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Accessing container out of bounds using ValueFlow */
|
/** Accessing container out of bounds using ValueFlow */
|
||||||
|
@ -179,6 +180,10 @@ public:
|
||||||
|
|
||||||
/** @brief Reading from empty stl container (using valueflow) */
|
/** @brief Reading from empty stl container (using valueflow) */
|
||||||
void readingEmptyStlContainer2();
|
void readingEmptyStlContainer2();
|
||||||
|
|
||||||
|
/** @brief Look for loops that can replaced with std algorithms */
|
||||||
|
void useStlAlgorithm();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2);
|
void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2);
|
||||||
void string_c_strThrowError(const Token* tok);
|
void string_c_strThrowError(const Token* tok);
|
||||||
|
@ -216,6 +221,8 @@ private:
|
||||||
|
|
||||||
void readingEmptyStlContainerError(const Token* tok, const ValueFlow::Value *value=nullptr);
|
void readingEmptyStlContainerError(const Token* tok, const ValueFlow::Value *value=nullptr);
|
||||||
|
|
||||||
|
void useStlAlgorithmError(const Token *tok, const std::string &algoName);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override {
|
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override {
|
||||||
CheckStl c(nullptr, settings, errorLogger);
|
CheckStl c(nullptr, settings, errorLogger);
|
||||||
c.outOfBoundsError(nullptr, nullptr, nullptr);
|
c.outOfBoundsError(nullptr, nullptr, nullptr);
|
||||||
|
@ -250,6 +257,7 @@ private:
|
||||||
c.uselessCallsRemoveError(nullptr, "remove");
|
c.uselessCallsRemoveError(nullptr, "remove");
|
||||||
c.dereferenceInvalidIteratorError(nullptr, "i");
|
c.dereferenceInvalidIteratorError(nullptr, "i");
|
||||||
c.readingEmptyStlContainerError(nullptr);
|
c.readingEmptyStlContainerError(nullptr);
|
||||||
|
c.useStlAlgorithmError(nullptr, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
@ -271,7 +279,8 @@ private:
|
||||||
"- using auto pointer (auto_ptr)\n"
|
"- using auto pointer (auto_ptr)\n"
|
||||||
"- useless calls of string and STL functions\n"
|
"- useless calls of string and STL functions\n"
|
||||||
"- dereferencing an invalid iterator\n"
|
"- dereferencing an invalid iterator\n"
|
||||||
"- reading from empty STL container\n";
|
"- reading from empty STL container\n"
|
||||||
|
"- consider using an STL algorithm instead of raw loop\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
515
test/teststl.cpp
515
test/teststl.cpp
|
@ -144,6 +144,13 @@ private:
|
||||||
TEST_CASE(dereferenceInvalidIterator);
|
TEST_CASE(dereferenceInvalidIterator);
|
||||||
TEST_CASE(dereferenceInvalidIterator2); // #6572
|
TEST_CASE(dereferenceInvalidIterator2); // #6572
|
||||||
TEST_CASE(dereference_auto);
|
TEST_CASE(dereference_auto);
|
||||||
|
|
||||||
|
TEST_CASE(loopAlgoElementAssign);
|
||||||
|
TEST_CASE(loopAlgoAccumulateAssign);
|
||||||
|
TEST_CASE(loopAlgoContainerInsert);
|
||||||
|
TEST_CASE(loopAlgoIncrement);
|
||||||
|
TEST_CASE(loopAlgoConditional);
|
||||||
|
TEST_CASE(loopAlgoMinMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) {
|
void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) {
|
||||||
|
@ -3156,6 +3163,514 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:18]: (error, inconclusive) Invalid iterator 'it' used.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:18]: (error, inconclusive) Invalid iterator 'it' used.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loopAlgoElementAssign()
|
||||||
|
{
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x = 1;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::fill algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x = x + 1;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo(int a, int b) {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x = a + b;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::fill or std::generate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x += 1;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x = f();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::generate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" f();\n"
|
||||||
|
" x = 1;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" x = 1;\n"
|
||||||
|
" f();\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// There should probably be a message for unconditional break
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" x = 1;\n"
|
||||||
|
" break;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x = ++x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopAlgoAccumulateAssign()
|
||||||
|
{
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n += x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = n + x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n += 1;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::distance algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = n + 1;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::distance algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool f(int);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" b &= f(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool f(int);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" b |= f(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool f(int);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" b = b && f(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool f(int);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" b = b || f(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" n = ++x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopAlgoContainerInsert()
|
||||||
|
{
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_back(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::copy algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_back(f(x));\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_back(x + 1);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_front(x);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::copy algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_front(f(x));\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_front(x + 1);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("(style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_back(v);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" c.push_back(0);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopAlgoIncrement()
|
||||||
|
{
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n++;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::distance algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" ++n;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::distance algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" x++;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" for(int& x:v)\n"
|
||||||
|
" ++x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopAlgoConditional()
|
||||||
|
{
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" x = 1; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::replace_if algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" n += x; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" n += 1; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::count_if algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" n++; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::count_if algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" x = x + 1; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::transform algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" c.push_back(x); \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::copy_if algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" return false; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::any_of algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" break; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::any_of algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void f();\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" f(); \n"
|
||||||
|
" break; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::any_of algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void f(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" f(x); \n"
|
||||||
|
" break; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (style) Consider using std::find_if algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" b = true;\n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" if(b) {}\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" b |= true;\n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" bool b = false;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" b &= true;\n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (style) Consider using std::any_of, std::all_of, std::none_of, or std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"bool foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" return false; \n"
|
||||||
|
" }\n"
|
||||||
|
" return true;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// There is no transform_if
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" std::vector<int> c;\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" c.push_back(x + 1); \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int& x:v) {\n"
|
||||||
|
" x++;\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" x = 1; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("bool pred(int x);\n"
|
||||||
|
"void f();\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" for(int x:v) {\n"
|
||||||
|
" if (pred(x)) {\n"
|
||||||
|
" if(x) { return; }\n"
|
||||||
|
" break; \n"
|
||||||
|
" }\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopAlgoMinMax()
|
||||||
|
{
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = x > n ? x : n;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::max_element algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = x < n ? x : n;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::min_element algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = x > n ? n : x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::min_element algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = x < n ? n : x;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::max_element algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo(int m) {\n"
|
||||||
|
" int n = 0;\n"
|
||||||
|
" for(int x:v)\n"
|
||||||
|
" n = x > m ? x : n;\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestStl)
|
REGISTER_TEST(TestStl)
|
||||||
|
|
Loading…
Reference in New Issue