Skip to content

Commit 4ebf54d

Browse files
pfultz2danmar
authored andcommitted
Fix issue 9437: Dont assume init list constructor for strings (cppcheck-opensource#2366)
* Fix issue 9437: Dont assume init list constuctor for strings * Update the schema * Add documentation
1 parent 5654630 commit 4ebf54d

7 files changed

Lines changed: 42 additions & 5 deletions

File tree

cfg/cppcheck-cfg.rng

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@
369369
<optional>
370370
<attribute name="opLessAllowed"><ref name="DATA-BOOL"/></attribute>
371371
</optional>
372+
<optional>
373+
<attribute name="hasInitializerListConstructor"><ref name="DATA-BOOL"/></attribute>
374+
</optional>
372375
<optional>
373376
<attribute name="itEndPattern"><text/></attribute>
374377
</optional>

cfg/std.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7821,7 +7821,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
78217821
<realloc init="true" realloc-arg="3">freopen</realloc>
78227822
<dealloc>fclose</dealloc>
78237823
</resource>
7824-
<container id="stdContainer" endPattern="&gt; !!::" opLessAllowed="false" itEndPattern="&gt; :: iterator|const_iterator|reverse_iterator|const_reverse_iterator">
7824+
<container id="stdContainer" endPattern="&gt; !!::" opLessAllowed="false" itEndPattern="&gt; :: iterator|const_iterator|reverse_iterator|const_reverse_iterator" hasInitializerListConstructor="true">
78257825
<type templateParameter="0"/>
78267826
<size>
78277827
<function name="resize" action="resize"/>
@@ -7960,7 +7960,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
79607960
<function name="sort" action="change-content"/>
79617961
</access>
79627962
</container>
7963-
<container id="stdAllString" inherits="stdContainer" opLessAllowed="true">
7963+
<container id="stdAllString" inherits="stdContainer" opLessAllowed="true" hasInitializerListConstructor="false">
79647964
<type string="std-like"/>
79657965
<size>
79667966
<function name="push_back" action="push"/>

lib/library.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
405405
const char* const opLessAllowed = node->Attribute("opLessAllowed");
406406
if (opLessAllowed)
407407
container.opLessAllowed = std::string(opLessAllowed) == "true";
408+
const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor");
409+
if (hasInitializerListConstructor)
410+
container.hasInitializerListConstructor = std::string(hasInitializerListConstructor) == "true";
408411

409412
for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) {
410413
const std::string containerNodeName = containerNode->Name();

lib/library.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ class CPPCHECKLIB Library {
196196
arrayLike_indexOp(false),
197197
stdStringLike(false),
198198
stdAssociativeLike(false),
199-
opLessAllowed(true) {
199+
opLessAllowed(true),
200+
hasInitializerListConstructor(false) {
200201
}
201202

202203
enum class Action {
@@ -219,6 +220,7 @@ class CPPCHECKLIB Library {
219220
bool stdStringLike;
220221
bool stdAssociativeLike;
221222
bool opLessAllowed;
223+
bool hasInitializerListConstructor;
222224

223225
Action getAction(const std::string& function) const {
224226
const std::map<std::string, Function>::const_iterator i = functions.find(function);

lib/valueflow.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3695,16 +3695,30 @@ static void valueFlowLifetimeConstructor(Token* tok,
36953695
}
36963696
}
36973697
}
3698+
3699+
static bool hasInitList(const Token* tok)
3700+
{
3701+
if (astIsPointer(tok))
3702+
return true;
3703+
if (astIsContainer(tok)) {
3704+
const Library::Container * library = getLibraryContainer(tok);
3705+
if (!library)
3706+
return false;
3707+
return library->hasInitializerListConstructor;
3708+
}
3709+
return false;
3710+
}
3711+
36983712
static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings)
36993713
{
37003714
if (!Token::Match(tok, "(|{"))
37013715
return;
37023716
Token* parent = tok->astParent();
37033717
while (Token::simpleMatch(parent, ","))
37043718
parent = parent->astParent();
3705-
if (Token::simpleMatch(parent, "{") && (astIsContainer(parent->astParent()) || astIsPointer(parent->astParent()))) {
3719+
if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) {
37063720
valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings);
3707-
} else if (Token::simpleMatch(tok, "{") && (astIsContainer(parent) || astIsPointer(parent))) {
3721+
} else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) {
37083722
std::vector<const Token *> args = getArguments(tok);
37093723
for (const Token *argtok : args) {
37103724
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object};

man/reference-cfg-format.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ The size of the type is specified in bytes. Possible values for the "sign" attri
530530

531531
A lot of C++ libraries, among those the STL itself, provide containers with very similar functionality. Libraries can be used to tell cppcheck about their behaviour. Each container needs a unique ID. It can optionally have a startPattern, which must be a valid Token::Match pattern and an endPattern that is compared to the linked token of the first token with such a link. The optional attribute "inherits" takes an ID from a previously defined container.
532532

533+
The `hasInitializerListConstructor` attribute can be set when the container has a constructor taking an initializer list.
534+
533535
Inside the `<container>` tag, functions can be defined inside of the tags `<size>`, `<access>` and `<other>` (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator".
534536

535537
The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown):

test/testautovariables.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,19 @@ class TestAutoVariables : public TestFixture {
20712071
" return var->c_str();\n"
20722072
"}\n");
20732073
ASSERT_EQUALS("", errout.str());
2074+
2075+
check("std::string f() {\n"
2076+
" std::vector<char> data{};\n"
2077+
" data.push_back('a');\n"
2078+
" return std::string{ data.data(), data.size() };\n"
2079+
"}\n");
2080+
ASSERT_EQUALS("", errout.str());
2081+
2082+
check("std::vector<char*> f() {\n"
2083+
" char a = 0;\n"
2084+
" return std::vector<char*>{&a};\n"
2085+
"}\n");
2086+
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str());
20742087
}
20752088

20762089
void danglingLifetime() {

0 commit comments

Comments
 (0)