diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 005c86961..6fcdfc4a8 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -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 callstack(1, tok); + const std::list 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 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 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::mapvarId()]; 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::const_iterator use = possibleUsage.find(varid); diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h index 2e7225339..4808e4784 100644 --- a/lib/checkleakautovar.h +++ b/lib/checkleakautovar.h @@ -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() { diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 97a0c3483..2dc5f1d0c 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -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 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 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 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 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 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 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 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 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 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 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 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"