Added new attribute "arg" to <alloc> and <dealloc> to specify the argument that is allocated/deallocated.
This fixes several issues with allocation functions in windows.cfg, such as HeapAlloc() and VirtualAllocEx() (#7503)
This commit is contained in:
parent
fbc499d033
commit
64d2fd2f57
|
@ -1001,7 +1001,7 @@
|
|||
<memory>
|
||||
<alloc>HeapAlloc</alloc>
|
||||
<alloc>HeapReAlloc</alloc>
|
||||
<dealloc>HeapFree</dealloc>
|
||||
<dealloc arg="3">HeapFree</dealloc>
|
||||
</memory>
|
||||
<memory>
|
||||
<alloc>IoAllocateErrorLogEntry</alloc>
|
||||
|
@ -1062,7 +1062,7 @@
|
|||
<memory>
|
||||
<alloc>VirtualAllocEx</alloc>
|
||||
<alloc>VirtualAllocExNuma</alloc>
|
||||
<dealloc>VirtualFreeEx</dealloc>
|
||||
<dealloc arg="2">VirtualFreeEx</dealloc>
|
||||
</memory>
|
||||
<memory>
|
||||
<alloc>LocalAlloc</alloc>
|
||||
|
|
|
@ -207,7 +207,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
|
||||
if (tok->str() == "(" && tok->previous()->isName()) {
|
||||
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
|
||||
functionCall(tok->previous(), varInfo, allocation);
|
||||
functionCall(tok->previous(), varInfo, allocation, nullptr);
|
||||
tok = tok->link();
|
||||
continue;
|
||||
}
|
||||
|
@ -274,9 +274,9 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
|
||||
// allocation?
|
||||
if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) {
|
||||
int i = _settings->library.alloc(varTok->next()->astOperand2()->previous());
|
||||
if (i > 0) {
|
||||
alloctype[varTok->varId()].type = i;
|
||||
const Library::AllocFunc* f = _settings->library.alloc(varTok->next()->astOperand2()->previous());
|
||||
if (f && f->arg == -1) {
|
||||
alloctype[varTok->varId()].type = f->groupId;
|
||||
alloctype[varTok->varId()].status = VarInfo::ALLOC;
|
||||
}
|
||||
} else if (_tokenizer->isCPP() && varTok->strAt(2) == "new") {
|
||||
|
@ -301,10 +301,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
if (innerTok->str() == ")")
|
||||
break;
|
||||
if (innerTok->str() == "(" && innerTok->previous()->isName()) {
|
||||
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
||||
if (allocation.type == 0)
|
||||
allocation.status = VarInfo::NOALLOC;
|
||||
functionCall(innerTok->previous(), varInfo, allocation);
|
||||
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
|
||||
functionCall(innerTok->previous(), varInfo, allocation, nullptr);
|
||||
innerTok = innerTok->link();
|
||||
}
|
||||
}
|
||||
|
@ -440,10 +438,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
|
||||
// Function call..
|
||||
else if (Token::Match(tok, "%type% (")) {
|
||||
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
||||
const Library::AllocFunc* af = _settings->library.dealloc(tok);
|
||||
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
|
||||
if (allocation.type == 0)
|
||||
allocation.status = VarInfo::NOALLOC;
|
||||
functionCall(tok, varInfo, allocation);
|
||||
functionCall(tok, varInfo, allocation, af);
|
||||
|
||||
tok = tok->next()->link();
|
||||
|
||||
|
@ -516,13 +515,14 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocI
|
|||
}
|
||||
}
|
||||
|
||||
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation)
|
||||
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af)
|
||||
{
|
||||
// Ignore function call?
|
||||
const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end());
|
||||
if (ignore)
|
||||
return;
|
||||
|
||||
int argNr = 1;
|
||||
for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
|
||||
if (_tokenizer->isCPP() && arg->str() == "new") {
|
||||
arg = arg->next();
|
||||
|
@ -537,10 +537,12 @@ void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const Va
|
|||
arg = arg->next();
|
||||
|
||||
// Is variable allocated?
|
||||
changeAllocStatus(varInfo, allocation, tok, arg);
|
||||
if (!af || af->arg == argNr)
|
||||
changeAllocStatus(varInfo, allocation, tok, arg);
|
||||
} else if (Token::Match(arg, "%name% (")) {
|
||||
functionCall(arg, varInfo, allocation);
|
||||
functionCall(arg, varInfo, allocation, af);
|
||||
}
|
||||
argNr++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ private:
|
|||
std::set<unsigned int> notzero);
|
||||
|
||||
/** parse function call */
|
||||
void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation);
|
||||
void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af);
|
||||
|
||||
/** parse changes in allocation status */
|
||||
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);
|
||||
|
|
|
@ -154,11 +154,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2,
|
|||
}
|
||||
|
||||
// Does tok2 point on a Library allocation function?
|
||||
const int alloctype = settings1->library.alloc(tok2);
|
||||
const int alloctype = settings1->library.alloc(tok2, -1);
|
||||
if (alloctype > 0) {
|
||||
if (alloctype == settings1->library.dealloc("free"))
|
||||
if (alloctype == settings1->library.deallocId("free"))
|
||||
return Malloc;
|
||||
if (alloctype == settings1->library.dealloc("fclose"))
|
||||
if (alloctype == settings1->library.deallocId("fclose"))
|
||||
return File;
|
||||
return Library::ismemory(alloctype) ? OtherMem : OtherRes;
|
||||
}
|
||||
|
@ -227,30 +227,34 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok
|
|||
if (Token::simpleMatch(tok, "fcloseall ( )"))
|
||||
return File;
|
||||
|
||||
const Token* vartok = tok->tokAt(2);
|
||||
while (Token::Match(vartok, "%name% .|::"))
|
||||
vartok = vartok->tokAt(2);
|
||||
int argNr = 1;
|
||||
for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) {
|
||||
const Token* vartok = tok2;
|
||||
while (Token::Match(vartok, "%name% .|::"))
|
||||
vartok = vartok->tokAt(2);
|
||||
|
||||
if (Token::Match(vartok, "%varid% )|,|-", varid)) {
|
||||
if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )"))
|
||||
return Malloc;
|
||||
|
||||
if (settings1->standards.posix) {
|
||||
if (tok->str() == "close")
|
||||
return Fd;
|
||||
if (tok->str() == "pclose")
|
||||
return Pipe;
|
||||
}
|
||||
|
||||
// Does tok point on a Library deallocation function?
|
||||
const int dealloctype = settings1->library.dealloc(tok);
|
||||
if (dealloctype > 0) {
|
||||
if (dealloctype == settings1->library.dealloc("free"))
|
||||
if (Token::Match(vartok, "%varid% )|,|-", varid)) {
|
||||
if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )"))
|
||||
return Malloc;
|
||||
if (dealloctype == settings1->library.dealloc("fclose"))
|
||||
return File;
|
||||
return Library::ismemory(dealloctype) ? OtherMem : OtherRes;
|
||||
|
||||
if (settings1->standards.posix) {
|
||||
if (tok->str() == "close")
|
||||
return Fd;
|
||||
if (tok->str() == "pclose")
|
||||
return Pipe;
|
||||
}
|
||||
|
||||
// Does tok point on a Library deallocation function?
|
||||
const int dealloctype = settings1->library.dealloc(tok, argNr);
|
||||
if (dealloctype > 0) {
|
||||
if (dealloctype == settings1->library.deallocId("free"))
|
||||
return Malloc;
|
||||
if (dealloctype == settings1->library.deallocId("fclose"))
|
||||
return File;
|
||||
return Library::ismemory(dealloctype) ? OtherMem : OtherRes;
|
||||
}
|
||||
}
|
||||
argNr++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1137,7 +1137,7 @@ void CheckStl::checkAutoPointer()
|
|||
std::set<unsigned int> autoPtrVarId;
|
||||
std::map<unsigned int, const std::string> mallocVarId; // variables allocated by the malloc-like function
|
||||
const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string";
|
||||
const int malloc = _settings->library.alloc("malloc"); // allocation function, which are not compatible with auto_ptr
|
||||
const int malloc = _settings->library.allocId("malloc"); // allocation function, which are not compatible with auto_ptr
|
||||
const bool printStyle = _settings->isEnabled("style");
|
||||
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
|
@ -1153,7 +1153,7 @@ void CheckStl::checkAutoPointer()
|
|||
if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) {
|
||||
autoPointerArrayError(tok2->next());
|
||||
}
|
||||
if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next()) == malloc) {
|
||||
if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next(), -1) == malloc) {
|
||||
// malloc-like function allocated memory passed to the auto_ptr constructor -> error
|
||||
autoPointerMallocError(tok2->next(), tok3->next()->str());
|
||||
}
|
||||
|
@ -1197,7 +1197,7 @@ void CheckStl::checkAutoPointer()
|
|||
if (iter != autoPtrVarId.end()) {
|
||||
autoPointerArrayError(tok);
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2)) == malloc) {
|
||||
} else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2), -1) == malloc) {
|
||||
// C library function like 'malloc' used together with auto pointer -> error
|
||||
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->varId());
|
||||
if (iter != autoPtrVarId.end()) {
|
||||
|
@ -1206,7 +1206,7 @@ void CheckStl::checkAutoPointer()
|
|||
// it is not an auto pointer variable and it is allocated by malloc like function.
|
||||
mallocVarId.insert(std::make_pair(tok->varId(), tok->strAt(2)));
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4)) == malloc) {
|
||||
} else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4), -1) == malloc) {
|
||||
// C library function like 'malloc' used when resetting auto pointer -> error
|
||||
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->varId());
|
||||
if (iter != autoPtrVarId.end()) {
|
||||
|
|
|
@ -139,9 +139,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
|||
int allocationId = 0;
|
||||
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
|
||||
if (strcmp(memorynode->Name(),"dealloc")==0) {
|
||||
const std::map<std::string,int>::const_iterator it = _dealloc.find(memorynode->GetText());
|
||||
const std::map<std::string, AllocFunc>::const_iterator it = _dealloc.find(memorynode->GetText());
|
||||
if (it != _dealloc.end()) {
|
||||
allocationId = it->second;
|
||||
allocationId = it->second.groupId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -158,14 +158,29 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
|||
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
|
||||
const std::string memorynodename = memorynode->Name();
|
||||
if (memorynodename == "alloc") {
|
||||
_alloc[memorynode->GetText()] = allocationId;
|
||||
AllocFunc temp;
|
||||
temp.groupId = allocationId;
|
||||
|
||||
const char *init = memorynode->Attribute("init");
|
||||
if (init && strcmp(init,"false")==0) {
|
||||
returnuninitdata.insert(memorynode->GetText());
|
||||
}
|
||||
} else if (memorynodename == "dealloc")
|
||||
_dealloc[memorynode->GetText()] = allocationId;
|
||||
else if (memorynodename == "use")
|
||||
const char *arg = memorynode->Attribute("arg");
|
||||
if (arg)
|
||||
temp.arg = atoi(arg);
|
||||
else
|
||||
temp.arg = -1;
|
||||
_alloc[memorynode->GetText()] = temp;
|
||||
} else if (memorynodename == "dealloc") {
|
||||
AllocFunc temp;
|
||||
temp.groupId = allocationId;
|
||||
const char *arg = memorynode->Attribute("arg");
|
||||
if (arg)
|
||||
temp.arg = atoi(arg);
|
||||
else
|
||||
temp.arg = 1;
|
||||
_dealloc[memorynode->GetText()] = temp;
|
||||
} else if (memorynodename == "use")
|
||||
use.insert(memorynode->GetText());
|
||||
else
|
||||
unknown_elements.insert(memorynodename);
|
||||
|
@ -772,18 +787,32 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const
|
|||
}
|
||||
|
||||
|
||||
/** get allocation id for function */
|
||||
int Library::alloc(const Token *tok) const
|
||||
/** get allocation info for function */
|
||||
const Library::AllocFunc* Library::alloc(const Token *tok) const
|
||||
{
|
||||
const std::string funcname = functionName(tok);
|
||||
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_alloc, funcname);
|
||||
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_alloc, funcname);
|
||||
}
|
||||
|
||||
/** get deallocation info for function */
|
||||
const Library::AllocFunc* Library::dealloc(const Token *tok) const
|
||||
{
|
||||
const std::string funcname = functionName(tok);
|
||||
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_dealloc, funcname);
|
||||
}
|
||||
|
||||
/** get allocation id for function */
|
||||
int Library::alloc(const Token *tok, int arg) const
|
||||
{
|
||||
const Library::AllocFunc* af = alloc(tok);
|
||||
return (af && af->arg == arg) ? af->groupId : 0;
|
||||
}
|
||||
|
||||
/** get deallocation id for function */
|
||||
int Library::dealloc(const Token *tok) const
|
||||
int Library::dealloc(const Token *tok, int arg) const
|
||||
{
|
||||
const std::string funcname = functionName(tok);
|
||||
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_dealloc, funcname);
|
||||
const Library::AllocFunc* af = dealloc(tok);
|
||||
return (af && af->arg == arg) ? af->groupId : 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -64,29 +64,54 @@ public:
|
|||
/** this is primarily meant for unit tests. it only returns true/false */
|
||||
bool loadxmldata(const char xmldata[], std::size_t len);
|
||||
|
||||
/** get allocation id for function by name (deprecated, use other alloc) */
|
||||
int alloc(const char name[]) const {
|
||||
return getid(_alloc, name);
|
||||
}
|
||||
struct AllocFunc {
|
||||
int groupId;
|
||||
int arg;
|
||||
};
|
||||
|
||||
/** get allocation info for function */
|
||||
const AllocFunc* alloc(const Token *tok) const;
|
||||
|
||||
/** get deallocation info for function */
|
||||
const AllocFunc* dealloc(const Token *tok) const;
|
||||
|
||||
/** get allocation id for function */
|
||||
int alloc(const Token *tok) const;
|
||||
int alloc(const Token *tok, int arg) const;
|
||||
|
||||
/** get deallocation id for function */
|
||||
int dealloc(const Token *tok) const;
|
||||
int dealloc(const Token *tok, int arg) const;
|
||||
|
||||
/** get allocation info for function by name (deprecated, use other alloc) */
|
||||
const AllocFunc* alloc(const char name[]) const {
|
||||
return getAllocDealloc(_alloc, name);
|
||||
}
|
||||
|
||||
/** get deallocation info for function by name (deprecated, use other alloc) */
|
||||
const AllocFunc* dealloc(const char name[]) const {
|
||||
return getAllocDealloc(_dealloc, name);
|
||||
}
|
||||
|
||||
/** get allocation id for function by name (deprecated, use other alloc) */
|
||||
int allocId(const char name[]) const {
|
||||
const AllocFunc* af = getAllocDealloc(_alloc, name);
|
||||
return af ? af->groupId : 0;
|
||||
}
|
||||
|
||||
/** get deallocation id for function by name (deprecated, use other alloc) */
|
||||
int dealloc(const char name[]) const {
|
||||
return getid(_dealloc, name);
|
||||
int deallocId(const char name[]) const {
|
||||
const AllocFunc* af = getAllocDealloc(_dealloc, name);
|
||||
return af ? af->groupId : 0;
|
||||
}
|
||||
|
||||
/** set allocation id for function */
|
||||
void setalloc(const std::string &functionname, int id) {
|
||||
_alloc[functionname] = id;
|
||||
void setalloc(const std::string &functionname, int id, int arg) {
|
||||
_alloc[functionname].groupId = id;
|
||||
_alloc[functionname].arg = arg;
|
||||
}
|
||||
|
||||
void setdealloc(const std::string &functionname, int id) {
|
||||
_dealloc[functionname] = id;
|
||||
void setdealloc(const std::string &functionname, int id, int arg) {
|
||||
_dealloc[functionname].groupId = id;
|
||||
_dealloc[functionname].arg = arg;
|
||||
}
|
||||
|
||||
/** add noreturn function setting */
|
||||
|
@ -98,11 +123,17 @@ public:
|
|||
static bool ismemory(int id) {
|
||||
return ((id > 0) && ((id & 1) == 0));
|
||||
}
|
||||
static bool ismemory(const AllocFunc* func) {
|
||||
return ((func->groupId > 0) && ((func->groupId & 1) == 0));
|
||||
}
|
||||
|
||||
/** is allocation type resource? */
|
||||
static bool isresource(int id) {
|
||||
return ((id > 0) && ((id & 1) == 1));
|
||||
}
|
||||
static bool isresource(const AllocFunc* func) {
|
||||
return ((func->groupId > 0) && ((func->groupId & 1) == 1));
|
||||
}
|
||||
|
||||
bool formatstr_function(const std::string& funcname) const {
|
||||
return _formatstr.find(funcname) != _formatstr.cend();
|
||||
|
@ -443,8 +474,8 @@ private:
|
|||
int allocid;
|
||||
std::set<std::string> _files;
|
||||
std::set<std::string> _useretval;
|
||||
std::map<std::string, int> _alloc; // allocation functions
|
||||
std::map<std::string, int> _dealloc; // deallocation functions
|
||||
std::map<std::string, AllocFunc> _alloc; // allocation functions
|
||||
std::map<std::string, AllocFunc> _dealloc; // deallocation functions
|
||||
std::map<std::string, bool> _noreturn; // is function noreturn?
|
||||
std::set<std::string> _ignorefunction; // ignore functions/macros from a library (gtk, qt etc)
|
||||
std::map<std::string, bool> _reporterrors;
|
||||
|
@ -462,9 +493,9 @@ private:
|
|||
|
||||
const ArgumentChecks * getarg(const Token *ftok, int argnr) const;
|
||||
|
||||
static int getid(const std::map<std::string,int> &data, const std::string &name) {
|
||||
const std::map<std::string,int>::const_iterator it = data.find(name);
|
||||
return (it == data.end()) ? 0 : it->second;
|
||||
static const AllocFunc* getAllocDealloc(const std::map<std::string, AllocFunc> &data, const std::string &name) {
|
||||
const std::map<std::string, AllocFunc>::const_iterator it = data.find(name);
|
||||
return (it == data.end()) ? nullptr : &it->second;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ private:
|
|||
void run() {
|
||||
int id = 0;
|
||||
while (!settings.library.ismemory(++id));
|
||||
settings.library.setalloc("malloc", id);
|
||||
settings.library.setdealloc("free", id);
|
||||
settings.library.setalloc("malloc", id, -1);
|
||||
settings.library.setdealloc("free", id, 1);
|
||||
while (!settings.library.isresource(++id));
|
||||
settings.library.setalloc("fopen", id);
|
||||
settings.library.setdealloc("fclose", id);
|
||||
settings.library.setalloc("fopen", id, -1);
|
||||
settings.library.setdealloc("fclose", id, 1);
|
||||
|
||||
// Assign
|
||||
TEST_CASE(assign1);
|
||||
|
@ -1269,8 +1269,7 @@ private:
|
|||
" HeapFree(MyHeap, 0, b);"
|
||||
" HeapDestroy(MyHeap);"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n"
|
||||
"[test.c:1]: (error) Resource handle 'MyHeap' freed twice.\n", errout.str());
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void f() {"
|
||||
" int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));"
|
||||
|
@ -1287,8 +1286,7 @@ private:
|
|||
" HeapFree(MyHeap, 0, a);"
|
||||
" HeapDestroy(MyHeap);"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: b", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n"
|
||||
"[test.c:1]: (error) Memory leak: b\n", errout.str());
|
||||
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str());
|
||||
|
||||
check("void f() {"
|
||||
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
||||
|
@ -1297,7 +1295,8 @@ private:
|
|||
" HeapFree(MyHeap, 0, a);"
|
||||
" HeapFree(MyHeap, 0, b);"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap",
|
||||
"", errout.str());
|
||||
|
||||
check("void f() {"
|
||||
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
||||
|
@ -1305,8 +1304,9 @@ private:
|
|||
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
|
||||
" HeapFree(MyHeap, 0, a);"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap\n[test.c:1] (error) Memory leak: b",
|
||||
"[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n[test.c:1]: (error) Memory leak: b\n", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n"
|
||||
"[test.c:1] (error) Memory leak: b",
|
||||
"[test.c:1]: (error) Memory leak: b\n", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ private:
|
|||
TEST_CASE(function_warn);
|
||||
TEST_CASE(memory);
|
||||
TEST_CASE(memory2); // define extra "free" allocation functions
|
||||
TEST_CASE(memory3);
|
||||
TEST_CASE(resource);
|
||||
TEST_CASE(podtype);
|
||||
TEST_CASE(container);
|
||||
|
@ -358,7 +359,11 @@ private:
|
|||
ASSERT(library.argumentChecks.empty());
|
||||
|
||||
ASSERT(Library::ismemory(library.alloc("CreateX")));
|
||||
ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX"));
|
||||
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
|
||||
const Library::AllocFunc* af = library.alloc("CreateX");
|
||||
ASSERT(af && af->arg == -1);
|
||||
const Library::AllocFunc* df = library.dealloc("DeleteX");
|
||||
ASSERT(df && df->arg == 1);
|
||||
}
|
||||
void memory2() const {
|
||||
const char xmldata1[] = "<?xml version=\"1.0\"?>\n"
|
||||
|
@ -380,8 +385,30 @@ private:
|
|||
library.loadxmldata(xmldata1, sizeof(xmldata1));
|
||||
library.loadxmldata(xmldata2, sizeof(xmldata2));
|
||||
|
||||
ASSERT_EQUALS(library.dealloc("free"), library.alloc("malloc"));
|
||||
ASSERT_EQUALS(library.dealloc("free"), library.alloc("foo"));
|
||||
ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc"));
|
||||
ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo"));
|
||||
}
|
||||
void memory3() const {
|
||||
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
||||
"<def>\n"
|
||||
" <memory>\n"
|
||||
" <alloc arg=\"5\" init=\"false\">CreateX</alloc>\n"
|
||||
" <dealloc arg=\"2\">DeleteX</dealloc>\n"
|
||||
" </memory>\n"
|
||||
"</def>";
|
||||
|
||||
Library library;
|
||||
readLibrary(library, xmldata);
|
||||
ASSERT(library.use.empty());
|
||||
ASSERT(library.leakignore.empty());
|
||||
ASSERT(library.argumentChecks.empty());
|
||||
|
||||
const Library::AllocFunc* af = library.alloc("CreateX");
|
||||
ASSERT(af && af->arg == 5);
|
||||
const Library::AllocFunc* df = library.dealloc("DeleteX");
|
||||
ASSERT(df && df->arg == 2);
|
||||
|
||||
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
|
||||
}
|
||||
|
||||
void resource() const {
|
||||
|
@ -399,8 +426,8 @@ private:
|
|||
ASSERT(library.leakignore.empty());
|
||||
ASSERT(library.argumentChecks.empty());
|
||||
|
||||
ASSERT(Library::isresource(library.alloc("CreateX")));
|
||||
ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX"));
|
||||
ASSERT(Library::isresource(library.allocId("CreateX")));
|
||||
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
|
||||
}
|
||||
|
||||
void podtype() const {
|
||||
|
|
Loading…
Reference in New Issue