Fixed #9017 (Simple classes without side effects not reported as unused)

This commit is contained in:
Daniel Marjamäki 2020-07-23 11:10:08 +02:00
parent 25ad22c6af
commit 682a6d1c02
2 changed files with 136 additions and 13 deletions

View File

@ -1442,24 +1442,50 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert( const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert(
std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions
bool & withoutSideEffects=found.first->second; bool & withoutSideEffects = found.first->second;
if (!found.second) if (!found.second)
return withoutSideEffects; return withoutSideEffects;
if (type && type->classScope && type->classScope->numConstructors == 0 && // unknown types are assumed to have side effects
(type->classScope->varlist.empty() || type->needInitialization == Type::NeedInitialization::True)) { if (!type || !type->classScope)
for (std::vector<Type::BaseInfo>::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) { return (withoutSideEffects = false);
if (!isRecordTypeWithoutSideEffects(i->type)) {
withoutSideEffects=false; // Non-empty constructors => possible side effects
return withoutSideEffects; for (const Function& f : type->classScope->functionList) {
} if (!f.isConstructor())
} continue;
withoutSideEffects=true; if (f.argDef && Token::Match(f.argDef->link(), ") ="))
return withoutSideEffects; continue; // ignore default/deleted constructors
const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }"));
const bool hasInitList = f.argDef && Token::simpleMatch(f.argDef->link(), ") :");
if (!emptyBody || hasInitList)
return (withoutSideEffects = false);
} }
withoutSideEffects=false; // unknown types are assumed to have side effects // Derived from type that has side effects?
return withoutSideEffects; for (const Type::BaseInfo& derivedFrom : type->derivedFrom) {
if (!isRecordTypeWithoutSideEffects(derivedFrom.type))
return (withoutSideEffects = false);
}
// Is there a member variable with possible side effects
for (const Variable& var : type->classScope->varlist) {
if (var.isPointer())
continue;
const Type* variableType = var.type();
if (variableType) {
if (!isRecordTypeWithoutSideEffects(variableType))
return (withoutSideEffects = false);
} else {
ValueType::Type valueType = var.valueType()->type;
if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD))
return (withoutSideEffects = false);
}
}
return (withoutSideEffects = true);
} }
bool CheckUnusedVar::isEmptyType(const Type* type) bool CheckUnusedVar::isEmptyType(const Type* type)

View File

@ -38,6 +38,8 @@ private:
settings.checkLibrary = true; settings.checkLibrary = true;
LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "std.cfg");
TEST_CASE(isRecordTypeWithoutSideEffects);
TEST_CASE(emptyclass); // #5355 - False positive: Variable is not assigned a value. TEST_CASE(emptyclass); // #5355 - False positive: Variable is not assigned a value.
TEST_CASE(emptystruct); // #5355 - False positive: Variable is not assigned a value. TEST_CASE(emptystruct); // #5355 - False positive: Variable is not assigned a value.
@ -232,6 +234,101 @@ private:
checkUnusedVar.checkStructMemberUsage(); checkUnusedVar.checkStructMemberUsage();
} }
void isRecordTypeWithoutSideEffects() {
functionVariableUsage(
"class A {};\n"
"void f() {\n"
" A a;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str());
functionVariableUsage(
"class A {};\n"
"class B {\n"
"public:\n"
" A a;\n"
"};\n"
"void f() {\n"
" B b;\n"
"}");
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: b\n", errout.str());
functionVariableUsage(
"class C {\n"
"public:\n"
" C() = default;\n"
"};\n"
"void f() {\n"
" C c;\n"
"}");
ASSERT_EQUALS("[test.cpp:6]: (style) Unused variable: c\n", errout.str());
functionVariableUsage(
"class D {\n"
"public:\n"
" D() {}\n"
"};\n"
"void f() {\n"
" D d;\n"
"}");
ASSERT_EQUALS("[test.cpp:6]: (style) Unused variable: d\n", errout.str());
functionVariableUsage(
"class E {\n"
"public:\n"
" uint32_t u{1};\n"
"};\n"
"void f() {\n"
" E e;\n"
"}");
ASSERT_EQUALS("[test.cpp:6]: (style) Unused variable: e\n", errout.str());
// non-empty constructor
functionVariableUsage(
"class F {\n"
"public:\n"
" F() {\n"
" int i =0;\n"
" (void) i;"
" }\n"
"};\n"
"void f() {\n"
" F f;\n"
"}");
TODO_ASSERT_EQUALS("error", "", errout.str());
// side-effect variable
functionVariableUsage(
"class F {\n"
"public:\n"
" F() {\n"
" int i =0;\n"
" (void) i;"
" }\n"
"};\n"
"class G {\n"
"public:\n"
" F f;\n"
"};\n"
"void f() {\n"
" G g;\n"
"}");
ASSERT_EQUALS("", errout.str());
// unknown variable type
functionVariableUsage(
"class H {\n"
"public:\n"
" unknown_type u{1};\n"
"};\n"
"void f() {\n"
" H h;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
// #5355 - False positive: Variable is not assigned a value. // #5355 - False positive: Variable is not assigned a value.
void emptyclass() { void emptyclass() {
functionVariableUsage("class Carla {\n" functionVariableUsage("class Carla {\n"