Fix #7031 (improve error message for memory related warnings) (#2204)

Printout both the locations on double free errors, mismatching
alloc/dealloc and dealloc return error.
This commit is contained in:
Rikard Falkeborn 2019-09-22 21:50:02 +02:00 committed by Daniel Marjamäki
parent 28d13e7567
commit bb5ac32872
3 changed files with 73 additions and 57 deletions

View File

@ -133,10 +133,10 @@ void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, i
checkmemleak.memleakError(tok, varname);
}
void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname)
void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname)
{
const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings);
const std::list<const Token *> callstack(1, tok);
const std::list<const Token *> callstack = { allocTok, deallocTok };
c.mismatchAllocDealloc(callstack, varname);
}
@ -146,9 +146,10 @@ void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varn
c.deallocuseError(tok, varname);
}
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname)
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname)
{
reportError(tok, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, false);
const std::list<const Token *> locations = { deallocTok, tok };
reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, false);
}
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
@ -161,12 +162,14 @@ void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &fu
}
}
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type)
{
const std::list<const Token *> locations = { prevFreeTok, tok };
if (Library::isresource(type))
reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, false);
reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, false);
else
reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, false);
reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, false);
}
@ -381,6 +384,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
varAlloc.type = f->groupId;
varAlloc.status = VarInfo::ALLOC;
varAlloc.allocTok = fTok;
}
changeAllocStatusIfRealloc(alloctype, fTok, varTok);
@ -390,6 +394,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
varAlloc.status = VarInfo::ALLOC;
varAlloc.allocTok = varTok->tokAt(2);
}
// Assigning non-zero value variable. It might be used to
@ -425,6 +430,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
varAlloc.type = f->groupId;
varAlloc.status = VarInfo::ALLOC;
varAlloc.allocTok = tokRightAstOperand->previous();
} else {
// Fixme: warn about leak
alloctype.erase(innerTok->varId());
@ -437,6 +443,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
varAlloc.status = VarInfo::ALLOC;
varAlloc.allocTok = innerTok->tokAt(2);
}
}
@ -598,6 +605,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
// delete
else if (mTokenizer->isCPP() && tok->str() == "delete") {
const Token * delTok = tok;
const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]");
if (arrayDelete)
tok = tok->tokAt(3);
@ -609,7 +617,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
tok = tok->tokAt(2);
const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0;
if (!isnull && tok->varId() && tok->strAt(1) != "[") {
const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC);
const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC, delTok);
changeAllocStatus(varInfo, allocation, tok, tok);
}
}
@ -618,7 +626,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
else if (isFunctionCall(ftok)) {
const Token * openingPar = isFunctionCall(ftok);
const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok);
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok);
if (allocation.type == 0)
allocation.status = VarInfo::NOALLOC;
functionCall(ftok, openingPar, varInfo, allocation, af);
@ -716,7 +724,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
}
const Token * vtok = typeEndTok->tokAt(3);
const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED);
const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, ftok);
changeAllocStatus(varInfo, allocation, vtok, vtok);
}
}
@ -747,7 +755,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
const Token * const openingPar = isFunctionCall(tok);
if (openingPar) {
const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok);
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC);
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok);
if (alloc.type == 0)
alloc.status = VarInfo::NOALLOC;
functionCall(tok, openingPar, varInfo, alloc, nullptr);
@ -766,10 +774,12 @@ void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map<int, VarInfo::AllocIn
VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()];
VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()];
if (argAlloc.type != 0 && argAlloc.type != f->groupId)
mismatchError(fTok, argTok->str());
mismatchError(fTok, argAlloc.allocTok, argTok->str());
argAlloc.status = VarInfo::DEALLOC;
argAlloc.allocTok = fTok;
retAlloc.type = f->groupId;
retAlloc.status = VarInfo::ALLOC;
retAlloc.allocTok = fTok;
}
}
@ -785,18 +795,20 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocI
if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
varInfo->erase(arg->varId());
} else if (var->second.managed()) {
doubleFreeError(tok, arg->str(), allocation.type);
doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type);
} else if (var->second.type != allocation.type) {
// mismatching allocation and deallocation
mismatchError(tok, arg->str());
mismatchError(tok, var->second.allocTok, arg->str());
varInfo->erase(arg->varId());
} else {
// deallocation
var->second.status = allocation.status;
var->second.type = allocation.type;
var->second.allocTok = allocation.allocTok;
}
} else if (allocation.status != VarInfo::NOALLOC) {
alloctype[arg->varId()].status = VarInfo::DEALLOC;
alloctype[arg->varId()].allocTok = tok;
}
}
@ -844,6 +856,7 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
// Check smart pointer
else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) {
const Token * typeEndTok = arg->linkAt(1);
const Token * allocTok = nullptr;
if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}"))
continue;
@ -874,15 +887,17 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
const Scope * tscope = dtok->type()->classScope;
for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) {
sp_af = mSettings->library.getDeallocFuncInfo(tok2);
if (sp_af)
if (sp_af) {
allocTok = tok2;
break;
}
}
}
}
}
const Token * vtok = typeEndTok->tokAt(2);
const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED);
const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, allocTok);
changeAllocStatus(varInfo, sp_allocation, vtok, vtok);
} else {
checkTokenInsideExpression(arg, varInfo);
@ -955,7 +970,7 @@ void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
// return deallocated pointer
if (used && it->second.status == VarInfo::DEALLOC)
deallocReturnError(tok, var->name());
deallocReturnError(tok, it->second.allocTok, var->name());
else if (!used && !it->second.managed()) {
const std::map<int, std::string>::const_iterator use = possibleUsage.find(varid);

View File

@ -46,7 +46,8 @@ public:
* checkleakautovar allocation type.
*/
int type;
AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC) : status(status_), type(type_) {}
const Token * allocTok;
AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {}
bool managed() const {
return status < 0;
@ -142,19 +143,19 @@ private:
void leakIfAllocated(const Token *vartok, const VarInfo &varInfo);
void leakError(const Token* tok, const std::string &varname, int type);
void mismatchError(const Token* tok, const std::string &varname);
void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname);
void deallocUseError(const Token *tok, const std::string &varname);
void deallocReturnError(const Token *tok, const std::string &varname);
void doubleFreeError(const Token *tok, const std::string &varname, int type);
void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname);
void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type);
/** message: user configuration is needed to complete analysis */
void configurationInfo(const Token* tok, const std::string &functionName);
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE {
CheckLeakAutoVar c(nullptr, settings, errorLogger);
c.deallocReturnError(nullptr, "p");
c.deallocReturnError(nullptr, nullptr, "p");
c.configurationInfo(nullptr, "f"); // user configuration is needed to complete analysis
c.doubleFreeError(nullptr, "varname", 0);
c.doubleFreeError(nullptr, nullptr, "varname", 0);
}
static std::string myName() {

View File

@ -513,7 +513,7 @@ private:
" free(p);\n"
" return p;\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout.str());
check("void f(char *p) {\n"
" if (!p) free(p);\n"
@ -621,14 +621,14 @@ private:
" p = 0;\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:3] -> [test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p) {\n"
" free(p);\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p, char *r) {\n"
@ -665,7 +665,7 @@ private:
" bar();\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p) {\n"
@ -673,14 +673,14 @@ private:
" printf(\"Freed memory at location %x\", p);\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(FILE *p) {\n"
" fclose(p);\n"
" fclose(p);\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str());
check(
"void foo(FILE *p, FILE *r) {\n"
@ -710,7 +710,7 @@ private:
" gethandle();\n"
" fclose(p);\n"
"}");
ASSERT_EQUALS("[test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str());
check(
"void foo(Data* p) {\n"
@ -739,7 +739,7 @@ private:
" }\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:4] -> [test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void f() {\n"
@ -755,7 +755,7 @@ private:
" delete p;\n"
" delete p;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p, char *r) {\n"
@ -792,14 +792,14 @@ private:
" bar();\n"
" delete p;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p) {\n"
" delete[] p;\n"
" delete[] p;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void foo(char *p, char *r) {\n"
@ -822,7 +822,7 @@ private:
" bar();\n"
" delete[] p;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"LineMarker::~LineMarker() {\n"
@ -866,7 +866,7 @@ private:
" delete a;\n"
" return 0;\n"
"}", true);
TODO_ASSERT_EQUALS("", "[test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str());
TODO_ASSERT_EQUALS("", "[test.cpp:8] -> [test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str());
check(
"void foo(int y)\n"
@ -956,7 +956,7 @@ private:
" }\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:6] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
check(
"void MyFunction()\n"
@ -1068,7 +1068,7 @@ private:
" x = (q == p);\n"
" free(p);\n"
"}");
ASSERT_EQUALS("[test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
}
void doublefree6() { // #7685
@ -1101,35 +1101,35 @@ private:
" std::unique_ptr<int> x(i);\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
check("void f() {\n"
" int * i = new int;\n"
" delete i;\n"
" std::unique_ptr<int> x(i);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
check("void f() {\n"
" int * i = new int;\n"
" std::unique_ptr<int> x{i};\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
check("void f() {\n"
" int * i = new int;\n"
" std::shared_ptr<int> x(i);\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
check("void f() {\n"
" int * i = new int;\n"
" std::shared_ptr<int> x{i};\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
// Check for use-after-free FP
check("void f() {\n"
@ -1144,7 +1144,7 @@ private:
" std::unique_ptr<int[]> x(i);\n"
" delete i;\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
}
void doublefree9() {
@ -1477,13 +1477,13 @@ private:
" FILE*f=fopen(fname,a);\n"
" free(f);\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
check("void f() {\n"
" FILE*f=fopen(fname,a);\n"
" free((void*)f);\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
check("void f() {\n"
" char *cPtr = new char[100];\n"
@ -1493,13 +1493,13 @@ private:
" cPtr = new char[100];\n"
" delete cPtr;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
check("void f() {\n"
" char *cPtr = new char[100];\n"
" free(cPtr);\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
check("void f() {\n"
" char *cPtr = new (buf) char[100];\n"
@ -1510,21 +1510,21 @@ private:
" int * i = new int[1];\n"
" std::unique_ptr<int> x(i);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
check("void f() {\n"
" int * i = new int;\n"
" std::unique_ptr<int[]> x(i);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
check("void f() {\n"
" void* a = malloc(1);\n"
" void* b = freopen(f, p, a);\n"
" free(b);\n"
"}");
ASSERT_EQUALS("[test.c:3]: (error) Mismatching allocation and deallocation: a\n"
"[test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str());
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: a\n"
"[test.c:3] -> [test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str());
check("void f() {\n"
" void* a;\n"
@ -1538,8 +1538,8 @@ private:
" int * j = realloc(i, 2 * sizeof(int));\n"
" delete[] j;\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n"
"[test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n"
"[test.cpp:3] -> [test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str());
}
void smartPointerDeleter() {
@ -1547,7 +1547,7 @@ private:
" FILE*f=fopen(fname,a);\n"
" std::unique_ptr<FILE> fp{f};\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
check("void f() {\n"
" FILE*f=fopen(fname,a);\n"
@ -1600,13 +1600,13 @@ private:
" FILE*f=fopen(fname,a);\n"
" std::shared_ptr<FILE> fp{f, [](FILE* x) { free(f); }};\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
check("void f() {\n"
" FILE*f=fopen(fname,a);\n"
" std::shared_ptr<FILE> fp{f, [](FILE* x) {}};\n"
"}", true);
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
check("class C;\n"
"void f() {\n"