Skip to content

Commit 9849ab1

Browse files
author
Aaron Danen
committed
impl and tests
1 parent 8a92e99 commit 9849ab1

2 files changed

Lines changed: 109 additions & 5 deletions

File tree

lib/checkleakautovar.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,40 @@ static const Token * isFunctionCall(const Token * nameToken)
267267
return nullptr;
268268
}
269269

270+
/** checks if tok is part of the LHS of an anonymous function call:
271+
* tok should be the first token in a unary expression that evaluates to a
272+
* function pointer, or the name of a lambda or function
273+
* (*func_ptr)(arg)
274+
* or
275+
* (lambda)(arg)
276+
* or
277+
* get_function()(arg)
278+
*
279+
* @param token on the LHS of a function call
280+
* @return opening parenthesis token or nullptr if not a function call
281+
*/
282+
static const Token * isAnonymousFunctionCall(const Token * tok)
283+
{
284+
// match one of the supported LHS patterns
285+
if (tok->previous()->str() == "(" && !tok->previous()->isBinaryOp() && tok->linkAt(-1)) {
286+
tok = tok->linkAt(-1)->next();
287+
} else if (!tok->isStandardType() && tok->isName() && tok->linkAt(1)) {
288+
tok = tok->linkAt(1)->next();
289+
} else {
290+
return nullptr;
291+
}
292+
293+
// skip over potential template arguments
294+
if (tok->link() && tok->str() == "<")
295+
tok = tok->link()->next();
296+
297+
// return the opening parenthesis
298+
if (tok && tok->link() && !tok->isCast() && tok->str() == "(")
299+
return tok;
300+
301+
return nullptr;
302+
}
303+
270304
static const Token* getOutparamAllocation(const Token* tok, const Library& library)
271305
{
272306
if (!tok)
@@ -787,11 +821,22 @@ bool CheckLeakAutoVarImpl::checkScope(const Token * const startToken,
787821
}
788822
}
789823

824+
// a regular function call can return an anonymous function
825+
openingPar = isAnonymousFunctionCall(ftok);
826+
if (openingPar) {
827+
functionCall(nullptr, openingPar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
828+
tok = openingPar->link();
829+
}
830+
790831
continue;
791-
}
832+
833+
// top level call to an anonymous function
834+
} else if (const Token *openingPar = isAnonymousFunctionCall(tok)) {
835+
functionCall(nullptr, openingPar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
836+
tok = openingPar->link();
792837

793838
// goto => weird execution path
794-
else if (tok->str() == "goto") {
839+
} else if (tok->str() == "goto") {
795840
varInfo.clear();
796841
return false;
797842
}
@@ -941,17 +986,27 @@ const Token * CheckLeakAutoVarImpl::checkTokenInsideExpression(const Token * con
941986

942987
// check for function call
943988
const Token * const openingPar = inFuncCall ? nullptr : isFunctionCall(tok);
989+
const Token * const anonOpeningPar = isAnonymousFunctionCall(tok);
944990
if (openingPar) {
945991
const Library::AllocFunc* allocFunc = mSettings.library.getDeallocFuncInfo(tok);
946992
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok);
947993
if (alloc.type == 0)
948994
alloc.status = VarInfo::NOALLOC;
949995
functionCall(tok, openingPar, varInfo, alloc, nullptr);
950996
const std::string &returnValue = mSettings.library.returnValue(tok);
997+
951998
if (startsWith(returnValue, "arg"))
952999
// the function returns one of its argument, we need to process a potential assignment
9531000
return openingPar;
954-
return isCPPCast(tok->astParent()) ? openingPar : openingPar->link();
1001+
1002+
if (!anonOpeningPar)
1003+
return isCPPCast(tok->astParent()) ? openingPar : openingPar->link();
1004+
}
1005+
1006+
// check for anonymous function call
1007+
if (anonOpeningPar) {
1008+
functionCall(nullptr, anonOpeningPar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
1009+
return anonOpeningPar->link();
9551010
}
9561011

9571012
return nullptr;
@@ -1022,7 +1077,7 @@ void CheckLeakAutoVarImpl::functionCall(const Token *tokName, const Token *tokOp
10221077
const bool isLeakIgnore = mSettings.library.isLeakIgnore(mSettings.library.getFunctionName(tokName));
10231078
if (mSettings.library.getReallocFuncInfo(tokName))
10241079
return;
1025-
if (tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike)
1080+
if (tokName && tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike)
10261081
return;
10271082

10281083
const Token * const tokFirstArg = tokOpeningPar->next();
@@ -1269,7 +1324,7 @@ void CheckLeakAutoVarImpl::ret(const Token *tok, VarInfo &varInfo, const bool is
12691324
const auto use = possibleUsage.find(varid);
12701325
if (use == possibleUsage.end()) {
12711326
leakError(tok, var->name(), it->second.type);
1272-
} else if (!use->second.first->variable()) { // TODO: handle constructors
1327+
} else if (use->second.first && !use->second.first->variable()) { // TODO: handle constructors
12731328
configurationInfo(tok, use->second);
12741329
}
12751330
}

test/testleakautovar.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class TestLeakAutoVar : public TestFixture {
118118

119119
// handling function calls
120120
TEST_CASE(functioncall1);
121+
TEST_CASE(anonymousFunctionCall1);
121122

122123
// goto
123124
TEST_CASE(goto1);
@@ -1910,6 +1911,54 @@ class TestLeakAutoVar : public TestFixture {
19101911
ASSERT_EQUALS("[test.cpp:4:1]: (error) Memory leak: b [memleak]\n", errout_str());
19111912
}
19121913

1914+
void anonymousFunctionCall1() { // #14990
1915+
// function pointer
1916+
check("void f(void (*fptr)(void *)) {\n"
1917+
"void *buf = malloc(1);\n"
1918+
"(*fptr)(buf);\n"
1919+
"}\n");
1920+
ASSERT_EQUALS("", errout_str());
1921+
1922+
// lambda
1923+
check("void f() {\n"
1924+
"auto x = [](void *ptr) { g(ptr) };\n"
1925+
"void *p = malloc(1);\n"
1926+
"(x)(p);\n"
1927+
"}\n");
1928+
ASSERT_EQUALS("", errout_str());
1929+
1930+
// Function returning a function pointer
1931+
check("void f() {\n"
1932+
" void *buf = malloc(1);\n"
1933+
" get_function()(buf);\n"
1934+
"}\n");
1935+
ASSERT_EQUALS("", errout_str());
1936+
1937+
// Function returning a function pointer, passed as an arg to a normal
1938+
// function
1939+
check("void f() {\n"
1940+
" void *buf = malloc(1);\n"
1941+
" foo(get_function()(buf));\n"
1942+
"}\n");
1943+
ASSERT_EQUALS("[test.c:3:29]: (information) --check-library: Function foo() should have <noreturn> configuration [checkLibraryNoReturn]\n"
1944+
"[test.c:4:1]: (information) --check-library: Function foo() should have <use>/<leak-ignore> configuration [checkLibraryUseIgnore]\n",
1945+
errout_str());
1946+
1947+
// Function returning a function pointer, passed as an arg to another
1948+
// function returning a function pointer
1949+
check("void f() {\n"
1950+
" void *buf = malloc(1);\n"
1951+
" get_function()(get_function()(buf));\n"
1952+
"}\n");
1953+
ASSERT_EQUALS("", errout_str());
1954+
1955+
check("void f() {\n"
1956+
" void *buf = malloc(1);\n"
1957+
" get_function(buf)(get_function(NULL)(NULL));\n"
1958+
"}\n");
1959+
ASSERT_EQUALS("[test.c:4:1]: (information) --check-library: Function get_function() should have <use>/<leak-ignore> configuration [checkLibraryUseIgnore]\n", errout_str());
1960+
}
1961+
19131962
void goto1() {
19141963
check("static void f() {\n"
19151964
" int err = -ENOMEM;\n"

0 commit comments

Comments
 (0)