diff --git a/cfg/boost.cfg b/cfg/boost.cfg
index ded96a399..9ed67d79a 100644
--- a/cfg/boost.cfg
+++ b/cfg/boost.cfg
@@ -80,14 +80,18 @@
-
+
+
+
-
+
+
+
diff --git a/cfg/std.cfg b/cfg/std.cfg
index 54860b0de..812c6e688 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -8266,9 +8266,13 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
-
+
+
+
-
+
+
+
diff --git a/cfg/wxwidgets.cfg b/cfg/wxwidgets.cfg
index 708c44b9c..e4a316e7d 100644
--- a/cfg/wxwidgets.cfg
+++ b/cfg/wxwidgets.cfg
@@ -5234,8 +5234,12 @@
-
-
+
+
+
+
+
+
diff --git a/lib/library.cpp b/lib/library.cpp
index 7d1ee95a1..57eefa9f5 100644
--- a/lib/library.cpp
+++ b/lib/library.cpp
@@ -498,8 +498,15 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
else if (nodename == "smart-pointer") {
const char *className = node->Attribute("class-name");
- if (className)
- smartPointers.insert(className);
+ if (!className)
+ return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name");
+ SmartPointer& smartPointer = smartPointers[className];
+ for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode;
+ smartPointerNode = smartPointerNode->NextSiblingElement()) {
+ const std::string smartPointerNodeName = smartPointerNode->Name();
+ if (smartPointerNodeName == "unique")
+ smartPointer.unique = true;
+ }
}
else if (nodename == "type-checks") {
@@ -1501,14 +1508,19 @@ bool Library::isimporter(const std::string& file, const std::string &importer) c
return (it != mImporters.end() && it->second.count(importer) > 0);
}
-bool Library::isSmartPointer(const Token *tok) const
+bool Library::isSmartPointer(const Token* tok) const { return detectSmartPointer(tok); }
+
+const Library::SmartPointer* Library::detectSmartPointer(const Token* tok) const
{
std::string typestr;
while (Token::Match(tok, "%name%|::")) {
typestr += tok->str();
tok = tok->next();
}
- return smartPointers.find(typestr) != smartPointers.end();
+ auto it = smartPointers.find(typestr);
+ if (it == smartPointers.end())
+ return nullptr;
+ return &it->second;
}
CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok)
diff --git a/lib/library.h b/lib/library.h
index 040a6c5b8..dd2e20e66 100644
--- a/lib/library.h
+++ b/lib/library.h
@@ -432,8 +432,13 @@ public:
std::vector defines; // to provide some library defines
- std::unordered_set smartPointers;
+ struct SmartPointer {
+ bool unique = false;
+ };
+
+ std::map smartPointers;
bool isSmartPointer(const Token *tok) const;
+ const SmartPointer* detectSmartPointer(const Token* tok) const;
struct PodType {
unsigned int size;
diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp
index 14517d3d6..2f1222eed 100644
--- a/lib/symboldatabase.cpp
+++ b/lib/symboldatabase.cpp
@@ -6170,10 +6170,11 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
// we are past the end of the type
type = type->previous();
continue;
- } else if (settings->library.isSmartPointer(type)) {
+ } else if (const Library::SmartPointer* smartPointer = settings->library.detectSmartPointer(type)) {
const Token* argTok = Token::findsimplematch(type, "<");
if (!argTok)
continue;
+ valuetype->smartPointer = smartPointer;
valuetype->smartPointerTypeToken = argTok->next();
valuetype->smartPointerType = argTok->next()->type();
valuetype->type = ValueType::Type::NONSTD;
diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h
index 5101d7325..e40811e15 100644
--- a/lib/symboldatabase.h
+++ b/lib/symboldatabase.h
@@ -1201,16 +1201,21 @@ class CPPCHECKLIB ValueType {
public:
enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign;
enum Type { UNKNOWN_TYPE, NONSTD, RECORD, CONTAINER, ITERATOR, VOID, BOOL, CHAR, SHORT, WCHAR_T, INT, LONG, LONGLONG, UNKNOWN_INT, FLOAT, DOUBLE, LONGDOUBLE } type;
- nonneg int bits; ///< bitfield bitcount
- nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
- nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=**
- Reference reference = Reference::None;///< Is the outermost indirection of this type a reference or rvalue reference or not? pointer=2, Reference=LValue would be a T**&
- const Scope *typeScope; ///< if the type definition is seen this point out the type scope
- const ::Type *smartPointerType; ///< Smart pointer type
- const Token* smartPointerTypeToken; ///< Smart pointer type token
- const Library::Container *container; ///< If the type is a container defined in a cfg file, this is the used container
- const Token *containerTypeToken; ///< The container type token. the template argument token that defines the container element type.
- std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t" when type is CHAR.
+ nonneg int bits; ///< bitfield bitcount
+ nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
+ nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=**
+ Reference reference = Reference::None; ///< Is the outermost indirection of this type a reference or rvalue
+ ///< reference or not? pointer=2, Reference=LValue would be a T**&
+ const Scope* typeScope; ///< if the type definition is seen this point out the type scope
+ const ::Type* smartPointerType; ///< Smart pointer type
+ const Token* smartPointerTypeToken; ///< Smart pointer type token
+ const Library::SmartPointer* smartPointer; ///< Smart pointer
+ const Library::Container* container; ///< If the type is a container defined in a cfg file, this is the used
+ ///< container
+ const Token* containerTypeToken; ///< The container type token. the template argument token that defines the
+ ///< container element type.
+ std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t"
+ ///< when type is CHAR.
ValueType()
: sign(UNKNOWN_SIGN),
@@ -1221,6 +1226,7 @@ public:
typeScope(nullptr),
smartPointerType(nullptr),
smartPointerTypeToken(nullptr),
+ smartPointer(nullptr),
container(nullptr),
containerTypeToken(nullptr)
{}
@@ -1233,6 +1239,7 @@ public:
typeScope(nullptr),
smartPointerType(nullptr),
smartPointerTypeToken(nullptr),
+ smartPointer(nullptr),
container(nullptr),
containerTypeToken(nullptr)
{}
@@ -1245,6 +1252,7 @@ public:
typeScope(nullptr),
smartPointerType(nullptr),
smartPointerTypeToken(nullptr),
+ smartPointer(nullptr),
container(nullptr),
containerTypeToken(nullptr)
{}
@@ -1257,24 +1265,11 @@ public:
typeScope(nullptr),
smartPointerType(nullptr),
smartPointerTypeToken(nullptr),
+ smartPointer(nullptr),
container(nullptr),
containerTypeToken(nullptr),
originalTypeName(otn)
{}
- ValueType(const ValueType &vt)
- : sign(vt.sign)
- , type(vt.type)
- , bits(vt.bits)
- , pointer(vt.pointer)
- , constness(vt.constness)
- , reference(vt.reference)
- , typeScope(vt.typeScope)
- , smartPointerType(vt.smartPointerType)
- , smartPointerTypeToken(vt.smartPointerTypeToken)
- , container(vt.container)
- , containerTypeToken(vt.containerTypeToken)
- , originalTypeName(vt.originalTypeName)
- {}
static ValueType parseDecl(const Token *type, const Settings *settings);
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index 87f73471e..ae1a455a6 100644
--- a/lib/valueflow.cpp
+++ b/lib/valueflow.cpp
@@ -2615,6 +2615,15 @@ static void valueFlowReverse(TokenList* tokenlist,
valueFlowReverse(tok, nullptr, varToken, values, tokenlist, settings);
}
+static bool isUniqueSmartPointer(const Token* tok)
+{
+ if (!astIsSmartPointer(tok))
+ return false;
+ if (!tok->valueType()->smartPointer)
+ return false;
+ return tok->valueType()->smartPointer->unique;
+}
+
std::string lifetimeType(const Token *tok, const ValueFlow::Value *val)
{
std::string result;
@@ -2786,8 +2795,11 @@ static std::vector getLifetimeTokens(const Token* tok,
false);
}
}
- } else if (Token::Match(tok, ".|::|[")) {
+ } else if (Token::Match(tok, ".|::|[") || tok->isUnaryOp("*")) {
+
const Token *vartok = tok;
+ if (tok->isUnaryOp("*"))
+ vartok = tok->astOperand1();
while (vartok) {
if (vartok->str() == "[" || vartok->originalName() == "->")
vartok = vartok->astOperand1();
@@ -2800,7 +2812,8 @@ static std::vector getLifetimeTokens(const Token* tok,
if (!vartok)
return {{tok, std::move(errorPath)}};
const Variable *tokvar = vartok->variable();
- if (!astIsContainer(vartok) && !(tokvar && tokvar->isArray() && !tokvar->isArgument()) &&
+ if (!isUniqueSmartPointer(vartok) && !astIsContainer(vartok) &&
+ !(tokvar && tokvar->isArray() && !tokvar->isArgument()) &&
(Token::Match(vartok->astParent(), "[|*") || vartok->astParent()->originalName() == "->")) {
for (const ValueFlow::Value &v : vartok->values()) {
if (!v.isLocalLifetimeValue())
diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp
index ad4598d94..dd93b9694 100644
--- a/test/testautovariables.cpp
+++ b/test/testautovariables.cpp
@@ -117,6 +117,7 @@ private:
TEST_CASE(returnReference19); // #9597
TEST_CASE(returnReference20); // #9536
TEST_CASE(returnReference21); // #9530
+ TEST_CASE(returnReference22);
TEST_CASE(returnReferenceFunction);
TEST_CASE(returnReferenceContainer);
TEST_CASE(returnReferenceLiteral);
@@ -1407,6 +1408,50 @@ private:
ASSERT_EQUALS("", errout.str());
}
+ void returnReference22()
+ {
+ check("int& f() {\n"
+ " std::unique_ptr p = std::make_unique(1);\n"
+ " return *p;\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout.str());
+
+ check("void g(const std::unique_ptr&);\n"
+ "int& f() {\n"
+ " std::unique_ptr p = std::make_unique(1);\n"
+ " g(p);\n"
+ " return *p;\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:5]: (error) Reference to local variable returned.\n", errout.str());
+
+ check("void g(std::shared_ptr);\n"
+ "int& f() {\n"
+ " std::shared_ptr p = std::make_shared(1);\n"
+ " g(p);\n"
+ " return *p;\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+
+ check("std::shared_ptr g();\n"
+ "int& f() {\n"
+ " return *g();\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+
+ check("std::unique_ptr g();\n"
+ "int& f() {\n"
+ " return *g();\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str());
+
+ check("struct A { int x; };\n"
+ "int& f() {\n"
+ " std::unique_ptr p = std::make_unique();\n"
+ " return p->x;\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str());
+ }
+
void returnReferenceFunction() {
check("int& f(int& a) {\n"
" return a;\n"
diff --git a/test/testexprengine.cpp b/test/testexprengine.cpp
index f23671a3d..387b69259 100644
--- a/test/testexprengine.cpp
+++ b/test/testexprengine.cpp
@@ -221,7 +221,7 @@ private:
std::string getRange(const char code[], const std::string &str, int linenr = 0) {
Settings settings;
settings.platform(cppcheck::Platform::Unix64);
- settings.library.smartPointers.insert("std::shared_ptr");
+ settings.library.smartPointers["std::shared_ptr"];
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
@@ -248,7 +248,7 @@ private:
settings->bugHunting = true;
settings->debugBugHunting = true;
settings->platform(cppcheck::Platform::Unix64);
- settings->library.smartPointers.insert("std::shared_ptr");
+ settings->library.smartPointers["std::shared_ptr"];
Tokenizer tokenizer(settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp
index f770826d2..5f48b740c 100644
--- a/test/testleakautovar.cpp
+++ b/test/testleakautovar.cpp
@@ -48,8 +48,9 @@ private:
settings.library.setalloc("fopen", id, -1);
settings.library.setrealloc("freopen", id, -1, 3);
settings.library.setdealloc("fclose", id, 1);
- settings.library.smartPointers.insert("std::shared_ptr");
- settings.library.smartPointers.insert("std::unique_ptr");
+ settings.library.smartPointers["std::shared_ptr"];
+ settings.library.smartPointers["std::unique_ptr"];
+ settings.library.smartPointers["std::unique_ptr"].unique = true;
const char xmldata[] = "\n"
"\n"