Add check for unnecessary search before insertion
This will warn for cases where searching in an associative container happens before insertion, like this: ```cpp void f1(std::set<unsigned>& s, unsigned x) { if (s.find(x) == s.end()) { s.insert(x); } } void f2(std::map<unsigned, unsigned>& m, unsigned x) { if (m.find(x) == m.end()) { m.emplace(x, 1); } else { m[x] = 1; } } ``` In the case of the map it could be written as `m[x] = 1` as it will create the key if it doesnt exist, so the extra search is not necessary. I have this marked as `performance` as it is mostly concerning performance, but there could be a copy-paste error possibly, although I dont think thats common.
This commit is contained in:
parent
4edc248dae
commit
091f4bcf8d
|
@ -33,8 +33,10 @@
|
|||
<container id="boostArray" startPattern="boost :: array|scoped_array <" inherits="stdArray"/>
|
||||
<container id="boostCircularBuffer" startPattern="boost :: circular_buffer <" inherits="stdContainer"/>
|
||||
<container id="boostList" startPattern="boost :: list|slist <" inherits="stdList"/>
|
||||
<container id="boostMap" startPattern="boost :: map|flat_map|flat_multimap|multimap|unordered_map|unordered_multimap <" inherits="stdMap"/>
|
||||
<container id="boostSet" startPattern="boost :: set|flat_set|flat_multiset|multiset|unordered_set <" inherits="stdSet"/>
|
||||
<container id="boostMultiMap" startPattern="boost :: flat_multimap|multimap|unordered_multimap <" inherits="stdMultiMap"/>
|
||||
<container id="boostMultiSet" startPattern="boost :: flat_multiset|multiset <" inherits="stdMultiSet"/>
|
||||
<container id="boostMap" startPattern="boost :: map|flat_map|unordered_map <" inherits="stdMap"/>
|
||||
<container id="boostSet" startPattern="boost :: set|flat_set|unordered_set <" inherits="stdSet"/>
|
||||
<container id="boostVectorDeque" startPattern="boost :: deque|vector|small_vector|stable_vector|static_vector <" inherits="stdVectorDeque"/>
|
||||
<!-- ########## Boost smart pointers ########## -->
|
||||
<!-- https://www.boost.org/doc/libs/1_70_0/libs/smart_ptr/doc/html/smart_ptr.html -->
|
||||
|
|
|
@ -337,12 +337,19 @@
|
|||
<zeroOrMore>
|
||||
<choice>
|
||||
<element name="type">
|
||||
<choice>
|
||||
<optional>
|
||||
<attribute name="templateParameter"><data type="integer"/></attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="string">
|
||||
<value>std-like</value>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="associative">
|
||||
<value>std-like</value>
|
||||
</attribute>
|
||||
</optional>
|
||||
<empty/>
|
||||
</element>
|
||||
<element name="size">
|
||||
|
@ -352,14 +359,16 @@
|
|||
<zeroOrMore>
|
||||
<element name="function">
|
||||
<attribute name="name"><ref name="DATA-NAME"/></attribute>
|
||||
<choice>
|
||||
<optional>
|
||||
<attribute name="action">
|
||||
<ref name="CONTAINER-ACTION"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="yields">
|
||||
<ref name="CONTAINER-YIELDS"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<empty/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
|
|
33
cfg/std.cfg
33
cfg/std.cfg
|
@ -7325,8 +7325,8 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
|||
<function name="size" yields="size"/>
|
||||
<function name="empty" yields="empty"/>
|
||||
<function name="erase" action="erase"/>
|
||||
<function name="insert" action="insert"/>
|
||||
<function name="emplace" action="push"/>
|
||||
<function name="insert" action="insert" yields="iterator"/>
|
||||
<function name="emplace" action="push" yields="iterator"/>
|
||||
<function name="swap" action="change"/>
|
||||
<function name="assign" action="change"/>
|
||||
</size>
|
||||
|
@ -7390,30 +7390,45 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
|||
<function name="top" yields="item"/>
|
||||
</access>
|
||||
</container>
|
||||
<container id="stdSet" startPattern="std :: set|unordered_set|multiset|unordered_multiset <" inherits="stdContainer">
|
||||
<container id="stdMultiSet" startPattern="std :: multiset|unordered_multiset <" inherits="stdContainer">
|
||||
<type associative="std-like"/>
|
||||
<access>
|
||||
<function name="find" action="find" yields="iterator"/>
|
||||
<function name="count" action="find"/>
|
||||
<function name="emplace_hint" action="push"/>
|
||||
<function name="emplace_hint" action="push" yields="iterator"/>
|
||||
<function name="rehash" action="change-internal"/>
|
||||
<function name="lower_bound" yields="iterator"/>
|
||||
<function name="upper_bound" yields="iterator"/>
|
||||
</access>
|
||||
</container>
|
||||
<container id="stdMap" startPattern="std :: map|unordered_map|multimap|unordered_multimap <" inherits="stdContainer">
|
||||
<type templateParameter="1"/>
|
||||
<container id="stdMultiMap" startPattern="std :: multimap|unordered_multimap <" inherits="stdContainer">
|
||||
<type templateParameter="1" associative="std-like"/>
|
||||
<access>
|
||||
<function name="at" yields="at_index"/>
|
||||
<function name="count" action="find"/>
|
||||
<function name="find" action="find" yields="iterator"/>
|
||||
<function name="emplace_hint" action="push"/>
|
||||
<function name="try_emplace" action="push"/>
|
||||
<function name="insert_or_assign" action="push"/>
|
||||
<function name="emplace_hint" action="push" yields="iterator"/>
|
||||
<function name="rehash" action="change-internal"/>
|
||||
<function name="lower_bound" yields="iterator"/>
|
||||
<function name="upper_bound" yields="iterator"/>
|
||||
</access>
|
||||
</container>
|
||||
<container id="stdSet" startPattern="std :: set|unordered_set <" inherits="stdMultiSet">
|
||||
<access>
|
||||
<function name="insert" action="push"/>
|
||||
<function name="emplace" action="push"/>
|
||||
<function name="try_emplace" action="push"/>
|
||||
<function name="insert_or_assign" action="push"/>
|
||||
</access>
|
||||
</container>
|
||||
<container id="stdMap" startPattern="std :: map|unordered_map <" inherits="stdMultiMap">
|
||||
<access>
|
||||
<function name="insert" action="push"/>
|
||||
<function name="emplace" action="push"/>
|
||||
<function name="try_emplace" action="push"/>
|
||||
<function name="insert_or_assign" action="push"/>
|
||||
</access>
|
||||
</container>
|
||||
<container id="stdList" startPattern="std :: list|forward_list <" inherits="stdContainer">
|
||||
<size>
|
||||
<function name="push_back" action="push"/>
|
||||
|
|
142
lib/checkstl.cpp
142
lib/checkstl.cpp
|
@ -1237,6 +1237,148 @@ void CheckStl::if_findError(const Token *tok, bool str)
|
|||
reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked.", CWE398, false);
|
||||
}
|
||||
|
||||
static std::pair<const Token *, const Token *> isMapFind(const Token *tok)
|
||||
{
|
||||
if (!Token::simpleMatch(tok, "("))
|
||||
return {};
|
||||
if (!Token::simpleMatch(tok->astOperand1(), "."))
|
||||
return {};
|
||||
if (!astIsContainer(tok->astOperand1()->astOperand1()))
|
||||
return {};
|
||||
const Token * contTok = tok->astOperand1()->astOperand1();
|
||||
const Library::Container * container = contTok->valueType()->container;
|
||||
if (!container)
|
||||
return {};
|
||||
if (!container->stdAssociativeLike)
|
||||
return {};
|
||||
if (!Token::Match(tok->astOperand1(), ". find|count ("))
|
||||
return {};
|
||||
if (!tok->astOperand2())
|
||||
return {};
|
||||
return {contTok, tok->astOperand2()};
|
||||
}
|
||||
|
||||
static const Token *skipLocalVars(const Token *tok)
|
||||
{
|
||||
if (!tok)
|
||||
return tok;
|
||||
if (Token::simpleMatch(tok, "{"))
|
||||
return skipLocalVars(tok->next());
|
||||
const Scope *scope = tok->scope();
|
||||
|
||||
const Token *top = tok->astTop();
|
||||
if (!top) {
|
||||
const Token *semi = Token::findsimplematch(tok, ";");
|
||||
if (!semi)
|
||||
return tok;
|
||||
if (!Token::Match(semi->previous(), "%var% ;"))
|
||||
return tok;
|
||||
const Token *varTok = semi->previous();
|
||||
const Variable *var = varTok->variable();
|
||||
if (!var)
|
||||
return tok;
|
||||
if (var->nameToken() != varTok)
|
||||
return tok;
|
||||
return skipLocalVars(semi->next());
|
||||
}
|
||||
if (Token::Match(top, "%assign%")) {
|
||||
const Token *varTok = top->astOperand1();
|
||||
if (!Token::Match(varTok, "%var%"))
|
||||
return tok;
|
||||
const Variable *var = varTok->variable();
|
||||
if (!var)
|
||||
return tok;
|
||||
if (var->scope() != scope)
|
||||
return tok;
|
||||
const Token *endTok = nextAfterAstRightmostLeaf(top);
|
||||
if (!endTok)
|
||||
return tok;
|
||||
return skipLocalVars(endTok->next());
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
static const Token *findInsertValue(const Token *tok, const Token *containerTok, const Token *keyTok, const Library &library)
|
||||
{
|
||||
const Token *startTok = skipLocalVars(tok);
|
||||
const Token *top = startTok->astTop();
|
||||
|
||||
const Token *icontainerTok = nullptr;
|
||||
const Token *ikeyTok = nullptr;
|
||||
const Token *ivalueTok = nullptr;
|
||||
if (Token::simpleMatch(top, "=") && Token::simpleMatch(top->astOperand1(), "[")) {
|
||||
icontainerTok = top->astOperand1()->astOperand1();
|
||||
ikeyTok = top->astOperand1()->astOperand2();
|
||||
ivalueTok = top->astOperand2();
|
||||
}
|
||||
if (Token::simpleMatch(top, "(") && Token::Match(top->astOperand1(), ". insert|emplace (") && !astIsIterator(top->astOperand1()->tokAt(2))) {
|
||||
icontainerTok = top->astOperand1()->astOperand1();
|
||||
const Token *itok = top->astOperand1()->tokAt(2)->astOperand2();
|
||||
if (Token::simpleMatch(itok, ",")) {
|
||||
ikeyTok = itok->astOperand1();
|
||||
ivalueTok = itok->astOperand2();
|
||||
} else {
|
||||
ikeyTok = itok;
|
||||
}
|
||||
}
|
||||
if (!ikeyTok || !icontainerTok)
|
||||
return nullptr;
|
||||
if (isSameExpression(true, true, containerTok, icontainerTok, library, true, false) &&
|
||||
isSameExpression(true, true, keyTok, ikeyTok, library, true, true)) {
|
||||
if (ivalueTok)
|
||||
return ivalueTok;
|
||||
else
|
||||
return ikeyTok;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CheckStl::checkFindInsert()
|
||||
{
|
||||
if (!mSettings->isEnabled(Settings::PERFORMANCE))
|
||||
return;
|
||||
|
||||
const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||
for (const Scope *scope : symbolDatabase->functionScopes) {
|
||||
for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (!Token::simpleMatch(tok, "if ("))
|
||||
continue;
|
||||
if (!Token::simpleMatch(tok->next()->link(), ") {"))
|
||||
continue;
|
||||
if (!Token::Match(tok->next()->astOperand2(), "%comp%"))
|
||||
continue;
|
||||
const Token *condTok = tok->next()->astOperand2();
|
||||
const Token *containerTok;
|
||||
const Token *keyTok;
|
||||
std::tie(containerTok, keyTok) = isMapFind(condTok->astOperand1());
|
||||
if (!containerTok)
|
||||
continue;
|
||||
|
||||
const Token *thenTok = tok->next()->link()->next();
|
||||
const Token *valueTok = findInsertValue(thenTok, containerTok, keyTok, mSettings->library);
|
||||
if (!valueTok)
|
||||
continue;
|
||||
|
||||
if (Token::simpleMatch(thenTok->link(), "} else {")) {
|
||||
const Token *valueTok2 =
|
||||
findInsertValue(thenTok->link()->tokAt(2), containerTok, keyTok, mSettings->library);
|
||||
if (!valueTok2)
|
||||
continue;
|
||||
if (isSameExpression(true, true, valueTok, valueTok2, mSettings->library, true, true)) {
|
||||
checkFindInsertError(valueTok);
|
||||
}
|
||||
} else {
|
||||
checkFindInsertError(valueTok);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckStl::checkFindInsertError(const Token *tok)
|
||||
{
|
||||
reportError(
|
||||
tok, Severity::performance, "stlFindInsert", "Searching before insertion is not necessary.", CWE398, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is container.size() slow?
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
CheckStl checkStl(tokenizer, settings, errorLogger);
|
||||
checkStl.erase();
|
||||
checkStl.if_find();
|
||||
checkStl.checkFindInsert();
|
||||
checkStl.iterators();
|
||||
checkStl.mismatchingContainers();
|
||||
checkStl.missingComparison();
|
||||
|
@ -132,6 +133,8 @@ public:
|
|||
/** if (a.find(x)) - possibly incorrect condition */
|
||||
void if_find();
|
||||
|
||||
void checkFindInsert();
|
||||
|
||||
/**
|
||||
* Suggest using empty() instead of checking size() against zero for containers.
|
||||
* Item 4 from Scott Meyers book "Effective STL".
|
||||
|
@ -202,6 +205,7 @@ private:
|
|||
void invalidPointerError(const Token* tok, const std::string& func, const std::string& pointer_name);
|
||||
void stlBoundariesError(const Token* tok);
|
||||
void if_findError(const Token* tok, bool str);
|
||||
void checkFindInsertError(const Token *tok);
|
||||
void sizeError(const Token* tok);
|
||||
void redundantIfRemoveError(const Token* tok);
|
||||
|
||||
|
@ -239,6 +243,7 @@ private:
|
|||
c.stlBoundariesError(nullptr);
|
||||
c.if_findError(nullptr, false);
|
||||
c.if_findError(nullptr, true);
|
||||
c.checkFindInsertError(nullptr);
|
||||
c.string_c_strError(nullptr);
|
||||
c.string_c_strReturn(nullptr);
|
||||
c.string_c_strParam(nullptr, 0);
|
||||
|
@ -270,6 +275,7 @@ private:
|
|||
"- for vectors: using iterator/pointer after push_back has been used\n"
|
||||
"- optimisation: use empty() instead of size() to guarantee fast code\n"
|
||||
"- suspicious condition when using find\n"
|
||||
"- unnecessary searching in associative containers\n"
|
||||
"- redundant condition\n"
|
||||
"- common mistakes when using string::c_str()\n"
|
||||
"- useless calls of string and STL functions\n"
|
||||
|
|
|
@ -483,6 +483,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
|||
const char* const string = containerNode->Attribute("string");
|
||||
if (string)
|
||||
container.stdStringLike = std::string(string) == "std-like";
|
||||
const char* const associative = containerNode->Attribute("associative");
|
||||
if (associative)
|
||||
container.stdAssociativeLike = std::string(associative) == "std-like";
|
||||
} else
|
||||
unknown_elements.insert(containerNodeName);
|
||||
}
|
||||
|
|
|
@ -181,6 +181,7 @@ public:
|
|||
size_templateArgNo(-1),
|
||||
arrayLike_indexOp(false),
|
||||
stdStringLike(false),
|
||||
stdAssociativeLike(false),
|
||||
opLessAllowed(true) {
|
||||
}
|
||||
|
||||
|
@ -202,6 +203,7 @@ public:
|
|||
int size_templateArgNo;
|
||||
bool arrayLike_indexOp;
|
||||
bool stdStringLike;
|
||||
bool stdAssociativeLike;
|
||||
bool opLessAllowed;
|
||||
|
||||
Action getAction(const std::string& function) const {
|
||||
|
|
|
@ -852,6 +852,14 @@ public:
|
|||
|
||||
static bool returnsReference(const Function *function);
|
||||
|
||||
const Token* returnDefEnd() const {
|
||||
if (this->hasTrailingReturnType()) {
|
||||
return Token::findsimplematch(retDef, "{");
|
||||
} else {
|
||||
return tokenDef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return token to ":" if the function is a constructor
|
||||
* and it contains member initialization otherwise a nullptr is returned
|
||||
|
|
|
@ -1759,6 +1759,79 @@ void Token::type(const ::Type *t)
|
|||
tokType(eName);
|
||||
}
|
||||
|
||||
const ::Type *Token::typeOf(const Token *tok)
|
||||
{
|
||||
if (Token::simpleMatch(tok, "return")) {
|
||||
const Scope *scope = tok->scope();
|
||||
if (!scope)
|
||||
return nullptr;
|
||||
const Function *function = scope->function;
|
||||
if (!function)
|
||||
return nullptr;
|
||||
return function->retType;
|
||||
} else if (Token::Match(tok, "%type%")) {
|
||||
return tok->type();
|
||||
} else if (Token::Match(tok, "%var%")) {
|
||||
const Variable *var = tok->variable();
|
||||
if (!var)
|
||||
return nullptr;
|
||||
return var->type();
|
||||
} else if (Token::Match(tok, "%name%")) {
|
||||
const Function *function = tok->function();
|
||||
if (!function)
|
||||
return nullptr;
|
||||
return function->retType;
|
||||
} else if (Token::simpleMatch(tok, "=")) {
|
||||
return Token::typeOf(tok->astOperand1());
|
||||
} else if (Token::simpleMatch(tok, ".")) {
|
||||
return Token::typeOf(tok->astOperand2());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::pair<const Token*, const Token*> Token::typeDecl(const Token * tok)
|
||||
{
|
||||
if (Token::simpleMatch(tok, "return")) {
|
||||
const Scope *scope = tok->scope();
|
||||
if (!scope)
|
||||
return {};
|
||||
const Function *function = scope->function;
|
||||
if (!function)
|
||||
return {};
|
||||
return {function->retDef, function->returnDefEnd()};
|
||||
} else if (Token::Match(tok, "%type%")) {
|
||||
return {tok, tok->next()};
|
||||
} else if (Token::Match(tok, "%var%")) {
|
||||
const Variable *var = tok->variable();
|
||||
if (!var)
|
||||
return {};
|
||||
if (!var->typeStartToken() || !var->typeEndToken())
|
||||
return {};
|
||||
return {var->typeStartToken(), var->typeEndToken()->next()};
|
||||
} else if (Token::Match(tok, "%name%")) {
|
||||
const Function *function = tok->function();
|
||||
if (!function)
|
||||
return {};
|
||||
return {function->retDef, function->returnDefEnd()};
|
||||
} else if (Token::simpleMatch(tok, "=")) {
|
||||
return Token::typeDecl(tok->astOperand1());
|
||||
} else if (Token::simpleMatch(tok, ".")) {
|
||||
return Token::typeDecl(tok->astOperand2());
|
||||
} else {
|
||||
const ::Type * t = typeOf(tok);
|
||||
if (!t || !t->classDef)
|
||||
return {};
|
||||
return {t->classDef->next(), t->classDef->tokAt(2)};
|
||||
}
|
||||
}
|
||||
std::string Token::typeStr(const Token* tok)
|
||||
{
|
||||
std::pair<const Token*, const Token*> r = Token::typeDecl(tok);
|
||||
if (!r.first || !r.second)
|
||||
return "";
|
||||
return r.first->stringifyList(r.second, false);
|
||||
}
|
||||
|
||||
TokenImpl::~TokenImpl()
|
||||
{
|
||||
delete mOriginalName;
|
||||
|
|
|
@ -806,6 +806,12 @@ public:
|
|||
return mTokType == eType ? mImpl->mType : nullptr;
|
||||
}
|
||||
|
||||
static const ::Type *typeOf(const Token *tok);
|
||||
|
||||
static std::pair<const Token*, const Token*> typeDecl(const Token * tok);
|
||||
|
||||
static std::string typeStr(const Token* tok);
|
||||
|
||||
/**
|
||||
* @return a pointer to the Enumerator associated with this token.
|
||||
*/
|
||||
|
|
|
@ -3142,39 +3142,11 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
|
|||
}
|
||||
}
|
||||
|
||||
static const Type *getTypeOf(const Token *tok)
|
||||
{
|
||||
if (Token::simpleMatch(tok, "return")) {
|
||||
const Scope *scope = tok->scope();
|
||||
if (!scope)
|
||||
return nullptr;
|
||||
const Function *function = scope->function;
|
||||
if (!function)
|
||||
return nullptr;
|
||||
return function->retType;
|
||||
} else if (Token::Match(tok, "%type%")) {
|
||||
return tok->type();
|
||||
} else if (Token::Match(tok, "%var%")) {
|
||||
const Variable *var = tok->variable();
|
||||
if (!var)
|
||||
return nullptr;
|
||||
return var->type();
|
||||
} else if (Token::Match(tok, "%name%")) {
|
||||
const Function *function = tok->function();
|
||||
if (!function)
|
||||
return nullptr;
|
||||
return function->retType;
|
||||
} else if (Token::simpleMatch(tok, "=")) {
|
||||
return getTypeOf(tok->astOperand1());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void valueFlowLifetimeConstructor(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
||||
{
|
||||
if (!Token::Match(tok, "(|{"))
|
||||
return;
|
||||
if (const Type *t = getTypeOf(tok->previous())) {
|
||||
if (const Type *t = Token::typeOf(tok->previous())) {
|
||||
const Scope *scope = t->classScope;
|
||||
if (!scope)
|
||||
return;
|
||||
|
|
|
@ -1330,6 +1330,11 @@ The following example provides a definition for std::vector, based on the defini
|
|||
</container>
|
||||
</def>
|
||||
|
||||
The tag `<type>` can be added as well to provide more information about the type of container. Here is some of the attributes that can be set:
|
||||
|
||||
* `string='std-like'` can be set for containers that match `std::string` interfaces.
|
||||
* `associative='std-like'` can be set for containers that match C++'s `AssociativeContainer` interfaces.
|
||||
|
||||
## HTML Report
|
||||
|
||||
You can convert the XML output from cppcheck into a HTML report. You'll need Python and the pygments module (<http://pygments.org/)> for this to work. In the Cppcheck source tree there is a folder htmlreport that contains a script that transforms a Cppcheck XML file into HTML output.
|
||||
|
|
223
test/teststl.cpp
223
test/teststl.cpp
|
@ -156,6 +156,7 @@ private:
|
|||
TEST_CASE(loopAlgoIncrement);
|
||||
TEST_CASE(loopAlgoConditional);
|
||||
TEST_CASE(loopAlgoMinMax);
|
||||
TEST_CASE(findInsert);
|
||||
}
|
||||
|
||||
void check(const char code[], const bool inconclusive=false, const Standards::cppstd_t cppstandard=Standards::CPP11) {
|
||||
|
@ -3844,6 +3845,228 @@ private:
|
|||
true);
|
||||
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str());
|
||||
}
|
||||
|
||||
void findInsert() {
|
||||
check("void f1(std::set<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f2(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.find(x) == m.end()) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f3(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f4(std::set<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f5(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f6(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f1(std::unordered_set<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f2(std::unordered_map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.find(x) == m.end()) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f3(std::unordered_map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f4(std::unordered_set<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f5(std::unordered_map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void f6(std::unordered_map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:3]: (performance) Searching before insertion is not necessary.\n", errout.str());
|
||||
|
||||
check("void g1(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.find(x) == m.end()) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 2;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void g1(std::map<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 2;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f1(QSet<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f1(std::multiset<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f2(std::multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.find(x) == m.end()) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f3(std::multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f4(std::multiset<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f5(std::multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f1(std::unordered_multiset<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f2(std::unordered_multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.find(x) == m.end()) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f3(std::unordered_multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f4(std::unordered_multiset<unsigned>& s, unsigned x) {\n"
|
||||
" if (s.find(x) == s.end()) {\n"
|
||||
" s.insert(x);\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f5(std::unordered_multimap<unsigned, unsigned>& m, unsigned x) {\n"
|
||||
" if (m.count(x) == 0) {\n"
|
||||
" m.emplace(x, 1);\n"
|
||||
" } else {\n"
|
||||
" m[x] = 1;\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestStl)
|
||||
|
|
Loading…
Reference in New Issue