Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1392 lines (1187 sloc) 63.436 kb
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "checkstl.h"
#include "executionpath.h"
#include "symboldatabase.h"
#include <sstream>
// Register this check class (by creating a static instance of it)
namespace {
CheckStl instance;
}
// Error message for bad iterator usage..
void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName)
{
reportError(tok, Severity::error, "invalidIterator1", "Invalid iterator: " + iteratorName);
}
void CheckStl::iteratorsError(const Token *tok, const std::string &container1, const std::string &container2)
{
reportError(tok, Severity::error, "iterators", "Same iterator is used with both " + container1 + " and " + container2);
}
// Error message used when dereferencing an iterator that has been erased..
void CheckStl::dereferenceErasedError(const Token *tok, const std::string &itername)
{
reportError(tok, Severity::error, "eraseDereference", "Dereferenced iterator '" + itername + "' has been erased");
}
void CheckStl::iterators()
{
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
// Using same iterator against different containers.
// for (it = foo.begin(); it != bar.end(); ++it)
for (unsigned int iteratorId = 1; iteratorId < symbolDatabase->getVariableListSize(); iteratorId++) {
const Variable* var = symbolDatabase->getVariableFromVarId(iteratorId);
// Check that its an iterator
if (!var || !var->isLocal() || !Token::Match(var->nameToken()->previous(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator"))
continue;
// the validIterator flag says if the iterator has a valid value or not
bool validIterator = Token::Match(var->nameToken()->next(), "[(=]");
const Scope* invalidationScope = 0;
// The container this iterator can be used with
const Variable* container = 0;
const Scope* containerAssignScope = 0;
// When "validatingToken" is reached the validIterator is set to true
const Token* validatingToken = 0;
// Scan through the rest of the code and see if the iterator is
// used against other containers.
for (const Token *tok2 = var->nameToken(); tok2 != var->scope()->classEnd; tok2 = tok2->next()) {
if (invalidationScope && tok2 == invalidationScope->classEnd)
validIterator = true; // Assume that the iterator becomes valid again
if (containerAssignScope && tok2 == containerAssignScope->classEnd)
container = 0; // We don't know which containers might be used with the iterator
if (tok2 == validatingToken)
validIterator = true;
// Is iterator compared against different container?
if (Token::Match(tok2, "%varid% !=|== %var% . end|rend|cend|crend ( )", iteratorId) && container && tok2->tokAt(2)->varId() != container->varId()) {
iteratorsError(tok2, container->name(), tok2->strAt(2));
tok2 = tok2->tokAt(6);
}
// Is the iterator used in a insert/erase operation?
else if (Token::Match(tok2, "%var% . insert|erase ( *| %varid% )|,", iteratorId)) {
const Token* itTok = tok2->tokAt(4);
if (itTok->str() == "*") {
if (tok2->strAt(2) == "insert")
continue;
itTok = itTok->next();
}
// It is bad to insert/erase an invalid iterator
if (!validIterator)
invalidIteratorError(tok2, itTok->str());
// If insert/erase is used on different container then
// report an error
if (container && tok2->varId() != container->varId()) {
// skip error message if container is a set..
const Variable *variableInfo = symbolDatabase->getVariableFromVarId(tok2->varId());
const Token *decltok = variableInfo ? variableInfo->typeStartToken() : NULL;
if (Token::Match(decltok, "const| std :: set"))
continue; // No warning
// skip error message if the iterator is erased/inserted by value
if (itTok->previous()->str() == "*")
continue;
// Show error message, mismatching iterator is used.
iteratorsError(tok2, container->name(), tok2->str());
}
// invalidate the iterator if it is erased
else if (tok2->strAt(2) == std::string("erase")) {
validIterator = false;
invalidationScope = tok2->scope();
}
// skip the operation
tok2 = itTok->next();
}
// it = foo.erase(..
// taking the result of an erase is ok
else if (Token::Match(tok2, "%varid% = %var% . erase (", iteratorId)) {
// the returned iterator is valid
validatingToken = tok2->linkAt(5);
tok2 = tok2->tokAt(5);
}
// Reassign the iterator
else if (Token::Match(tok2, "%varid% = %var% . begin|rbegin|cbegin|crbegin|find (", iteratorId)) {
validatingToken = tok2->linkAt(5);
container = symbolDatabase->getVariableFromVarId(tok2->tokAt(2)->varId());
containerAssignScope = tok2->scope();
// skip ahead
tok2 = tok2->tokAt(5);
}
// Reassign the iterator
else if (Token::Match(tok2, "%varid% = %any%", iteratorId)) {
// Assume that the iterator becomes valid.
// TODO: add checking that checks if the iterator becomes valid or not
validatingToken = Token::findmatch(tok2->tokAt(2), "[;)]");
// skip ahead
tok2 = tok2->tokAt(2);
}
// Passing iterator to function. Iterator might be initialized
else if (Token::Match(tok2, "%varid% ,|)", iteratorId)) {
validIterator = true;
}
// Dereferencing invalid iterator?
else if (!validIterator && Token::Match(tok2, "* %varid%", iteratorId)) {
dereferenceErasedError(tok2, tok2->strAt(1));
tok2 = tok2->next();
} else if (!validIterator && Token::Match(tok2, "%varid% . %var%", iteratorId)) {
dereferenceErasedError(tok2, tok2->str());
tok2 = tok2->tokAt(2);
}
// bailout handling. Assume that the iterator becomes valid if we see return/break.
// TODO: better handling
else if (Token::Match(tok2, "return|break")) {
validatingToken = Token::findsimplematch(tok2->next(), ";");
}
// bailout handling. Assume that the iterator becomes valid if we see else.
// TODO: better handling
else if (tok2->str() == "else") {
validIterator = true;
}
}
}
}
// Error message for bad iterator usage..
void CheckStl::mismatchingContainersError(const Token *tok)
{
reportError(tok, Severity::error, "mismatchingContainers", "Iterators of mismatching containers are used together.");
}
void CheckStl::mismatchingContainers()
{
static const char* const algorithm2_strings[] = { // func(begin1, end1
"adjacent_find", "all_of", "any_of", "binary_search", "copy", "copy_if", "count", "count_if", "equal", "equal_range",
"find", "find_if", "find_if_not", "for_each", "generate", "is_heap", "is_heap_until", "is_partitioned",
"is_permutation", "is_sorted", "is_sorted_until", "lower_bound", "make_heap", "max_element", "minmax_element",
"min_element", "mismatch", "move", "move_backward", "next_permutation", "none_of", "partition", "partition_copy",
"partition_point", "pop_heap", "prev_permutation", "push_heap", "random_shuffle", "remove", "remove_copy",
"remove_copy_if", "remove_if", "replace", "replace_copy", "replace_copy_if", "replace_if", "reverse", "reverse_copy",
"search_n", "shuffle", "sort", "sort_heap", "stable_partition", "stable_sort", "swap_ranges", "transform", "unique",
"unique_copy", "upper_bound"
};
static const char* const algorithm22_strings[] = { // func(begin1, end1, begin2, end2
"find_end", "find_first_of", "includes", "lexicographical_compare", "merge", "partial_sort_copy",
"search", "set_difference", "set_intersection", "set_symmetric_difference", "set_union"
};
static const char* const algorithm1x1_strings[] = { // func(begin1, x, end1
"inplace_merge", "nth_element", "partial_sort", "rotate", "rotate_copy"
};
static const std::set<std::string> algorithm2(algorithm2_strings, &algorithm2_strings[sizeof(algorithm2_strings) / sizeof(*algorithm2_strings)]);
static const std::set<std::string> algorithm22(algorithm22_strings, &algorithm22_strings[sizeof(algorithm22_strings) / sizeof(*algorithm22_strings)]);
static const std::set<std::string> algorithm1x1(algorithm1x1_strings, &algorithm1x1_strings[sizeof(algorithm1x1_strings) / sizeof(*algorithm1x1_strings)]);
static const std::string iteratorBeginFuncPattern = "begin|cbegin|rbegin|crbegin";
static const std::string iteratorEndFuncPattern = "end|cend|rend|crend";
static const std::string pattern1x1_1 = "%var% . " + iteratorBeginFuncPattern + " ( ) , ";
static const std::string pattern1x1_2 = "%var% . " + iteratorEndFuncPattern + " ( ) ,|)";
static const std::string pattern2 = pattern1x1_1 + pattern1x1_2;
// Check if different containers are used in various calls of standard functions
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (!Token::Match(tok, "std :: %type% ( !!)"))
continue;
const Token* arg1 = tok->tokAt(4);
// TODO: If iterator variables are used instead then there are false negatives.
if (Token::Match(arg1, pattern2.c_str()) && algorithm2.find(tok->strAt(2)) != algorithm2.end()) {
if (arg1->str() != arg1->strAt(6)) {
mismatchingContainersError(arg1);
}
} else if (algorithm22.find(tok->strAt(2)) != algorithm22.end()) {
if (Token::Match(arg1, pattern2.c_str()) && arg1->str() != arg1->strAt(6))
mismatchingContainersError(arg1);
// Find third parameter
const Token* arg3 = arg1;
for (unsigned int i = 0; i < 2 && arg3; i++)
arg3 = arg3->nextArgument();
if (Token::Match(arg3, pattern2.c_str()) && arg3->str() != arg3->strAt(6))
mismatchingContainersError(arg3);
} else if (Token::Match(arg1, pattern1x1_1.c_str()) && algorithm1x1.find(tok->strAt(2)) != algorithm1x1.end()) {
// Find third parameter
const Token *arg3 = arg1->tokAt(6)->nextArgument();
if (Token::Match(arg3, pattern1x1_2.c_str())) {
if (arg1->str() != arg3->str()) {
mismatchingContainersError(arg1);
}
}
}
tok = arg1->linkAt(-1);
}
}
void CheckStl::stlOutOfBounds()
{
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
// Scan through all tokens..
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
const Token* const tok = i->classDef;
// only interested in "for" loops
if (i->type != Scope::eFor || !tok)
continue;
// check if the for loop condition is wrong
for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != tok->next()->link(); tok2 = tok2->next()) {
if (Token::Match(tok2, "; %var% <= %var% . size ( ) ;")) {
// variable id for loop variable.
unsigned int numId = tok2->next()->varId();
// variable id for the container variable
unsigned int varId = tok2->tokAt(3)->varId();
// Is it a vector?
const Variable *container = symbolDatabase->getVariableFromVarId(varId);
if (!container)
continue;
if (!Token::simpleMatch(container->typeStartToken(), "std :: vector <"))
continue;
for (const Token *tok3 = tok2->tokAt(8); tok3 && tok3 != i->classEnd; tok3 = tok3->next()) {
if (tok3->varId() == varId) {
if (Token::simpleMatch(tok3->next(), ". size ( )"))
break;
else if (Token::Match(tok3->next(), "[ %varid% ]", numId))
stlOutOfBoundsError(tok3, tok3->strAt(2), tok3->str(), false);
else if (Token::Match(tok3->next(), ". at ( %varid% )", numId))
stlOutOfBoundsError(tok3, tok3->strAt(4), tok3->str(), true);
}
}
break;
}
}
}
}
// Error message for bad iterator usage..
void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at)
{
if (at)
reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + ".at(" + num + ") is out of bounds");
else
reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + "[" + num + "] is out of bounds");
}
/**
* @brief %Check for invalid iterator usage after erase/insert/etc
*/
class EraseCheckLoop : public ExecutionPath {
public:
static void checkScope(CheckStl *checkStl, const Token *it) {
const Token *tok = it;
// Search for the start of the loop body..
while (0 != (tok = tok->next())) {
if (tok->str() == "(")
tok = tok->link();
else if (tok->str() == ")")
break;
// reassigning iterator in loop head
else if (Token::Match(tok, "%var% =") && tok->str() == it->str())
break;
}
if (! Token::simpleMatch(tok, ") {"))
return;
EraseCheckLoop c(checkStl, it->varId());
std::list<ExecutionPath *> checks;
checks.push_back(c.copy());
ExecutionPath::checkScope(tok->tokAt(2), checks);
c.end(checks, tok->link());
while (!checks.empty()) {
delete checks.back();
checks.pop_back();
}
}
private:
/** Startup constructor */
EraseCheckLoop(Check *o, unsigned int varid)
: ExecutionPath(o, varid), eraseToken(0) {
}
/** @brief token where iterator is erased (non-zero => the iterator is invalid) */
const Token *eraseToken;
/** @brief Copy this check. Called from the ExecutionPath baseclass. */
ExecutionPath *copy() {
return new EraseCheckLoop(*this);
}
/** @brief is another execution path equal? */
bool is_equal(const ExecutionPath *e) const {
const EraseCheckLoop *c = static_cast<const EraseCheckLoop *>(e);
return (eraseToken == c->eraseToken);
}
/** @brief no implementation => compiler error if used by accident */
void operator=(const EraseCheckLoop &);
/** @brief parse tokens */
const Token *parse(const Token &tok, std::list<ExecutionPath *> &checks) const {
// bail out if there are assignments. We don't check the assignments properly.
if (Token::Match(&tok, "[;{}] %var% =") || Token::Match(&tok, "= %var% ;")) {
ExecutionPath::bailOutVar(checks, tok.next()->varId());
}
// the loop stops here. Bail out all execution checks that reach
// this statement
if (Token::Match(&tok, "[;{}] break ;")) {
ExecutionPath::bailOut(checks);
}
// erasing iterator => it is invalidated
if (Token::Match(&tok, "erase ( ++|--| %var% )")) {
// check if there is a "it = ints.erase(it);" pattern. if so
// the it is not invalidated.
const Token *token = &tok;
while (NULL != (token = token ? token->previous() : 0)) {
if (Token::Match(token, "[;{}]"))
break;
else if (token->str() == "=")
token = 0;
}
// the it is invalidated by the erase..
if (token) {
// get variable id for the iterator
unsigned int iteratorId = 0;
if (tok.tokAt(2)->isName())
iteratorId = tok.tokAt(2)->varId();
else
iteratorId = tok.tokAt(3)->varId();
// invalidate this iterator in the corresponding checks
for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it) {
EraseCheckLoop *c = dynamic_cast<EraseCheckLoop *>(*it);
if (c && c->varId == iteratorId) {
c->eraseToken = &tok;
}
}
}
}
// don't skip any tokens. return the token that we received.
return &tok;
}
/**
* Parse condition. @sa ExecutionPath::parseCondition
* @param tok first token in condition.
* @param checks The execution paths. All execution paths in the list are executed in the current scope
* @return true => bail out all checking
**/
bool parseCondition(const Token &tok, std::list<ExecutionPath *> &checks) {
// no checking of conditions.
(void)tok;
(void)checks;
return false;
}
/** @brief going out of scope - all execution paths end */
void end(const std::list<ExecutionPath *> &checks, const Token * /*tok*/) const {
// check if there are any invalid iterators. If so there is an error.
for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it) {
EraseCheckLoop *c = dynamic_cast<EraseCheckLoop *>(*it);
if (c && c->eraseToken) {
CheckStl *checkStl = dynamic_cast<CheckStl *>(c->owner);
if (checkStl) {
checkStl->eraseError(c->eraseToken);
}
}
}
}
};
void CheckStl::erase()
{
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
const Token* const tok = i->classDef;
if (tok && i->type == Scope::eFor) {
for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
if (tok2->str() == ";") {
if (Token::Match(tok2, "; %var% !=")) {
// Get declaration token for var..
const Variable *variableInfo = symbolDatabase->getVariableFromVarId(tok2->next()->varId());
const Token *decltok = variableInfo ? variableInfo->typeEndToken() : NULL;
// Is variable an iterator?
bool isIterator = false;
if (decltok && Token::Match(decltok->tokAt(-2), "> :: iterator %varid%", tok2->next()->varId()))
isIterator = true;
// If tok2->next() is an iterator, check scope
if (isIterator)
EraseCheckLoop::checkScope(this, tok2->next());
}
break;
}
if (Token::Match(tok2, "%var% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( )") &&
tok2->str() == tok2->strAt(8) &&
tok2->strAt(2) == tok2->strAt(10)) {
EraseCheckLoop::checkScope(this, tok2);
break;
}
}
}
else if (i->type == Scope::eWhile && Token::Match(tok, "while ( %var% !=")) {
const unsigned int varid = tok->tokAt(2)->varId();
if (varid > 0 && Token::findmatch(_tokenizer->tokens(), "> :: iterator %varid%", varid))
EraseCheckLoop::checkScope(this, tok->tokAt(2));
}
}
}
// Error message for bad iterator usage..
void CheckStl::eraseError(const Token *tok)
{
reportError(tok, Severity::error, "erase",
"Dangerous iterator usage after erase()-method.\n"
"The iterator is invalid after it has been used in erase() function. "
"Dereferencing or comparing it with another iterator is invalid operation.");
}
void CheckStl::pushback()
{
// Pointer can become invalid after push_back, push_front, reserve or resize..
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "%var% = & %var% [")) {
// Variable id for pointer
const unsigned int pointerId(tok->varId());
// Variable id for the container variable
const unsigned int containerId(tok->tokAt(3)->varId());
if (pointerId == 0 || containerId == 0)
continue;
// Count { , } and parentheses for tok2
int indent = 0;
bool invalidPointer = false;
for (const Token *tok2 = tok; indent >= 0 && tok2; tok2 = tok2->next()) {
if (tok2->str() == "{" || tok2->str() == "(")
++indent;
else if (tok2->str() == "}" || tok2->str() == ")") {
if (indent == 0 && Token::simpleMatch(tok2, ") {"))
tok2 = tok2->next();
else
--indent;
}
// push_back on vector..
if (Token::Match(tok2, "%varid% . push_front|push_back|resize|reserve", containerId))
invalidPointer = true;
// Using invalid pointer..
if (invalidPointer && tok2->varId() == pointerId) {
if (tok2->previous()->str() == "*")
invalidPointerError(tok2, tok2->str());
else if (tok2->next()->str() == ".")
invalidPointerError(tok2, tok2->str());
break;
}
}
}
}
// Iterator becomes invalid after reserve, resize, insert, push_back or push_front..
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (!Token::simpleMatch(tok, "vector <"))
continue;
// if iterator declaration inside for() loop
bool iteratorDeclaredInsideLoop = false;
if ((tok->tokAt(-2) && Token::simpleMatch(tok->tokAt(-2), "for (")) ||
(tok->tokAt(-4) && Token::simpleMatch(tok->tokAt(-4), "for ( std ::"))) {
iteratorDeclaredInsideLoop = true;
}
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
if (!Token::Match(tok, "> :: iterator|const_iterator %var% =|;"))
continue;
const unsigned int iteratorid(tok->tokAt(3)->varId());
if (iteratorid == 0)
continue;
if (iteratorDeclaredInsideLoop && tok->strAt(4) == "=") {
// skip "> :: iterator|const_iterator"
tok = tok->tokAt(3);
}
// the variable id for the vector
unsigned int vectorid = 0;
// count { , } and parentheses for tok2
int indent = 0;
std::string invalidIterator;
for (const Token *tok2 = tok; indent >= 0 && tok2; tok2 = tok2->next()) {
if (tok2->str() == "{" || tok2->str() == "(")
++indent;
else if (tok2->str() == "}" || tok2->str() == ")") {
if (indent == 0 && Token::simpleMatch(tok2, ") {"))
tok2 = tok2->next();
else
--indent;
}
// Using push_back or push_front inside a loop..
if (Token::simpleMatch(tok2, "for (")) {
tok2 = tok2->tokAt(2);
}
if (Token::Match(tok2, "%varid% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %varid% != %var% . end|rend|cend|crend ( ) ; ++| %varid% ++| ) {", iteratorid)) {
// variable id for the loop iterator
const unsigned int varId(tok2->tokAt(2)->varId());
if (varId == 0)
continue;
const Token *pushbackTok = 0;
// Count { and } for tok3
const Token *tok3 = tok2->tokAt(20);
for (const Token* const end3 = tok3->linkAt(-1); tok3 != end3; tok3 = tok3->next()) {
if (tok3->str() == "break" || tok3->str() == "return") {
pushbackTok = 0;
break;
} else if (Token::Match(tok3, "%varid% . push_front|push_back|insert|reserve|resize (", varId)) {
pushbackTok = tok3->tokAt(2);
}
}
if (pushbackTok)
invalidIteratorError(pushbackTok, pushbackTok->str(), tok2->str());
}
// Assigning iterator..
if (Token::Match(tok2, "%varid% =", iteratorid)) {
if (Token::Match(tok2->tokAt(2), "%var% . begin|end|rbegin|rend|cbegin|cend|crbegin|crend ( )")) {
vectorid = tok2->tokAt(2)->varId();
tok2 = tok2->tokAt(6);
} else {
vectorid = 0;
}
invalidIterator = "";
}
// push_back on vector..
if (vectorid > 0 && Token::Match(tok2, "%varid% . push_front|push_back|insert|reserve|resize (", vectorid)) {
if (!invalidIterator.empty() && Token::Match(tok2->tokAt(2), "insert ( %varid% ,", iteratorid)) {
invalidIteratorError(tok2, invalidIterator, tok2->strAt(4));
break;
}
invalidIterator = tok2->strAt(2);
tok2 = tok2->linkAt(3);
}
// TODO: instead of bail out for 'else' try to check all execution paths.
else if (tok2->str() == "return" || tok2->str() == "break" || tok2->str() == "else") {
invalidIterator.clear();
}
// Using invalid iterator..
if (!invalidIterator.empty()) {
if (Token::Match(tok2, "++|--|*|+|-|(|,|=|!= %varid%", iteratorid))
invalidIteratorError(tok2, invalidIterator, tok2->strAt(1));
if (Token::Match(tok2, "%varid% ++|--|+|-", iteratorid))
invalidIteratorError(tok2, invalidIterator, tok2->str());
}
}
}
}
// Error message for bad iterator usage..
void CheckStl::invalidIteratorError(const Token *tok, const std::string &func, const std::string &iterator_name)
{
reportError(tok, Severity::error, "invalidIterator2", "After " + func + ", the iterator '" + iterator_name + "' may be invalid");
}
// Error message for bad iterator usage..
void CheckStl::invalidPointerError(const Token *tok, const std::string &pointer_name)
{
reportError(tok, Severity::error, "invalidPointer", "Invalid pointer '" + pointer_name + "' after push_back / push_front");
}
void CheckStl::stlBoundries()
{
// containers (not the vector)..
static const char STL_CONTAINER_LIST[] = "bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset";
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
// Declaring iterator..
if (tok->str() == "<" && Token::Match(tok->previous(), STL_CONTAINER_LIST)) {
const std::string& container_name(tok->strAt(-1));
if (tok->link())
tok = tok->link();
else
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
if (Token::Match(tok, "> :: iterator|const_iterator %var% =|;")) {
const unsigned int iteratorid(tok->tokAt(3)->varId());
if (iteratorid == 0)
continue;
// Using "iterator < ..." is not allowed
const Token* const end = tok->scope()->classEnd;
for (const Token *tok2 = tok; tok2 != end; tok2 = tok2->next()) {
if (Token::Match(tok2, "!!* %varid% <", iteratorid)) {
stlBoundriesError(tok2, container_name);
} else if (Token::Match(tok2, "> %varid% !!.", iteratorid)) {
stlBoundriesError(tok2, container_name);
}
}
}
}
}
}
// Error message for bad boundary usage..
void CheckStl::stlBoundriesError(const Token *tok, const std::string &container_name)
{
reportError(tok, Severity::error, "stlBoundries",
"Dangerous container iterator compare using < operator for " + container_name + "\n"
"Container '" + container_name + "' iterator compared with < operator. "
"Using < operator with container type iterators is dangerous since the order of "
"the items is not guaranteed. One should use != operator instead when comparing "
"iterators in the container.");
}
static bool if_findCompare(const Token * const tokBack)
{
const Token *tok = tokBack;
while (tok && tok->str() == ")") {
tok = tok->next();
if (Token::Match(tok,",|==|!="))
return true;
if (Token::Match(tok, ") !!{") &&
tok->link()->previous() &&
(Token::Match(tok->link()->previous(),",|==|!=") ||
tok->link()->previous()->isName()))
return true;
}
return false;
}
void CheckStl::if_find()
{
if (!_settings->isEnabled("style"))
return;
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
if ((i->type != Scope::eIf && i->type != Scope::eElseIf && i->type != Scope::eWhile) || !i->classDef)
continue;
const Token* tok = i->classDef->next();
if (tok->str() == "if")
tok = tok->next();
for (const Token* const end = tok->link(); tok != end; tok = (tok == end) ? end : tok->next()) {
if (Token::Match(tok, "&&|(|%oror%"))
tok = tok->next();
else
continue;
while (tok->str() == "(")
tok = tok->next();
if (tok->str() == "!")
tok = tok->next();
if (Token::Match(tok, "%var% . find (")) {
if (if_findCompare(tok->linkAt(3)))
continue;
const unsigned int varid = tok->varId();
const Variable *var = symbolDatabase->getVariableFromVarId(varid);
if (var) {
// Is the variable a std::string or STL container?
const Token * decl = var->typeStartToken();
if (decl->str() == "const")
decl = decl->next();
// stl container
if (Token::Match(decl, "std :: %var% < %type% > &| %varid%", varid))
if_findError(tok, false);
else if (Token::Match(decl, "std :: string &| %varid%", varid))
if_findError(tok, true);
}
}
//check also for vector-like or pointer containers
else if (Token::Match(tok, "* %var%") || Token::Match(tok, "%var% [")) {
// goto %var%
if (tok->str() == "*")
tok = tok->next();
const Token *tok2 = tok->next();
if (tok2->str() == "[")
tok2 = tok2->link()->next();
if (!Token::simpleMatch(tok2, ". find ("))
continue;
if (if_findCompare(tok2->linkAt(2)))
continue;
const unsigned int varid = tok->varId();
const Variable *var = symbolDatabase->getVariableFromVarId(varid);
if (var) {
// Is the variable a std::string or STL container?
const Token * decl = var->typeStartToken();
//jump next to 'const'
if (decl->str() == "const")
decl = decl->next();
//pretty bad limitation.. but it is there in order to avoid
//own implementations of 'find' or any container
if (!Token::simpleMatch(decl, "std ::"))
continue;
decl = decl->tokAt(2);
if (Token::Match(decl, "%var% <")) {
decl = decl->tokAt(2);
//stl-like
if (Token::Match(decl, "std :: %var% < %type% > > &| %varid%", varid))
if_findError(tok, false);
//not stl-like, then let's hope it's a pointer or an array
else if (Token::Match(decl, "%type% >")) {
decl = decl->tokAt(2);
if (Token::Match(decl, "* &| %varid%", varid) ||
Token::Match(decl, "&| %varid% [ ]| %any% ]| ", varid))
if_findError(tok, false);
}
else if (Token::Match(decl, "std :: string > &| %varid%", varid))
if_findError(tok, true);
}
else if (decl && decl->str() == "string") {
decl = decl->next();
if (Token::Match(decl, "* &| %varid%", varid) ||
Token::Match(decl, "&| %varid% [ ]| %any% ]| ", varid))
if_findError(tok, true);
}
}
}
else if (Token::Match(tok, "std :: find|find_if (")) {
// check that result is checked properly
if (!if_findCompare(tok->linkAt(3))) {
if_findError(tok, false);
}
}
}
}
}
void CheckStl::if_findError(const Token *tok, bool str)
{
if (str)
reportError(tok, Severity::performance, "stlIfStrFind",
"Inefficient usage of string::find in condition; string::compare would be faster.\n"
"Either inefficent or wrong usage of string::find. string::compare will be faster if "
"string::find's result is compared with 0, because it will not scan the whole "
"string. If your intention is to check that there are no findings in the string, "
"you should compare with string::npos.");
else
reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find is an iterator, but it is not properly checked.");
}
bool CheckStl::isStlContainer(unsigned int varid)
{
// check if this token is defined
if (varid) {
// find where this token is defined
const Variable *var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(varid);
if (!var)
return false;
// find where this tokens type starts
const Token *type = var->typeStartToken();
// ignore "const"
if (type->str() == "const")
type = type->next();
// discard namespace if supplied
if (Token::simpleMatch(type, "std ::"))
type = type->tokAt(2);
// all possible stl containers as a token
static const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string";
// check if it's an stl template
if (Token::Match(type, STL_CONTAINER_LIST))
return true;
}
return false;
}
void CheckStl::size()
{
if (!_settings->isEnabled("performance"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "%var% . size ( )") ||
Token::Match(tok, "%var% . %var% . size ( )")) {
const Token *tok1 = tok;
unsigned int varid = 0;
// get the variable id
if (tok->strAt(2) != "size") {
tok1 = tok1->tokAt(2);
// found a.b.size(), lookup class/struct variable
const Variable *var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId());
if (var && var->type()) {
// get class/struct variable type
const Scope *type = var->type();
// lookup variable member
std::list<Variable>::const_iterator it;
for (it = type->varlist.begin(); it != type->varlist.end(); ++it) {
if (it->name() == tok1->str()) {
// found member variable, save varid
varid = it->varId();
break;
}
}
}
} else
varid = tok1->varId();
const Token* const end = tok1->tokAt(5);
if (varid) {
// check for comparison to zero
if ((tok->previous() && !tok->previous()->isArithmeticalOp() && Token::Match(end, "==|<=|!=|> 0")) ||
(end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "0 ==|>=|!=|<"))) {
if (isStlContainer(varid))
sizeError(tok1);
}
// check for using as boolean expression
else if ((Token::Match(tok->tokAt(-2), "if|while (") && end->str() == ")") ||
(tok->previous()->type() == Token::eLogicalOp && Token::Match(end, "&&|)|,|;|%oror%"))) {
if (isStlContainer(varid))
sizeError(tok1);
}
}
}
}
}
void CheckStl::sizeError(const Token *tok)
{
const std::string varname = tok ? tok->str() : std::string("list");
reportError(tok, Severity::performance, "stlSize",
"Possible inefficient checking for '" + varname + "' emptiness.\n"
"Checking for '" + varname + "' emptiness might be inefficient. "
"Using " + varname + ".empty() instead of " + varname + ".size() can be faster. " +
varname + ".size() can take linear time but " + varname + ".empty() is "
"guaranteed to take constant time.");
}
void CheckStl::redundantCondition()
{
const char pattern[] = "if ( %var% . find ( %any% ) != %var% . end|rend|cend|crend ( ) ) "
"{"
" %var% . remove ( %any% ) ;";
const Token *tok = Token::findmatch(_tokenizer->tokens(), pattern);
while (tok) {
// Get tokens for the fields %var% and %any%
const Token *var1 = tok->tokAt(2);
const Token *any1 = var1->tokAt(4);
const Token *var2 = any1->tokAt(3);
const Token *var3 = var2->tokAt(7);
const Token *any2 = var3->tokAt(4);
// Check if all the "%var%" fields are the same and if all the "%any%" are the same..
if (var1->str() == var2->str() &&
var2->str() == var3->str() &&
any1->str() == any2->str()) {
redundantIfRemoveError(tok);
}
tok = Token::findmatch(tok->next(), pattern);
}
}
void CheckStl::redundantIfRemoveError(const Token *tok)
{
reportError(tok, Severity::style, "redundantIfRemove",
"Redundant checking of STL container element.\n"
"Redundant checking of STL container element existence before removing it. "
"The remove method in the STL will not do anything if element doesn't exist");
}
void CheckStl::missingComparison()
{
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
if (i->type != Scope::eFor || !i->classDef)
continue;
for (const Token *tok2 = i->classDef->tokAt(2); tok2 != i->classStart; tok2 = tok2->next()) {
if (tok2->str() == ";")
break;
if (!Token::Match(tok2, "%var% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( ) ; ++| %var% ++| ) {"))
continue;
// same container
if (tok2->strAt(2) != tok2->strAt(10))
break;
const unsigned int iteratorId(tok2->varId());
if (iteratorId == 0)
break;
// same iterator
if (iteratorId == tok2->tokAt(10)->varId())
break;
// increment iterator
if (!Token::Match(tok2->tokAt(16), "++ %varid% )", iteratorId) &&
!Token::Match(tok2->tokAt(16), "%varid% ++ )", iteratorId)) {
break;
}
const Token *incrementToken = 0;
// Parse loop..
for (const Token *tok3 = i->classStart; tok3 != i->classEnd; tok3 = tok3->next()) {
if (Token::Match(tok3, "%varid% ++", iteratorId))
incrementToken = tok3;
else if (Token::Match(tok3->previous(), "++ %varid% !!.", iteratorId))
incrementToken = tok3;
else if (Token::Match(tok3, "%varid% !=|==", iteratorId))
incrementToken = 0;
else if (tok3->str() == "break" || tok3->str() == "return")
incrementToken = 0;
else if (Token::Match(tok3, "%varid% = %var% . insert ( ++| %varid% ++| ,", iteratorId)) {
// skip insertion..
tok3 = tok3->linkAt(6);
if (!tok3)
break;
}
}
if (incrementToken)
missingComparisonError(incrementToken, tok2->tokAt(16));
}
}
}
void CheckStl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2)
{
std::ostringstream errmsg;
errmsg << "Missing bounds check for extra iterator increment in loop.\n"
<< "The iterator incrementing is suspicious - it is incremented at line "
<< incrementToken1->linenr() << " and then at line " << incrementToken2->linenr()
<< " The loop might unintentionally skip an element in the container. "
<< "There is no comparison between these increments to prevent that the iterator is "
<< "incremented beyond the end.";
reportError(incrementToken1, Severity::warning, "StlMissingComparison", errmsg.str());
}
static bool isLocal(const SymbolDatabase* symbolDatabase, unsigned int varid)
{
const Variable* var = symbolDatabase->getVariableFromVarId(varid);
return var && !var->isStatic() && var->isLocal();
}
void CheckStl::string_c_str()
{
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
// Find all functions that take std::string as argument
std::multimap<std::string, unsigned int> c_strFuncParam;
if (_settings->isEnabled("performance")) {
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
if (c_strFuncParam.erase(func->tokenDef->str()) != 0) { // Check if function with this name was already found
c_strFuncParam.insert(std::make_pair(func->tokenDef->str(), 0)); // Disable, because there are overloads. TODO: Handle overloads
continue;
}
unsigned int numpar = 0;
c_strFuncParam.insert(std::make_pair(func->tokenDef->str(), numpar)); // Insert function as dummy, to indicate that there is at least one function with that name
for (const Token* tok = func->argDef->next(); tok != 0; tok = tok->nextArgument()) {
numpar++;
if (Token::Match(tok, "std :: string !!&") || Token::simpleMatch(tok, "const std :: string"))
c_strFuncParam.insert(std::make_pair(func->tokenDef->str(), numpar));
}
}
}
}
// Try to detect common problems when using string::c_str()
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
if (scope->type != Scope::eFunction || !scope->function)
continue;
enum {charPtr, stdString, stdStringConstRef, Other} returnType = Other;
if (Token::simpleMatch(scope->function->tokenDef->tokAt(-2), "char *"))
returnType = charPtr;
else if (Token::simpleMatch(scope->function->tokenDef->tokAt(-5), "const std :: string &"))
returnType = stdStringConstRef;
else if (Token::Match(scope->function->tokenDef->tokAt(-3), "std :: string !!&"))
returnType = stdString;
for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
// Invalid usage..
if (Token::Match(tok, "throw %var% . c_str ( ) ;") && isLocal(symbolDatabase, tok->next()->varId())) {
string_c_strThrowError(tok);
} else if (Token::Match(tok, "[;{}] %var% = %var% . str ( ) . c_str ( ) ;")) {
const Variable* var = symbolDatabase->getVariableFromVarId(tok->next()->varId());
if (var && var->isPointer())
string_c_strError(tok);
} else if (Token::Match(tok, "[;{}] %var% = %var% (") &&
Token::simpleMatch(tok->linkAt(4), ") . c_str ( ) ;") &&
Token::findmatch(_tokenizer->tokens(), ("std :: string " + tok->strAt(3) + " (").c_str())) {
const Variable* var = symbolDatabase->getVariableFromVarId(tok->next()->varId());
if (var && var->isPointer())
string_c_strError(tok);
} else if (Token::Match(tok, "%var% ( !!)") && c_strFuncParam.find(tok->str()) != c_strFuncParam.end() &&
_settings->isEnabled("performance") && !Token::Match(tok->previous(), "::|.") && tok->varId() == 0 && tok->str() != scope->className) { // calling function. TODO: Add support for member functions
std::pair<std::multimap<std::string, unsigned int>::const_iterator, std::multimap<std::string, unsigned int>::const_iterator> range = c_strFuncParam.equal_range(tok->str());
for (std::multimap<std::string, unsigned int>::const_iterator i = range.first; i != range.second; ++i) {
if (i->second == 0)
continue;
const Token* tok2 = tok->tokAt(2);
unsigned int j;
for (j = 0; tok2 && j < i->second-1; j++)
tok2 = tok2->nextArgument();
if (tok2)
tok2 = tok2->nextArgument();
else
break;
if (!tok2 && j == i->second-1)
tok2 = tok->next()->link();
else
tok2 = tok2->previous();
if (tok2 && Token::simpleMatch(tok2->tokAt(-4), ". c_str ( )"))
string_c_strParam(tok, i->second);
}
}
// Using c_str() to get the return value is only dangerous if the function returns a char*
if (returnType == charPtr) {
if (Token::Match(tok, "return %var% . c_str ( ) ;") && isLocal(symbolDatabase, tok->next()->varId())) {
string_c_strError(tok);
} else if (Token::Match(tok, "return %var% . str ( ) . c_str ( ) ;") && isLocal(symbolDatabase, tok->next()->varId())) {
string_c_strError(tok);
} else if (Token::simpleMatch(tok, "return std :: string (") &&
Token::simpleMatch(tok->linkAt(4), ") . c_str ( ) ;")) {
string_c_strError(tok);
} else if (Token::Match(tok, "return %var% (") && Token::simpleMatch(tok->linkAt(2), ") . c_str ( ) ;")) {
const Function* func =_tokenizer->getSymbolDatabase()->findFunctionByName(tok->strAt(1), tok->scope());;
if (func && Token::simpleMatch(func->tokenDef->tokAt(-3), "std :: string"))
string_c_strError(tok);
} else if (Token::simpleMatch(tok, "return (") &&
Token::simpleMatch(tok->next()->link(), ") . c_str ( ) ;")) {
// Check for "+ localvar" or "+ std::string(" inside the bracket
bool is_implicit_std_string = _settings->inconclusive;
const Token *search_end = tok->next()->link();
for (const Token *search_tok = tok->tokAt(2); search_tok != search_end; search_tok = search_tok->next()) {
if (Token::Match(search_tok, "+ %var%") && isLocal(symbolDatabase, search_tok->next()->varId())) {
is_implicit_std_string = true;
break;
} else if (Token::simpleMatch(search_tok, "+ std :: string (")) {
is_implicit_std_string = true;
break;
}
}
if (is_implicit_std_string)
string_c_strError(tok);
}
}
// Using c_str() to get the return value is redundant if the function returns std::string or const std::string&.
else if ((returnType == stdString || returnType == stdStringConstRef) && _settings->isEnabled("performance")) {
if (tok->str() == "return") {
const Token* tok2 = Token::findsimplematch(tok->next(), ";");
if (Token::simpleMatch(tok2->tokAt(-4), ". c_str ( )"))
string_c_strReturn(tok);
}
}
}
}
}
void CheckStl::string_c_strThrowError(const Token* tok)
{
reportError(tok, Severity::error, "stlcstrthrow", "Dangerous usage of c_str(). The returned value by c_str() is invalid after throw call.\n"
"Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid.");
}
void CheckStl::string_c_strError(const Token* tok)
{
reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str(). The returned value by c_str() is invalid after this call.\n"
"Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted.");
}
void CheckStl::string_c_strReturn(const Token* tok)
{
reportError(tok, Severity::performance, "stlcstrReturn", "Returning the result of c_str() in a function that returns std::string is slow and redundant.\n"
"The conversion from const char* as returned by c_str to std::string creates an unnecessary string copy. Solve that by directly returning the string.");
}
void CheckStl::string_c_strParam(const Token* tok, unsigned int number)
{
std::ostringstream oss;
oss << "Passing the result of c_str() to a function that takes std::string as argument " << number << " is slow and redundant.\n"
"The conversion from const char* as returned by c_str to std::string creates an unnecessary string copy. Solve that by directly passing the string.";
reportError(tok, Severity::performance, "stlcstrParam", oss.str());
}
static bool hasArrayEnd(const Token *tok1)
{
const Token *end = Token::findsimplematch(tok1, ";");
return (end && Token::simpleMatch(end->previous(), "] ;"));
}
static bool hasArrayEndParen(const Token *tok1)
{
const Token *end = Token::findsimplematch(tok1, ";");
return (end && end->previous() &&
Token::simpleMatch(end->tokAt(-2), "] ) ;"));
}
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CheckStl::checkAutoPointer()
{
std::set<unsigned int> autoPtrVarId;
static const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string";
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "auto_ptr <")) {
if ((tok->strAt(-1) == "<" && Token::Match(tok->tokAt(-2), STL_CONTAINER_LIST)) ||
(Token::simpleMatch(tok->tokAt(-3), "< std :: auto_ptr") && Token::Match(tok->tokAt(-4), STL_CONTAINER_LIST))) {
autoPointerContainerError(tok);
} else {
const Token *tok2 = tok->tokAt(2);
while (tok2) {
if (Token::Match(tok2, "> %var%")) {
const Token *tok3 = tok2->tokAt(2);
if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) {
autoPointerArrayError(tok2->next());
break;
}
while (tok3 && tok3->str() != ";") {
tok3 = tok3->next();
}
if (tok3) {
tok3 = tok3->tokAt(-2);
if (Token::simpleMatch(tok3->previous(), "[ ] )")) {
autoPointerArrayError(tok2->next());
} else if (tok3->varId()) {
const Token *decltok = Token::findmatch(_tokenizer->tokens(), "%varid% = new %type%", tok3->varId());
if (decltok && hasArrayEnd(decltok)) {
autoPointerArrayError(tok2->next());
}
}
if (tok2->next()->varId()) {
autoPtrVarId.insert(tok2->next()->varId());
}
break;
}
}
tok2 = tok2->next();
}
}
} else {
if (Token::Match(tok, "%var% = %var% ;")) {
if (_settings->isEnabled("style")) {
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->tokAt(2)->varId());
if (iter != autoPtrVarId.end()) {
autoPointerError(tok->tokAt(2));
}
}
} else if ((Token::Match(tok, "%var% = new %type% ") && hasArrayEnd(tok)) ||
(Token::Match(tok, "%var% . reset ( new %type% ") && hasArrayEndParen(tok))) {
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->varId());
if (iter != autoPtrVarId.end()) {
autoPointerArrayError(tok);
}
}
}
}
}
void CheckStl::autoPointerError(const Token *tok)
{
reportError(tok, Severity::style, "useAutoPointerCopy",
"Copy 'auto_ptr' pointer to another do not create two equal objects since one has lost its ownership of the pointer.\n"
"The auto_ptr has semantics of strict ownership, meaning that the auto_ptr instance is the sole entity responsible for the object's lifetime. If an auto_ptr is copied, the source loses the reference."
);
}
void CheckStl::autoPointerContainerError(const Token *tok)
{
reportError(tok, Severity::error, "useAutoPointerContainer",
"You can randomly lose access to pointers if you store 'auto_ptr' pointers in a container because the copy-semantics of 'auto_ptr' are not compatible with containers.\n"
"An element of container must be able to be copied but 'auto_ptr' does not fulfill this requirement. You should consider to use 'shared_ptr' or 'unique_ptr'. It is suitable for use in containers, because they no longer copy their values, they move them."
);
}
void CheckStl::autoPointerArrayError(const Token *tok)
{
reportError(tok, Severity::error, "useAutoPointerArray",
"Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'.\n"
"Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes arrays, which are allocated by operator 'new[]' and must be deallocated by operator 'delete[]'."
);
}
void CheckStl::uselessCalls()
{
bool performance = _settings->isEnabled("performance");
bool style = _settings->isEnabled("style");
if (!performance && !style)
return;
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (tok->varId() && Token::Match(tok, "%var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %var% [,)]") &&
tok->varId() == tok->tokAt(4)->varId() && style) {
uselessCallsReturnValueError(tok->tokAt(4), tok->str(), tok->strAt(2));
} else if (tok->varId() && Token::Match(tok, "%var% . swap ( %var% )") &&
tok->varId() == tok->tokAt(4)->varId() && performance) {
uselessCallsSwapError(tok, tok->str());
} else if (Token::simpleMatch(tok, ". substr (") && performance) {
if (Token::Match(tok->tokAt(3), "0| )"))
uselessCallsSubstrError(tok, false);
else if (tok->strAt(3) == "0" && tok->linkAt(2)->strAt(-1) == "npos") {
if (!symbolDatabase->getVariableFromVarId(tok->linkAt(2)->previous()->varId())) // Make sure that its no variable
uselessCallsSubstrError(tok, false);
} else if (Token::simpleMatch(tok->linkAt(2)->tokAt(-2), ", 0 )"))
uselessCallsSubstrError(tok, true);
} else if (Token::Match(tok, "[{}:;] %var% . empty ( ) ;") && style)
uselessCallsEmptyError(tok->next());
else if (Token::Match(tok, "[{};] std :: remove|remove_if|unique (") && tok->tokAt(5)->nextArgument())
uselessCallsRemoveError(tok->next(), tok->strAt(3));
}
}
void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function)
{
std::ostringstream errmsg;
errmsg << "It is inefficient to call '" << varname << "." << function << "(" << varname << ")' as it always returns 0.\n"
<< "The 'std::string::" << function << "()' returns zero when given itself as parameter "
<< "(" << varname << "." << function << "(" << varname << ")). As it is currently the "
<< "code is inefficient. It is also possible either the string searched ('"
<< varname << "') or searched for ('" << varname << "') is wrong/mixed in the code?";
reportError(tok, Severity::warning, "uselessCallsCompare", errmsg.str());
}
void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname)
{
std::ostringstream errmsg;
errmsg << "It is inefficient to swap a object with itself by calling '" << varname << ".swap(" << varname << ")'\n"
<< "The 'swap()' function has no logical effect when given itself as parameter "
<< "(" << varname << ".swap(" << varname << ")). As it is currently the "
<< "code is inefficient. It is possible either the object or the parameter is wrong/mixed in the code?";
reportError(tok, Severity::performance, "uselessCallsSwap", errmsg.str());
}
void CheckStl::uselessCallsSubstrError(const Token *tok, bool empty)
{
if (empty)
reportError(tok, Severity::performance, "uselessCallsSubstr", "Useless call of function 'substr' because it returns an empty string.");
else
reportError(tok, Severity::performance, "uselessCallsSubstr", "Useless call of function 'substr' because it returns a copy of the object. Use operator= instead.");
}
void CheckStl::uselessCallsEmptyError(const Token *tok)
{
reportError(tok, Severity::warning, "uselessCallsEmpty", "Useless call of function 'empty()'. Did you intend to call 'clear()' instead?");
}
void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function)
{
reportError(tok, Severity::warning, "uselessCallsRemove", "Return value of std::" + function + "() ignored. Elements remain in container.\n"
"The return value of std::" + function + "() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. "
"Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them.");
}
Jump to Line
Something went wrong with that request. Please try again.