diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 627f9b939..82ad8d2cb 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1671,6 +1671,21 @@ static bool nextIsStandardTypeOrVoid(const Token *tok) return tok->isStandardType() || tok->str() == "void"; } +bool CheckOther::isRecordTypeWithoutSideEffects(const Token *tok) +{ + const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId()); + + // a type that has no side effects (no constructors and no members with constructors) + /** @todo false negative: check base class for side effects */ + /** @todo false negative: check constructors for side effects */ + if (var && var->type() && var->type()->numConstructors == 0 && + (var->type()->varlist.empty() || var->type()->needInitialization == Scope::True) && + var->type()->derivedFrom.empty()) + return true; + + return false; +} + void CheckOther::functionVariableUsage() { if (!_settings->_checkCodingStyle) @@ -1738,7 +1753,7 @@ void CheckOther::functionVariableUsage() // standard type declaration with possible initialization // int i; int j = 0; static int k; if (Token::Match(tok, "[;{}] static| %type% %var% ;|=") && - nextIsStandardType(tok)) + (nextIsStandardType(tok) || isRecordTypeWithoutSideEffects(tok->strAt(1) == "static" ? tok->tokAt(3) : tok->tokAt(2)))) { tok = tok->next(); diff --git a/lib/checkother.h b/lib/checkother.h index 9ee973359..0a98a7472 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -224,6 +224,9 @@ public: /** @brief %Check for suspicious code that compares string literals for equality */ void checkAlwaysTrueOrFalseStringCompare(); + /** @brief check if token is a record type without side effects */ + bool isRecordTypeWithoutSideEffects(const Token *tok); + // Error messages.. void cstyleCastError(const Token *tok); void dangerousUsageStrtolError(const Token *tok); diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index dd49cec5e..b0b9de55c 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -104,6 +104,7 @@ private: TEST_CASE(localvarStruct2); TEST_CASE(localvarStruct3); TEST_CASE(localvarStruct4); // Ticket #31: sigsegv on incomplete struct + TEST_CASE(localvarStruct5); TEST_CASE(localvarOp); // Usage with arithmetic operators TEST_CASE(localvarInvert); // Usage with inverted variable @@ -2348,6 +2349,107 @@ private: " struct { \n"); } + void localvarStruct5() + { + functionVariableUsage("int foo() {\n" + " A a;\n" + " return a.i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("struct A { int i; };\n" + "int foo() {\n" + " A a;\n" + " return a.i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("class A { int i; };\n" + "int foo() {\n" + " A a;\n" + " return a.i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("struct A { int i; };\n" + "int foo() {\n" + " A a;\n" + " a.i = 0;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("class A { int i; };\n" + "int foo() {\n" + " A a;\n" + " a.i = 0;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("struct A { int i; };\n" + "int foo() {\n" + " A a = { 0 };\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n", errout.str()); + + functionVariableUsage("class A { int i; };\n" + "int foo() {\n" + " A a = { 0 };\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n", errout.str()); + + functionVariableUsage("class A { int i; public: A(); { } };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("struct A { int i; };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str()); + + functionVariableUsage("class A { int i; };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: a\n", errout.str()); + + functionVariableUsage("class A { int i; public: A(); { } };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("class A { unknown i; };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("class A : public Fred { int i; };\n" + "int foo() {\n" + " A a;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void localvarOp() { const char op[] = "+-*/%&|^";