From ba84303501b4a2f2f02ec31a57e58141bd68b319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 27 Sep 2020 19:15:15 +0200 Subject: [PATCH] Fixed #9680 (false positive: style: Variable 'x' is assigned a value that is never used with smart pointers) --- lib/checkunusedvar.cpp | 83 ++++++++++++++++++++++++++---------------- test/testunusedvar.cpp | 31 ++++++++++++++++ 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index ca581a1f0..33ed4f12b 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -45,6 +45,53 @@ namespace { static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const struct CWE CWE665(665U); // Improper Initialization +/** Is scope a raii class scope */ +static bool isRaiiClassScope(const Scope *classScope) +{ + return classScope && classScope->getDestructor() != nullptr; +} + +/** Is ValueType a raii class? */ +static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn = true) +{ + if (!cpp) + return false; + + if (!valueType) + return defaultReturn; + + if (valueType->smartPointerType && isRaiiClassScope(valueType->smartPointerType->classScope)) + return true; + + switch (valueType->type) { + case ValueType::Type::UNKNOWN_TYPE: + case ValueType::Type::NONSTD: + return defaultReturn; + + case ValueType::Type::RECORD: + if (isRaiiClassScope(valueType->typeScope)) + return true; + return defaultReturn; + + case ValueType::Type::CONTAINER: + case ValueType::Type::ITERATOR: + case ValueType::Type::VOID: + case ValueType::Type::BOOL: + case ValueType::Type::CHAR: + case ValueType::Type::SHORT: + case ValueType::Type::WCHAR_T: + case ValueType::Type::INT: + case ValueType::Type::LONG: + case ValueType::Type::LONGLONG: + case ValueType::Type::UNKNOWN_INT: + case ValueType::Type::FLOAT: + case ValueType::Type::DOUBLE: + case ValueType::Type::LONGDOUBLE: + return false; + } + + return defaultReturn; +} /** * @brief This class is used create a list of variables within a function. @@ -1136,38 +1183,12 @@ void CheckUnusedVar::checkFunctionVariableUsage() if (isIncrementOrDecrement && tok->astParent() && precedes(tok, tok->astOperand1())) continue; + if (tok->str() == "=" && isRaiiClass(tok->valueType(), mTokenizer->isCPP(), false)) + continue; + if (tok->isName()) { - if (mTokenizer->isCPP()) { - // do not check RAII/scope_lock objects - if (!tok->valueType()) - continue; - bool check = false; - switch (tok->valueType()->type) { - case ValueType::Type::UNKNOWN_TYPE: - case ValueType::Type::NONSTD: - case ValueType::Type::RECORD: - check = tok->valueType()->typeScope && !tok->valueType()->typeScope->getDestructor(); - break; - case ValueType::Type::CONTAINER: - case ValueType::Type::ITERATOR: - case ValueType::Type::VOID: - case ValueType::Type::BOOL: - case ValueType::Type::CHAR: - case ValueType::Type::SHORT: - case ValueType::Type::WCHAR_T: - case ValueType::Type::INT: - case ValueType::Type::LONG: - case ValueType::Type::LONGLONG: - case ValueType::Type::UNKNOWN_INT: - case ValueType::Type::FLOAT: - case ValueType::Type::DOUBLE: - case ValueType::Type::LONGDOUBLE: - check = true; - break; - } - if (!check) - continue; - } + if (isRaiiClass(tok->valueType(), mTokenizer->isCPP(), true)) + continue; tok = tok->next(); } if (tok->astParent() && tok->str() != "(") { diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 3fdfb59cd..37e7c7bdf 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -159,6 +159,7 @@ private: TEST_CASE(localVarStd); TEST_CASE(localVarClass); + TEST_CASE(localVarSmartPtr); // Don't give false positives for variables in structs/unions TEST_CASE(localvarStruct1); @@ -4433,6 +4434,36 @@ private: ASSERT_EQUALS("", errout.str()); } + void localVarSmartPtr() { + // handling of smart pointers (#9680) + functionVariableUsage("static int s_i = 0;\n" + "\n" + "class A {\n" + "public:\n" + " ~A() {\n" + " ++s_i;\n" + " }\n" + "};\n" + "\n" + "static void func() {\n" + " auto a = std::make_shared();\n" + " auto a2 = std::unique_ptr(new A());\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("class A {\n" + "public:\n" + " std::string x;\n" + "};\n" + "\n" + "static void func() {\n" + " auto a = std::make_shared();\n" + " auto a2 = std::unique_ptr(new A());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (style) Variable 'a' is assigned a value that is never used.\n" + "[test.cpp:8]: (style) Variable 'a2' is assigned a value that is never used.\n", errout.str()); + } + // ticket #3104 - false positive when variable is read with "if (NOT var)" void localvarIfNOT() { functionVariableUsage("void f() {\n"