Skip to content

Commit 00886b4

Browse files
lucasmroddanmar
authored andcommitted
Fixed cppcheck-opensource#4876 (Checking for sizeof(void))
1 parent 61e1dd5 commit 00886b4

3 files changed

Lines changed: 101 additions & 1 deletion

File tree

lib/checksizeof.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,46 @@ void CheckSizeof::divideSizeofError(const Token *tok)
278278
"Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, "
279279
"not the size of the memory area it points to.", true);
280280
}
281+
282+
void CheckSizeof::sizeofVoid()
283+
{
284+
if (!_settings->isEnabled("portability"))
285+
return;
286+
287+
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
288+
if (Token::simpleMatch(tok, "sizeof ( )")) { // "sizeof(void)" gets simplified to sizeof ( )
289+
sizeofVoidError(tok);
290+
} else if (Token::Match(tok, "sizeof ( * %var% )") && tok->tokAt(3)->variable() &&
291+
(Token::Match(tok->tokAt(3)->variable()->typeStartToken(), "void * !!*")) &&
292+
(!tok->tokAt(3)->variable()->isArray())) { // sizeof(*p) where p is of type "void*"
293+
sizeofDereferencedVoidPointerError(tok, tok->strAt(3));
294+
} else if (Token::Match(tok, "%var% +|-|++|--") || Token::Match(tok, "+|-|++|-- %var%")) { // Arithmetic operations on variable of type "void*"
295+
int index = (tok->isName()) ? 0 : 1;
296+
const Variable* var = tok->tokAt(index)->variable();
297+
if (var && Token::Match(var->typeStartToken(), "void *")) {
298+
arithOperationsOnVoidPointerError(tok, tok->tokAt(index)->str());
299+
}
300+
}
301+
}
302+
}
303+
304+
void CheckSizeof::sizeofVoidError(const Token *tok)
305+
{
306+
const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
307+
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.";
308+
reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose);
309+
}
310+
311+
void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname)
312+
{
313+
const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
314+
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.";
315+
reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose);
316+
}
317+
318+
void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname)
319+
{
320+
const std::string message = "'" + varname + "' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.";
321+
const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1.";
322+
reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", message + "\n" + verbose);
323+
}

lib/checksizeof.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class CPPCHECKLIB CheckSizeof : public Check {
5858
checkSizeof.checkSizeofForArrayParameter();
5959
checkSizeof.checkSizeofForPointerSize();
6060
checkSizeof.checkSizeofForNumericParameter();
61+
checkSizeof.sizeofVoid();
6162
}
6263

6364
/** @brief Run checks against the simplified token list */
@@ -82,6 +83,9 @@ class CPPCHECKLIB CheckSizeof : public Check {
8283
/** @brief %Check for using sizeof with numeric given as function argument */
8384
void checkSizeofForNumericParameter();
8485

86+
/** @brief %Check for using sizeof(void) */
87+
void sizeofVoid();
88+
8589
private:
8690
// Error messages..
8791
void sizeofsizeofError(const Token* tok);
@@ -91,6 +95,9 @@ class CPPCHECKLIB CheckSizeof : public Check {
9195
void sizeofForArrayParameterError(const Token* tok);
9296
void sizeofForPointerError(const Token* tok, const std::string &varname);
9397
void sizeofForNumericParameterError(const Token* tok);
98+
void sizeofVoidError(const Token *tok);
99+
void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname);
100+
void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname);
94101

95102
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const {
96103
CheckSizeof c(0, settings, errorLogger);
@@ -102,6 +109,9 @@ class CPPCHECKLIB CheckSizeof : public Check {
102109
c.sizeofCalculationError(0, false);
103110
c.multiplySizeofError(0);
104111
c.divideSizeofError(0);
112+
c.sizeofVoidError(0);
113+
c.sizeofDereferencedVoidPointerError(0, "varname");
114+
c.arithOperationsOnVoidPointerError(0, "varname");
105115
}
106116

107117
static std::string myName() {
@@ -116,7 +126,8 @@ class CPPCHECKLIB CheckSizeof : public Check {
116126
"* using sizeof(pointer) instead of the size of pointed data\n"
117127
"* look for 'sizeof sizeof ..'\n"
118128
"* look for calculations inside sizeof()\n"
119-
"* look for suspicious calculations with sizeof()\n";
129+
"* look for suspicious calculations with sizeof()\n"
130+
"* using 'sizeof(void)' which is undefined\n";
120131
}
121132
};
122133
/// @}

test/testsizeof.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class TestSizeof : public TestFixture {
3939
TEST_CASE(sizeofForArrayParameter);
4040
TEST_CASE(sizeofForNumericParameter);
4141
TEST_CASE(suspiciousSizeofCalculation);
42+
TEST_CASE(sizeofVoid);
4243
}
4344

4445
void check(const char code[]) {
@@ -47,6 +48,7 @@ class TestSizeof : public TestFixture {
4748

4849
Settings settings;
4950
settings.addEnabled("warning");
51+
settings.addEnabled("portability");
5052
settings.inconclusive = true;
5153

5254
// Tokenize..
@@ -488,6 +490,50 @@ class TestSizeof : public TestFixture {
488490
"}");
489491
ASSERT_EQUALS("", errout.str());
490492
}
493+
494+
void sizeofVoid() {
495+
check("void f() {\n"
496+
" int size = sizeof(void);\n"
497+
"}");
498+
ASSERT_EQUALS("[test.cpp:2]: (portability) Behaviour of 'sizeof(void)' is not covered by the ISO C standard.\n", errout.str());
499+
500+
check("void f() {\n"
501+
" void* p;\n"
502+
" int size = sizeof(*p);\n"
503+
"}");
504+
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());
505+
506+
check("void f() {\n"
507+
" void* p = malloc(10);\n"
508+
" int* p2 = p + 4;\n"
509+
" int* p3 = p - 1;\n"
510+
"}");
511+
ASSERT_EQUALS("[test.cpp:3]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n"
512+
"[test.cpp:4]: (portability) 'p' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str());
513+
514+
check("void f() {\n"
515+
" void* p1 = malloc(10);\n"
516+
" void* p2 = malloc(5);\n"
517+
" p1--;\n"
518+
" p2++;\n"
519+
"}");
520+
ASSERT_EQUALS("[test.cpp:4]: (portability) 'p1' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n"
521+
"[test.cpp:5]: (portability) 'p2' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str());
522+
523+
check("void f() {\n"
524+
" void** p1;\n"
525+
" int j = sizeof(*p1);\n"
526+
"}");
527+
ASSERT_EQUALS("", errout.str());
528+
529+
check("void f() {\n"
530+
" void* p1[5];\n"
531+
" int j = sizeof(*p1);\n"
532+
"}");
533+
ASSERT_EQUALS("", errout.str());
534+
}
535+
491536
};
492537

493538
REGISTER_TEST(TestSizeof)
539+

0 commit comments

Comments
 (0)