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:
Paul Fultz II 2019-05-02 11:04:23 +02:00 committed by Daniel Marjamäki
parent 4edc248dae
commit 091f4bcf8d
13 changed files with 510 additions and 44 deletions

View File

@ -33,8 +33,10 @@
<container id="boostArray" startPattern="boost :: array|scoped_array &lt;" inherits="stdArray"/> <container id="boostArray" startPattern="boost :: array|scoped_array &lt;" inherits="stdArray"/>
<container id="boostCircularBuffer" startPattern="boost :: circular_buffer &lt;" inherits="stdContainer"/> <container id="boostCircularBuffer" startPattern="boost :: circular_buffer &lt;" inherits="stdContainer"/>
<container id="boostList" startPattern="boost :: list|slist &lt;" inherits="stdList"/> <container id="boostList" startPattern="boost :: list|slist &lt;" inherits="stdList"/>
<container id="boostMap" startPattern="boost :: map|flat_map|flat_multimap|multimap|unordered_map|unordered_multimap &lt;" inherits="stdMap"/> <container id="boostMultiMap" startPattern="boost :: flat_multimap|multimap|unordered_multimap &lt;" inherits="stdMultiMap"/>
<container id="boostSet" startPattern="boost :: set|flat_set|flat_multiset|multiset|unordered_set &lt;" inherits="stdSet"/> <container id="boostMultiSet" startPattern="boost :: flat_multiset|multiset &lt;" inherits="stdMultiSet"/>
<container id="boostMap" startPattern="boost :: map|flat_map|unordered_map &lt;" inherits="stdMap"/>
<container id="boostSet" startPattern="boost :: set|flat_set|unordered_set &lt;" inherits="stdSet"/>
<container id="boostVectorDeque" startPattern="boost :: deque|vector|small_vector|stable_vector|static_vector &lt;" inherits="stdVectorDeque"/> <container id="boostVectorDeque" startPattern="boost :: deque|vector|small_vector|stable_vector|static_vector &lt;" inherits="stdVectorDeque"/>
<!-- ########## Boost smart pointers ########## --> <!-- ########## Boost smart pointers ########## -->
<!-- https://www.boost.org/doc/libs/1_70_0/libs/smart_ptr/doc/html/smart_ptr.html --> <!-- https://www.boost.org/doc/libs/1_70_0/libs/smart_ptr/doc/html/smart_ptr.html -->

View File

@ -337,12 +337,19 @@
<zeroOrMore> <zeroOrMore>
<choice> <choice>
<element name="type"> <element name="type">
<choice> <optional>
<attribute name="templateParameter"><data type="integer"/></attribute> <attribute name="templateParameter"><data type="integer"/></attribute>
</optional>
<optional>
<attribute name="string"> <attribute name="string">
<value>std-like</value> <value>std-like</value>
</attribute> </attribute>
</choice> </optional>
<optional>
<attribute name="associative">
<value>std-like</value>
</attribute>
</optional>
<empty/> <empty/>
</element> </element>
<element name="size"> <element name="size">
@ -352,14 +359,16 @@
<zeroOrMore> <zeroOrMore>
<element name="function"> <element name="function">
<attribute name="name"><ref name="DATA-NAME"/></attribute> <attribute name="name"><ref name="DATA-NAME"/></attribute>
<choice> <optional>
<attribute name="action"> <attribute name="action">
<ref name="CONTAINER-ACTION"/> <ref name="CONTAINER-ACTION"/>
</attribute> </attribute>
</optional>
<optional>
<attribute name="yields"> <attribute name="yields">
<ref name="CONTAINER-YIELDS"/> <ref name="CONTAINER-YIELDS"/>
</attribute> </attribute>
</choice> </optional>
<empty/> <empty/>
</element> </element>
</zeroOrMore> </zeroOrMore>

View File

@ -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="size" yields="size"/>
<function name="empty" yields="empty"/> <function name="empty" yields="empty"/>
<function name="erase" action="erase"/> <function name="erase" action="erase"/>
<function name="insert" action="insert"/> <function name="insert" action="insert" yields="iterator"/>
<function name="emplace" action="push"/> <function name="emplace" action="push" yields="iterator"/>
<function name="swap" action="change"/> <function name="swap" action="change"/>
<function name="assign" action="change"/> <function name="assign" action="change"/>
</size> </size>
@ -7390,30 +7390,45 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
<function name="top" yields="item"/> <function name="top" yields="item"/>
</access> </access>
</container> </container>
<container id="stdSet" startPattern="std :: set|unordered_set|multiset|unordered_multiset &lt;" inherits="stdContainer"> <container id="stdMultiSet" startPattern="std :: multiset|unordered_multiset &lt;" inherits="stdContainer">
<type associative="std-like"/>
<access> <access>
<function name="find" action="find" yields="iterator"/> <function name="find" action="find" yields="iterator"/>
<function name="count" action="find"/> <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="rehash" action="change-internal"/>
<function name="lower_bound" yields="iterator"/> <function name="lower_bound" yields="iterator"/>
<function name="upper_bound" yields="iterator"/> <function name="upper_bound" yields="iterator"/>
</access> </access>
</container> </container>
<container id="stdMap" startPattern="std :: map|unordered_map|multimap|unordered_multimap &lt;" inherits="stdContainer"> <container id="stdMultiMap" startPattern="std :: multimap|unordered_multimap &lt;" inherits="stdContainer">
<type templateParameter="1"/> <type templateParameter="1" associative="std-like"/>
<access> <access>
<function name="at" yields="at_index"/> <function name="at" yields="at_index"/>
<function name="count" action="find"/> <function name="count" action="find"/>
<function name="find" action="find" yields="iterator"/> <function name="find" action="find" yields="iterator"/>
<function name="emplace_hint" action="push"/> <function name="emplace_hint" action="push" yields="iterator"/>
<function name="try_emplace" action="push"/>
<function name="insert_or_assign" action="push"/>
<function name="rehash" action="change-internal"/> <function name="rehash" action="change-internal"/>
<function name="lower_bound" yields="iterator"/> <function name="lower_bound" yields="iterator"/>
<function name="upper_bound" yields="iterator"/> <function name="upper_bound" yields="iterator"/>
</access> </access>
</container> </container>
<container id="stdSet" startPattern="std :: set|unordered_set &lt;" 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 &lt;" 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 &lt;" inherits="stdContainer"> <container id="stdList" startPattern="std :: list|forward_list &lt;" inherits="stdContainer">
<size> <size>
<function name="push_back" action="push"/> <function name="push_back" action="push"/>

View File

@ -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); 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? * Is container.size() slow?

View File

@ -62,6 +62,7 @@ public:
CheckStl checkStl(tokenizer, settings, errorLogger); CheckStl checkStl(tokenizer, settings, errorLogger);
checkStl.erase(); checkStl.erase();
checkStl.if_find(); checkStl.if_find();
checkStl.checkFindInsert();
checkStl.iterators(); checkStl.iterators();
checkStl.mismatchingContainers(); checkStl.mismatchingContainers();
checkStl.missingComparison(); checkStl.missingComparison();
@ -132,6 +133,8 @@ public:
/** if (a.find(x)) - possibly incorrect condition */ /** if (a.find(x)) - possibly incorrect condition */
void if_find(); void if_find();
void checkFindInsert();
/** /**
* Suggest using empty() instead of checking size() against zero for containers. * Suggest using empty() instead of checking size() against zero for containers.
* Item 4 from Scott Meyers book "Effective STL". * 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 invalidPointerError(const Token* tok, const std::string& func, const std::string& pointer_name);
void stlBoundariesError(const Token* tok); void stlBoundariesError(const Token* tok);
void if_findError(const Token* tok, bool str); void if_findError(const Token* tok, bool str);
void checkFindInsertError(const Token *tok);
void sizeError(const Token* tok); void sizeError(const Token* tok);
void redundantIfRemoveError(const Token* tok); void redundantIfRemoveError(const Token* tok);
@ -239,6 +243,7 @@ private:
c.stlBoundariesError(nullptr); c.stlBoundariesError(nullptr);
c.if_findError(nullptr, false); c.if_findError(nullptr, false);
c.if_findError(nullptr, true); c.if_findError(nullptr, true);
c.checkFindInsertError(nullptr);
c.string_c_strError(nullptr); c.string_c_strError(nullptr);
c.string_c_strReturn(nullptr); c.string_c_strReturn(nullptr);
c.string_c_strParam(nullptr, 0); c.string_c_strParam(nullptr, 0);
@ -270,6 +275,7 @@ private:
"- for vectors: using iterator/pointer after push_back has been used\n" "- for vectors: using iterator/pointer after push_back has been used\n"
"- optimisation: use empty() instead of size() to guarantee fast code\n" "- optimisation: use empty() instead of size() to guarantee fast code\n"
"- suspicious condition when using find\n" "- suspicious condition when using find\n"
"- unnecessary searching in associative containers\n"
"- redundant condition\n" "- redundant condition\n"
"- common mistakes when using string::c_str()\n" "- common mistakes when using string::c_str()\n"
"- useless calls of string and STL functions\n" "- useless calls of string and STL functions\n"

View File

@ -483,6 +483,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
const char* const string = containerNode->Attribute("string"); const char* const string = containerNode->Attribute("string");
if (string) if (string)
container.stdStringLike = std::string(string) == "std-like"; container.stdStringLike = std::string(string) == "std-like";
const char* const associative = containerNode->Attribute("associative");
if (associative)
container.stdAssociativeLike = std::string(associative) == "std-like";
} else } else
unknown_elements.insert(containerNodeName); unknown_elements.insert(containerNodeName);
} }

View File

@ -181,6 +181,7 @@ public:
size_templateArgNo(-1), size_templateArgNo(-1),
arrayLike_indexOp(false), arrayLike_indexOp(false),
stdStringLike(false), stdStringLike(false),
stdAssociativeLike(false),
opLessAllowed(true) { opLessAllowed(true) {
} }
@ -202,6 +203,7 @@ public:
int size_templateArgNo; int size_templateArgNo;
bool arrayLike_indexOp; bool arrayLike_indexOp;
bool stdStringLike; bool stdStringLike;
bool stdAssociativeLike;
bool opLessAllowed; bool opLessAllowed;
Action getAction(const std::string& function) const { Action getAction(const std::string& function) const {

View File

@ -852,6 +852,14 @@ public:
static bool returnsReference(const Function *function); 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 * @return token to ":" if the function is a constructor
* and it contains member initialization otherwise a nullptr is returned * and it contains member initialization otherwise a nullptr is returned

View File

@ -1759,6 +1759,79 @@ void Token::type(const ::Type *t)
tokType(eName); 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() TokenImpl::~TokenImpl()
{ {
delete mOriginalName; delete mOriginalName;

View File

@ -806,6 +806,12 @@ public:
return mTokType == eType ? mImpl->mType : nullptr; 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. * @return a pointer to the Enumerator associated with this token.
*/ */

View File

@ -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) static void valueFlowLifetimeConstructor(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{ {
if (!Token::Match(tok, "(|{")) if (!Token::Match(tok, "(|{"))
return; return;
if (const Type *t = getTypeOf(tok->previous())) { if (const Type *t = Token::typeOf(tok->previous())) {
const Scope *scope = t->classScope; const Scope *scope = t->classScope;
if (!scope) if (!scope)
return; return;

View File

@ -1330,6 +1330,11 @@ The following example provides a definition for std::vector, based on the defini
</container> </container>
</def> </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 ## 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. 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.

View File

@ -156,6 +156,7 @@ private:
TEST_CASE(loopAlgoIncrement); TEST_CASE(loopAlgoIncrement);
TEST_CASE(loopAlgoConditional); TEST_CASE(loopAlgoConditional);
TEST_CASE(loopAlgoMinMax); TEST_CASE(loopAlgoMinMax);
TEST_CASE(findInsert);
} }
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) {
@ -3844,6 +3845,228 @@ private:
true); true);
ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::accumulate algorithm instead of a raw loop.\n", errout.str()); 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) REGISTER_TEST(TestStl)