From 00886b4d0696f18571687773ca5c9c2da60ea171 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Fri, 5 Jul 2013 21:07:07 +0200 Subject: [PATCH] Fixed #4876 (Checking for sizeof(void)) --- lib/checksizeof.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++ lib/checksizeof.h | 13 ++++++++++++- test/testsizeof.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index 2fe6a32c1..2e403f1e9 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -278,3 +278,46 @@ void CheckSizeof::divideSizeofError(const Token *tok) "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " "not the size of the memory area it points to.", true); } + +void CheckSizeof::sizeofVoid() +{ + if (!_settings->isEnabled("portability")) + return; + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof ( )")) { // "sizeof(void)" gets simplified to sizeof ( ) + sizeofVoidError(tok); + } else if (Token::Match(tok, "sizeof ( * %var% )") && tok->tokAt(3)->variable() && + (Token::Match(tok->tokAt(3)->variable()->typeStartToken(), "void * !!*")) && + (!tok->tokAt(3)->variable()->isArray())) { // sizeof(*p) where p is of type "void*" + sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); + } else if (Token::Match(tok, "%var% +|-|++|--") || Token::Match(tok, "+|-|++|-- %var%")) { // Arithmetic operations on variable of type "void*" + int index = (tok->isName()) ? 0 : 1; + const Variable* var = tok->tokAt(index)->variable(); + if (var && Token::Match(var->typeStartToken(), "void *")) { + arithOperationsOnVoidPointerError(tok, tok->tokAt(index)->str()); + } + } + } +} + +void CheckSizeof::sizeofVoidError(const Token *tok) +{ + const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; + const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose); +} + +void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) +{ + const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; + const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose); +} + +void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname) +{ + const std::string message = "'" + varname + "' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined."; + const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", message + "\n" + verbose); +} diff --git a/lib/checksizeof.h b/lib/checksizeof.h index 323649acd..78c9794c7 100644 --- a/lib/checksizeof.h +++ b/lib/checksizeof.h @@ -58,6 +58,7 @@ public: checkSizeof.checkSizeofForArrayParameter(); checkSizeof.checkSizeofForPointerSize(); checkSizeof.checkSizeofForNumericParameter(); + checkSizeof.sizeofVoid(); } /** @brief Run checks against the simplified token list */ @@ -82,6 +83,9 @@ public: /** @brief %Check for using sizeof with numeric given as function argument */ void checkSizeofForNumericParameter(); + /** @brief %Check for using sizeof(void) */ + void sizeofVoid(); + private: // Error messages.. void sizeofsizeofError(const Token* tok); @@ -91,6 +95,9 @@ private: void sizeofForArrayParameterError(const Token* tok); void sizeofForPointerError(const Token* tok, const std::string &varname); void sizeofForNumericParameterError(const Token* tok); + void sizeofVoidError(const Token *tok); + void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); + void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const { CheckSizeof c(0, settings, errorLogger); @@ -102,6 +109,9 @@ private: c.sizeofCalculationError(0, false); c.multiplySizeofError(0); c.divideSizeofError(0); + c.sizeofVoidError(0); + c.sizeofDereferencedVoidPointerError(0, "varname"); + c.arithOperationsOnVoidPointerError(0, "varname"); } static std::string myName() { @@ -116,7 +126,8 @@ private: "* using sizeof(pointer) instead of the size of pointed data\n" "* look for 'sizeof sizeof ..'\n" "* look for calculations inside sizeof()\n" - "* look for suspicious calculations with sizeof()\n"; + "* look for suspicious calculations with sizeof()\n" + "* using 'sizeof(void)' which is undefined\n"; } }; /// @} diff --git a/test/testsizeof.cpp b/test/testsizeof.cpp index 110a68f47..707ceb550 100644 --- a/test/testsizeof.cpp +++ b/test/testsizeof.cpp @@ -39,6 +39,7 @@ private: TEST_CASE(sizeofForArrayParameter); TEST_CASE(sizeofForNumericParameter); TEST_CASE(suspiciousSizeofCalculation); + TEST_CASE(sizeofVoid); } void check(const char code[]) { @@ -47,6 +48,7 @@ private: Settings settings; settings.addEnabled("warning"); + settings.addEnabled("portability"); settings.inconclusive = true; // Tokenize.. @@ -488,6 +490,50 @@ private: "}"); ASSERT_EQUALS("", errout.str()); } + + void sizeofVoid() { + check("void f() {\n" + " int size = sizeof(void);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Behaviour of 'sizeof(void)' is not covered by the ISO C standard.\n", errout.str()); + + check("void f() {\n" + " void* p;\n" + " int size = sizeof(*p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) '*p' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard.\n", errout.str()); + + check("void f() {\n" + " void* p = malloc(10);\n" + " int* p2 = p + 4;\n" + " int* p3 = p - 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" + "[test.cpp:4]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); + + check("void f() {\n" + " void* p1 = malloc(10);\n" + " void* p2 = malloc(5);\n" + " p1--;\n" + " p2++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (portability) 'p1' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" + "[test.cpp:5]: (portability) 'p2' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); + + check("void f() {\n" + " void** p1;\n" + " int j = sizeof(*p1);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " void* p1[5];\n" + " int j = sizeof(*p1);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + }; REGISTER_TEST(TestSizeof) +