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"