New check: Use make_shared/make_unique (#5673)
This commit is contained in:
parent
016e89e422
commit
22bd20c94a
|
@ -2692,6 +2692,10 @@ void CheckMemoryLeakNoVar::check()
|
||||||
// Checks if a call to an allocation function like malloc() is made and its return value is not assigned.
|
// Checks if a call to an allocation function like malloc() is made and its return value is not assigned.
|
||||||
checkForUnusedReturnValue(scope);
|
checkForUnusedReturnValue(scope);
|
||||||
|
|
||||||
|
// Checks to see if a function is called with memory allocated for an argument that
|
||||||
|
// could be leaked if a function called for another argument throws.
|
||||||
|
checkForUnsafeArgAlloc(scope);
|
||||||
|
|
||||||
// goto the "}" that ends the executable scope..
|
// goto the "}" that ends the executable scope..
|
||||||
const Token *tok = scope->classEnd;
|
const Token *tok = scope->classEnd;
|
||||||
|
|
||||||
|
@ -2748,6 +2752,52 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// Check if an exception could cause a leak in an argument constructed with
|
||||||
|
// shared_ptr/unique_ptr. For example, in the following code, it is possible
|
||||||
|
// that if g() throws an exception, the memory allocated by "new int(42)"
|
||||||
|
// could be leaked. See stackoverflow.com/questions/19034538/
|
||||||
|
// why-is-there-memory-leak-while-using-shared-ptr-as-a-function-parameter
|
||||||
|
//
|
||||||
|
// void x() {
|
||||||
|
// f(shared_ptr<int>(new int(42)), g());
|
||||||
|
// }
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope)
|
||||||
|
{
|
||||||
|
// This test only applies to C++ source
|
||||||
|
if (!_tokenizer->isCPP() || !_settings->inconclusive || !_settings->isEnabled("warning"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
if (Token::Match(tok, "%var% (")) {
|
||||||
|
const Token *endParamToken = tok->next()->link();
|
||||||
|
std::string pointerType;
|
||||||
|
std::string objectType;
|
||||||
|
std::string functionCalled;
|
||||||
|
|
||||||
|
// Scan through the arguments to the function call
|
||||||
|
for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != endParamToken; tok2 = tok2->nextArgument()) {
|
||||||
|
const Function *pFunc = tok2->function();
|
||||||
|
const bool isNothrow = pFunc && (pFunc->isDeclspecNothrow() || pFunc->isAttributeNothrow());
|
||||||
|
|
||||||
|
if (Token::Match(tok2, "shared_ptr|unique_ptr < %var% > ( new %var%")) {
|
||||||
|
pointerType = tok2->str();
|
||||||
|
objectType = tok2->strAt(6);
|
||||||
|
} else if (!isNothrow) {
|
||||||
|
if (Token::Match(tok2, "%var% ("))
|
||||||
|
functionCalled = tok2->str();
|
||||||
|
else if (Token::Match(tok2, "%var% < %var% > ("))
|
||||||
|
functionCalled = tok2->str() + "<" + tok2->strAt(2) + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pointerType.empty() && !functionCalled.empty())
|
||||||
|
unsafeArgAllocError(tok, functionCalled, pointerType, objectType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall)
|
void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall)
|
||||||
{
|
{
|
||||||
reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.");
|
reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.");
|
||||||
|
@ -2757,3 +2807,11 @@ void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::error, "leakReturnValNotUsed", "Return value of allocation function " + alloc + " is not used.");
|
reportError(tok, Severity::error, "leakReturnValNotUsed", "Return value of allocation function " + alloc + " is not used.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType)
|
||||||
|
{
|
||||||
|
const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique";
|
||||||
|
reportError(tok, Severity::warning, "leakUnsafeArgAlloc",
|
||||||
|
"Unsafe allocation. If " + funcName + "() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.",
|
||||||
|
true); // Inconclusive because funcName may never throw
|
||||||
|
}
|
||||||
|
|
|
@ -446,14 +446,22 @@ private:
|
||||||
*/
|
*/
|
||||||
void checkForUnusedReturnValue(const Scope *scope);
|
void checkForUnusedReturnValue(const Scope *scope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr.
|
||||||
|
* @param scope The scope of the function to check.
|
||||||
|
*/
|
||||||
|
void checkForUnsafeArgAlloc(const Scope *scope);
|
||||||
|
|
||||||
void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
|
void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
|
||||||
void returnValueNotUsedError(const Token* tok, const std::string &alloc);
|
void returnValueNotUsedError(const Token* tok, const std::string &alloc);
|
||||||
|
void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
|
||||||
CheckMemoryLeakNoVar c(0, settings, e);
|
CheckMemoryLeakNoVar c(0, settings, e);
|
||||||
|
|
||||||
c.functionCallLeak(0, "funcName", "funcName");
|
c.functionCallLeak(0, "funcName", "funcName");
|
||||||
c.returnValueNotUsedError(0, "funcName");
|
c.returnValueNotUsedError(0, "funcName");
|
||||||
|
c.unsafeArgAllocError(0, "funcName", "shared_ptr", "int");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
|
|
@ -6277,6 +6277,8 @@ private:
|
||||||
|
|
||||||
void run() {
|
void run() {
|
||||||
settings.standards.posix = true;
|
settings.standards.posix = true;
|
||||||
|
settings.inconclusive = true;
|
||||||
|
settings.addEnabled("warning");
|
||||||
|
|
||||||
LOAD_LIB_2(settings.library, "gtk.cfg");
|
LOAD_LIB_2(settings.library, "gtk.cfg");
|
||||||
|
|
||||||
|
@ -6293,8 +6295,12 @@ private:
|
||||||
|
|
||||||
// pass allocated memory to function..
|
// pass allocated memory to function..
|
||||||
TEST_CASE(functionParameter);
|
TEST_CASE(functionParameter);
|
||||||
|
|
||||||
// never use leakable resource
|
// never use leakable resource
|
||||||
TEST_CASE(missingAssignment);
|
TEST_CASE(missingAssignment);
|
||||||
|
|
||||||
|
// pass allocated memory to function using a smart pointer
|
||||||
|
TEST_CASE(smartPointerFunctionParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void functionParameter() {
|
void functionParameter() {
|
||||||
|
@ -6441,6 +6447,55 @@ private:
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function f is not used.\n", "", errout.str());
|
TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function f is not used.\n", "", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void smartPointerFunctionParam() {
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(shared_ptr<int>(new int(42)), g());\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared<int>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" h(12, f(shared_ptr<int>(new int(42)), g()));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared<int>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(unique_ptr<int>(new int(42)), g());\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique<int>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(g(), shared_ptr<int>(new int(42)));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared<int>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(g(), unique_ptr<int>(new int(42)));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique<int>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(shared_ptr<char>(new char), make_unique<int>(32));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If make_unique<int>() throws, memory could be leaked. Use make_shared<char>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void x() {\n"
|
||||||
|
" f(g(124), h(\"test\", 234), shared_ptr<char>(new char));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If h() throws, memory could be leaked. Use make_shared<char>() instead.\n", errout.str());
|
||||||
|
|
||||||
|
check("void g(int x) throw() { }\n"
|
||||||
|
"void x() {\n"
|
||||||
|
" f(g(124), shared_ptr<char>(new char));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void __declspec(nothrow) g(int x) { }\n"
|
||||||
|
"void x() {\n"
|
||||||
|
" f(g(124), shared_ptr<char>(new char));\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
REGISTER_TEST(TestMemleakNoVar)
|
REGISTER_TEST(TestMemleakNoVar)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue