Find reference to dangling unique ptr (#3344)
This commit is contained in:
parent
b409d4a598
commit
8efe1d4ab4
|
@ -80,14 +80,18 @@
|
|||
<container id="boostDeque" startPattern="boost :: deque <" inherits="stdDeque"/>
|
||||
<!-- ########## Boost smart pointers ########## -->
|
||||
<!-- https://www.boost.org/doc/libs/1_70_0/libs/smart_ptr/doc/html/smart_ptr.html -->
|
||||
<smart-pointer class-name="boost::scoped_ptr"/>
|
||||
<smart-pointer class-name="boost::scoped_ptr">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<!-- <smart-pointer class-name="boost::scoped_array"/> Already configured as container -->
|
||||
<smart-pointer class-name="boost::shared_ptr"/>
|
||||
<smart-pointer class-name="boost::weak_ptr"/>
|
||||
<smart-pointer class-name="boost::intrusive_ptr"/>
|
||||
<smart-pointer class-name="boost::local_shared_ptr"/>
|
||||
<!-- https://www.boost.org/doc/libs/1_70_0/doc/html/boost/movelib/unique_ptr.html -->
|
||||
<smart-pointer class-name="boost::movelib::unique_ptr"/>
|
||||
<smart-pointer class-name="boost::movelib::unique_ptr">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<!-- ########## Boost functions ########## -->
|
||||
<!-- https://www.boost.org/doc/libs/1_69_0/doc/html/boost/algorithm/join.html -->
|
||||
<!-- template<typename SequenceSequenceT, typename Range1T>
|
||||
|
|
|
@ -487,6 +487,11 @@
|
|||
|
||||
<element name="smart-pointer">
|
||||
<attribute name="class-name"><ref name="DATA-EXTNAME"/></attribute>
|
||||
<optional>
|
||||
<element name="unique">
|
||||
<empty/>
|
||||
</element>
|
||||
</optional>
|
||||
</element>
|
||||
|
||||
<element name="type-checks">
|
||||
|
|
|
@ -5227,8 +5227,12 @@
|
|||
<smart-pointer class-name="QSharedPointer"/>
|
||||
<smart-pointer class-name="QWeakPointer"/>
|
||||
<smart-pointer class-name="QPointer"/>
|
||||
<smart-pointer class-name="QScopedPointer"/>
|
||||
<smart-pointer class-name="QScopedArrayPointer"/>
|
||||
<smart-pointer class-name="QScopedPointer">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<smart-pointer class-name="QScopedArrayPointer">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<!-- Internal Smart Pointers -->
|
||||
<smart-pointer class-name="QtPatternist::AutoPtr"/>
|
||||
<smart-pointer class-name="QGuard"/>
|
||||
|
|
|
@ -8266,9 +8266,13 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
|
|||
<type templateParameter="0"/>
|
||||
</container>
|
||||
<container id="stdString" startPattern="std :: string|wstring|u16string|u32string" endPattern="" inherits="stdAllString"/>
|
||||
<smart-pointer class-name="std::auto_ptr"/>
|
||||
<smart-pointer class-name="std::auto_ptr">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<smart-pointer class-name="std::shared_ptr"/>
|
||||
<smart-pointer class-name="std::unique_ptr"/>
|
||||
<smart-pointer class-name="std::unique_ptr">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<smart-pointer class-name="std::weak_ptr"/>
|
||||
<type-checks>
|
||||
<unusedvar>
|
||||
|
|
|
@ -5234,8 +5234,12 @@
|
|||
<podtype name="wxLogLevel"/>
|
||||
<!-- https://docs.wxwidgets.org/trunk/group__group__class__smartpointers.html -->
|
||||
<smart-pointer class-name="wxObjectDataPtr"/>
|
||||
<smart-pointer class-name="wxScopedArray"/>
|
||||
<smart-pointer class-name="wxScopedPtr"/>
|
||||
<smart-pointer class-name="wxScopedArray">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<smart-pointer class-name="wxScopedPtr">
|
||||
<unique/>
|
||||
</smart-pointer>
|
||||
<smart-pointer class-name="wxSharedPtr"/>
|
||||
<smart-pointer class-name="wxWeakRefDynamic"/>
|
||||
<smart-pointer class-name="wxWeakRef"/>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -432,8 +432,13 @@ public:
|
|||
|
||||
std::vector<std::string> defines; // to provide some library defines
|
||||
|
||||
std::unordered_set<std::string> smartPointers;
|
||||
struct SmartPointer {
|
||||
bool unique = false;
|
||||
};
|
||||
|
||||
std::map<std::string, SmartPointer> smartPointers;
|
||||
bool isSmartPointer(const Token *tok) const;
|
||||
const SmartPointer* detectSmartPointer(const Token* tok) const;
|
||||
|
||||
struct PodType {
|
||||
unsigned int size;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<LifetimeToken> 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<LifetimeToken> 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())
|
||||
|
|
|
@ -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<int> p = std::make_unique<int>(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<int>&);\n"
|
||||
"int& f() {\n"
|
||||
" std::unique_ptr<int> p = std::make_unique<int>(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<int>);\n"
|
||||
"int& f() {\n"
|
||||
" std::shared_ptr<int> p = std::make_shared<int>(1);\n"
|
||||
" g(p);\n"
|
||||
" return *p;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("std::shared_ptr<int> g();\n"
|
||||
"int& f() {\n"
|
||||
" return *g();\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("std::unique_ptr<int> 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<A> p = std::make_unique<A>();\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"
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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[] = "<?xml version=\"1.0\"?>\n"
|
||||
"<def>\n"
|
||||
|
|
Loading…
Reference in New Issue