From 2d2a694dfcf6ab923af5a42608c0810b423b7a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 14:02:31 +0200 Subject: [PATCH 01/25] Fix #13875 (Document and rewrite cstyleCast) [ci skip] --- man/checkers/cstyleCast.md | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 man/checkers/cstyleCast.md diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md new file mode 100644 index 00000000000..7b1d9048a1c --- /dev/null +++ b/man/checkers/cstyleCast.md @@ -0,0 +1,68 @@ + +# cstyleCast + +**Category**: Type Safety
+**Severity**: Style
+**Language**: C++, not applicable for C code + +## Motivation + +Casts can be dangerous: + * there can be unintentional loss of precision + * there can be unintentional loss of sign + * there can be unintentional loss of constness + * there can be invalid type conversions + +## Philosophy + +This checker warns about old style C casts. + +This checker is not about readability. It is about safety. + +We only want to warn if there are possible safety issues and a C++ cast can provide better safety. + +## Other tools / complements + +Related checkers: + * Misra C 11.8 is violated if there is "loss of constness" + * Misra C++ 8.2.2 is more pedantic than this checker and warns about all C style casts. + * Warnings about "loss of constness" may be written by compilers (gcc/clang reports Wcast-qual). + +## Example + +### Compliant C style cast + +The goal is to not warn about "safe" and/or "intentional" casts. + +For example: +``` +int *p = (int*)0; +``` + +### Non compliant C style cast + +A cast from a base class pointer to a derived class pointer is potentially unsafe: +``` +Derived *p = (Derived*)base; +``` +If it is obvious that `base` always point at a Derived object the cast is safe and ideally no warning should be reported. +But otherwise a `dynamic_cast` is safer. + +## How to fix + +You can use `dynamic_cast` or `static_cast` to fix warnings. + +Before: +``` +Derived *p = (Derived*)base; +``` + +After: +``` +Derived *p = dynamic_cast(base); +``` + +## Enable + +--enable=style + From fde3cf6223b8b3583a6fe5917dd00f96e6d58072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 16:15:31 +0200 Subject: [PATCH 02/25] tweaks --- man/checkers/cstyleCast.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 7b1d9048a1c..4a26f7efe5d 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -7,7 +7,7 @@ ## Motivation -Casts can be dangerous: +C style casts can be dangerous in many ways: * there can be unintentional loss of precision * there can be unintentional loss of sign * there can be unintentional loss of constness @@ -15,18 +15,19 @@ Casts can be dangerous: ## Philosophy -This checker warns about old style C casts. +This checker warns about old style C casts that looks dangerous. This checker is not about readability. It is about safety. We only want to warn if there are possible safety issues and a C++ cast can provide better safety. -## Other tools / complements +## Other tools / checkers Related checkers: - * Misra C 11.8 is violated if there is "loss of constness" - * Misra C++ 8.2.2 is more pedantic than this checker and warns about all C style casts. - * Warnings about "loss of constness" may be written by compilers (gcc/clang reports Wcast-qual). + * Complements: Misra C 11.8 is violated if there is "loss of constness" + * More noisy: Misra C++ 8.2.2, cover all C style casts. + * More noisy: C style cast compiler warnings (i.e. clang reports Wold-style-cast) + * Complements: Loss of constness compiler warnings. ## Example @@ -38,6 +39,13 @@ For example: ``` int *p = (int*)0; ``` +A static_cast can be used however technically it's the same as the C style cast. + +Other example: +``` +uint8_t x = (uint8_t)0x1234; +``` +A static_cast can be used however technically it's the same as the C style cast. ### Non compliant C style cast @@ -45,8 +53,7 @@ A cast from a base class pointer to a derived class pointer is potentially unsaf ``` Derived *p = (Derived*)base; ``` -If it is obvious that `base` always point at a Derived object the cast is safe and ideally no warning should be reported. -But otherwise a `dynamic_cast` is safer. +A `dynamic_cast` is safer than the C-style cast here. ## How to fix From 7b5562c44df0cdd42d1e940ce1ba31f3e1a0a777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 18:33:50 +0200 Subject: [PATCH 03/25] message --- man/checkers/cstyleCast.md | 1 + 1 file changed, 1 insertion(+) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 4a26f7efe5d..4aecfaf9d8a 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -1,6 +1,7 @@ # cstyleCast +**Message**: C-style pointer casting
**Category**: Type Safety
**Severity**: Style
**Language**: C++, not applicable for C code From 1ba34481a69e527f8ab24aa00ee59433ed484ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 20:37:10 +0200 Subject: [PATCH 04/25] tweaks --- man/checkers/cstyleCast.md | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 4aecfaf9d8a..7e661a61569 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -9,10 +9,10 @@ ## Motivation C style casts can be dangerous in many ways: - * there can be unintentional loss of precision - * there can be unintentional loss of sign - * there can be unintentional loss of constness - * there can be invalid type conversions + * loss of precision + * loss of sign + * loss of constness + * invalid type conversion ## Philosophy @@ -20,17 +20,22 @@ This checker warns about old style C casts that looks dangerous. This checker is not about readability. It is about safety. -We only want to warn if there are possible safety issues and a C++ cast can provide better safety. +This checker focus on invalid type conversions: +If there is loss of sign or precision in the cast; it's hard to know for Cppcheck if that is intentional. +If there is loss of constness; there are other checkers available and these apply to C code also. +Therefore we only warn about pointer/reference casts that could be invalid type conversions. ## Other tools / checkers -Related checkers: - * Complements: Misra C 11.8 is violated if there is "loss of constness" - * More noisy: Misra C++ 8.2.2, cover all C style casts. - * More noisy: C style cast compiler warnings (i.e. clang reports Wold-style-cast) - * Complements: Loss of constness compiler warnings. +If you want more warnings that warn about all c style casts: + * Misra C++ 8.2.2, cover all C style casts. + * C style cast compiler warnings (i.e. clang reports Wold-style-cast) -## Example +Checking for loss of constness: + * Misra C 11.8 + * Loss of constness compiler warnings (i.e. clang reports Wcast-qual). + +## Examples ### Compliant C style cast @@ -42,19 +47,12 @@ int *p = (int*)0; ``` A static_cast can be used however technically it's the same as the C style cast. -Other example: -``` -uint8_t x = (uint8_t)0x1234; -``` -A static_cast can be used however technically it's the same as the C style cast. - ### Non compliant C style cast A cast from a base class pointer to a derived class pointer is potentially unsafe: ``` Derived *p = (Derived*)base; ``` -A `dynamic_cast` is safer than the C-style cast here. ## How to fix From dd7216f170643dee0d5770889b20d7f978f40ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 20:41:18 +0200 Subject: [PATCH 05/25] review comments [ci skip] --- man/checkers/cstyleCast.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 7e661a61569..a363e745561 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -42,7 +42,7 @@ Checking for loss of constness: The goal is to not warn about "safe" and/or "intentional" casts. For example: -``` +```cpp int *p = (int*)0; ``` A static_cast can be used however technically it's the same as the C style cast. @@ -50,8 +50,12 @@ A static_cast can be used however technically it's the same as the C style cast. ### Non compliant C style cast A cast from a base class pointer to a derived class pointer is potentially unsafe: -``` -Derived *p = (Derived*)base; +```cpp +struct Base{}; +struct Derived: public Base {}; +void foo(Base* base) { + Derived *p = (Derived*)base; // <- dangerous +} ``` ## How to fix @@ -59,16 +63,19 @@ Derived *p = (Derived*)base; You can use `dynamic_cast` or `static_cast` to fix warnings. Before: -``` -Derived *p = (Derived*)base; +```cpp +struct Base{}; +struct Derived: public Base {}; +void foo(Base* base) { + Derived *p = (Derived*)base; // <- dangerous +} ``` After: +```cpp +struct Base{}; +struct Derived: public Base {}; +void foo(Base* base) { + Derived *p = dynamic_cast(base); +} ``` -Derived *p = dynamic_cast(base); -``` - -## Enable - ---enable=style - From c904d6dc0c70811d2b952f90ef5b73aee6877993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 18 May 2025 22:50:46 +0200 Subject: [PATCH 06/25] tweaking [ci skip] --- man/checkers/cstyleCast.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index a363e745561..e91ebb49fc7 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -27,11 +27,11 @@ Therefore we only warn about pointer/reference casts that could be invalid type ## Other tools / checkers -If you want more warnings that warn about all c style casts: +Extended checking; if you want more warnings that warn about all c style casts: * Misra C++ 8.2.2, cover all C style casts. * C style cast compiler warnings (i.e. clang reports Wold-style-cast) -Checking for loss of constness: +Complementing; checking for loss of constness etc: * Misra C 11.8 * Loss of constness compiler warnings (i.e. clang reports Wcast-qual). From 15d0a8fc09c3780d55354d9b1781b75d311bd627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 19 May 2025 10:41:55 +0200 Subject: [PATCH 07/25] Update man/checkers/cstyleCast.md [skip ci] Co-authored-by: chrchr-github <78114321+chrchr-github@users.noreply.github.com> --- man/checkers/cstyleCast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index e91ebb49fc7..111f3b4278b 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -27,7 +27,7 @@ Therefore we only warn about pointer/reference casts that could be invalid type ## Other tools / checkers -Extended checking; if you want more warnings that warn about all c style casts: +Extended checking; if you want more warnings that warn about all C style casts: * Misra C++ 8.2.2, cover all C style casts. * C style cast compiler warnings (i.e. clang reports Wold-style-cast) From 23e19aa156fac08bbcf777293546b4620b384371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 19 May 2025 10:42:11 +0200 Subject: [PATCH 08/25] Update man/checkers/cstyleCast.md [skip ci] Co-authored-by: chrchr-github <78114321+chrchr-github@users.noreply.github.com> --- man/checkers/cstyleCast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 111f3b4278b..a398b3c5a33 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -16,7 +16,7 @@ C style casts can be dangerous in many ways: ## Philosophy -This checker warns about old style C casts that looks dangerous. +This checker warns about old style C casts that can be dangerous. This checker is not about readability. It is about safety. From 264f1360debce33a0f2590c32431832541d50047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 19 May 2025 10:42:27 +0200 Subject: [PATCH 09/25] Update man/checkers/cstyleCast.md [skip ci] Co-authored-by: chrchr-github <78114321+chrchr-github@users.noreply.github.com> --- man/checkers/cstyleCast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index a398b3c5a33..7bfed3bb424 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -29,7 +29,7 @@ Therefore we only warn about pointer/reference casts that could be invalid type Extended checking; if you want more warnings that warn about all C style casts: * Misra C++ 8.2.2, cover all C style casts. - * C style cast compiler warnings (i.e. clang reports Wold-style-cast) + * C style cast compiler warnings (i.e. clang reports -Wold-style-cast) Complementing; checking for loss of constness etc: * Misra C 11.8 From 59aaaf1e7578aff7d9a1fea315e1bb8821665a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 19 May 2025 10:42:39 +0200 Subject: [PATCH 10/25] Update man/checkers/cstyleCast.md [skip ci] Co-authored-by: chrchr-github <78114321+chrchr-github@users.noreply.github.com> --- man/checkers/cstyleCast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 7bfed3bb424..0f64ef38e56 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -33,7 +33,7 @@ Extended checking; if you want more warnings that warn about all C style casts: Complementing; checking for loss of constness etc: * Misra C 11.8 - * Loss of constness compiler warnings (i.e. clang reports Wcast-qual). + * Loss of constness compiler warnings (i.e. clang reports -Wcast-qual). ## Examples From dff9a37a681f22a0cc0253195ada2dee59ce70d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 20 May 2025 10:10:48 +0200 Subject: [PATCH 11/25] tweak checker --- lib/checkother.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 282f3f3e50e..8744eee4fcb 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -314,7 +314,7 @@ void CheckOther::warningOldStylePointerCast() tok = scope->bodyStart; for (; tok && tok != scope->bodyEnd; tok = tok->next()) { // Old style pointer casting.. - if (tok->str() != "(") + if (!tok->isCast() || tok->isBinaryOp()) continue; const Token* castTok = tok->next(); while (Token::Match(castTok, "const|volatile|class|struct|union|%type%|::")) { From 6271ad9a9875a30342eaefac6b6c25e5b29e3513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 20 May 2025 19:16:15 +0200 Subject: [PATCH 12/25] tweak checker to match documentation --- lib/checkother.cpp | 71 +++++++++++----------- lib/checkother.h | 2 + test/testother.cpp | 144 ++++++++++++++++++++------------------------- 3 files changed, 104 insertions(+), 113 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 8744eee4fcb..58194131977 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -296,10 +296,6 @@ void CheckOther::suspiciousSemicolonError(const Token* tok) //--------------------------------------------------------------------------- void CheckOther::warningOldStylePointerCast() { - // Only valid on C++ code - if (!mTokenizer->isCPP()) - return; - if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("cstyleCast")) return; @@ -316,42 +312,42 @@ void CheckOther::warningOldStylePointerCast() // Old style pointer casting.. if (!tok->isCast() || tok->isBinaryOp()) continue; - const Token* castTok = tok->next(); - while (Token::Match(castTok, "const|volatile|class|struct|union|%type%|::")) { - castTok = castTok->next(); - if (Token::simpleMatch(castTok, "<") && castTok->link()) - castTok = castTok->link()->next(); - } - if (castTok == tok->next()) + const Token* from = tok->astOperand1(); + if (!from) continue; - bool isPtr = false, isRef = false; - while (Token::Match(castTok, "*|const|&")) { - if (castTok->str() == "*") - isPtr = true; - else if (castTok->str() == "&") - isRef = true; - castTok = castTok->next(); - } - if ((!isPtr && !isRef) || !Token::Match(castTok, ") (| %name%|%num%|%bool%|%char%|%str%|&")) + if (!tok->valueType() || !from->valueType()) continue; - - if (Token::Match(tok->previous(), "%type%")) + if (tok->valueType()->type == from->valueType()->type && + tok->valueType()->typeScope == from->valueType()->typeScope) continue; + const bool refcast = (tok->valueType()->reference != Reference::None); + if (!refcast && tok->valueType()->pointer == 0) + continue; + if (!refcast && from->valueType()->pointer == 0) { + // casting non-zero integer literal (decimal/octal) to pointer is suspicious + if (mSettings->severity.isEnabled(Severity::portability) && + tok->valueType()->pointer > 0 && + from->isNumber() && + !MathLib::isIntHex(from->str()) && + from->getKnownIntValue() != 0) + intToPointerCastError(tok); + continue; + } - // skip first "const" in "const Type* const" - while (Token::Match(tok->next(), "const|volatile|class|struct|union")) - tok = tok->next(); - const Token* typeTok = tok->next(); - // skip second "const" in "const Type* const" - if (tok->strAt(3) == "const") - tok = tok->next(); + // Use C++ cast: Only valid on C++ code + if (!mTokenizer->isCPP()) + continue; - const Token *p = tok->tokAt(4); - if (p->hasKnownIntValue() && p->getKnownIntValue()==0) // Casting nullpointers is safe + if (tok->valueType()->type == ValueType::Type::VOID || from->valueType()->type == ValueType::Type::VOID) + continue; + if (tok->valueType()->pointer == 0 && tok->valueType()->isIntegral()) + // ok: (uintptr_t)ptr; + continue; + if (from->valueType()->pointer == 0 && from->valueType()->isIntegral()) + // ok: (int *)addr; continue; - if (typeTok->tokType() == Token::eType || typeTok->tokType() == Token::eName) - cstyleCastError(tok, isPtr); + cstyleCastError(tok, tok->valueType()->pointer > 0); } } } @@ -367,6 +363,14 @@ void CheckOther::cstyleCastError(const Token *tok, bool isPtr) "which kind of cast is expected.", CWE398, Certainty::normal); } + +void CheckOther::intToPointerCastError(const Token *tok) +{ + reportError(tok, Severity::portability, "intToPointerCast", + "Casting non-zero integer literal in decimal or octal format to pointer.", + CWE398, Certainty::normal); +} + void CheckOther::suspiciousFloatingPointCast() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("suspiciousFloatingPointCast")) @@ -4459,6 +4463,7 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); + c.intToPointerCastError(nullptr); c.suspiciousFloatingPointCastError(nullptr); c.passedByValueError(nullptr, false); c.constVariableError(nullptr, nullptr); diff --git a/lib/checkother.h b/lib/checkother.h index b32461d07a9..a94f301a862 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -198,6 +198,7 @@ class CPPCHECKLIB CheckOther : public Check { void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok, bool isPtr = true); + void intToPointerCastError(const Token *tok); void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false); @@ -281,6 +282,7 @@ class CPPCHECKLIB CheckOther : public Check { // portability "- Passing NULL pointer to function with variable number of arguments leads to UB.\n" + "- Casting non-zero integer literal in decimal or octal format to pointer.\n" // style "- C-style pointer cast in C++ code\n" diff --git a/test/testother.cpp b/test/testother.cpp index 0eb6218361e..050defff22a 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1901,7 +1901,7 @@ class TestOther : public TestFixture { template void checkOldStylePointerCast_(const char* file, int line, const char (&code)[size], Standards::cppstd_t std = Standards::CPPLatest) { - const Settings settings = settingsBuilder().severity(Severity::style).cpp(std).build(); + const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::portability).cpp(std).build(); // Tokenize.. SimpleTokenizer tokenizerCpp(settings, *this); @@ -1912,75 +1912,44 @@ class TestOther : public TestFixture { } void oldStylePointerCast() { - checkOldStylePointerCast("class Base;\n" - "void foo()\n" + checkOldStylePointerCast("class Base{};\n" + "class Derived: public Base {};\n" + "void foo(Derived* derived)\n" "{\n" " Base * b = (Base *) derived;\n" "}"); - ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - checkOldStylePointerCast("class Base;\n" - "void foo()\n" + checkOldStylePointerCast("class Base{};\n" + "class Derived: public Base {};\n" + "void foo(Derived* derived)\n" "{\n" " Base * b = (const Base *) derived;\n" "}"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - - checkOldStylePointerCast("class Base;\n" - "void foo()\n" - "{\n" - " Base * b = (const Base * const) derived;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4:23]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - - checkOldStylePointerCast("class Base;\n" - "void foo()\n" - "{\n" - " Base * b = (volatile Base *) derived;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - - checkOldStylePointerCast("class Base;\n" - "void foo()\n" - "{\n" - " Base * b = (volatile Base * const) derived;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4:26]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - - checkOldStylePointerCast("class Base;\n" - "void foo()\n" - "{\n" - " Base * b = (const volatile Base *) derived;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4:23]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - - checkOldStylePointerCast("class Base;\n" - "void foo()\n" - "{\n" - " Base * b = (const volatile Base * const) derived;\n" - "}"); - ASSERT_EQUALS("[test.cpp:4:32]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - checkOldStylePointerCast("class Base;\n" + checkOldStylePointerCast("class Base{};\n" + "class Derived: public Base {};\n" "void foo()\n" "{\n" - " Base * b = (const Base *) ( new Derived() );\n" + " Base * b = (Base *) ( new Derived() );\n" "}"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - checkOldStylePointerCast("class Base;\n" + checkOldStylePointerCast("class Base{};\n" + "class Derived: public Base {};\n" "void foo()\n" "{\n" - " Base * b = (const Base *) new Derived();\n" + " Base * b = (Base *) new Derived();\n" "}"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); - checkOldStylePointerCast("class Base;\n" + checkOldStylePointerCast("class Base{};\n" "void foo()\n" "{\n" - " Base * b = (const Base *) new short[10];\n" + " Base * b = (Base *) new short[10];\n" "}"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class B;\n" "class A\n" @@ -1997,17 +1966,23 @@ class TestOther : public TestFixture { ASSERT_EQUALS("", errout_str()); // #3630 - checkOldStylePointerCast("class SomeType;\n" + checkOldStylePointerCast("class SomeType{};\n" "class X : public Base {\n" " X() : Base((SomeType*)7) {}\n" "};"); - ASSERT_EQUALS("[test.cpp:3:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:3:16]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + + checkOldStylePointerCast("class SomeType{};\n" + "class X : public Base {\n" + " X() : Base((SomeType*)0x7000) {}\n" // <- it's common in embedded code to cast address + "};"); + ASSERT_EQUALS("", errout_str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)var) {}\n" "};"); - ASSERT_EQUALS("[test.cpp:3:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("", errout_str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" @@ -2032,7 +2007,7 @@ class TestOther : public TestFixture { " std::vector v;\n" " v.push_back((Base*)new Derived);\n" "}"); - ASSERT_EQUALS("[test.cpp:5:15]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + // FIXME ASSERT_EQUALS("[test.cpp:5:15]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #7709 checkOldStylePointerCast("typedef struct S S;\n" @@ -2055,29 +2030,38 @@ class TestOther : public TestFixture { " TT* tt = (TT*)i;\n" " TT2* tt2 = (TT2*)i;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:13]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:11:15]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:12:22]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:13:13]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:14:21]: (style) C-style pointer casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:10:12]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:11:14]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:12:21]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:13:12]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:14:20]: (style) C-style pointer casting [cstyleCast]\n" "[test.cpp:15:15]: (style) C-style pointer casting [cstyleCast]\n" "[test.cpp:16:16]: (style) C-style pointer casting [cstyleCast]\n" "[test.cpp:17:16]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:18:15]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:19:17]: (style) C-style pointer casting [cstyleCast]\n", + "[test.cpp:18:14]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:19:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #8649 checkOldStylePointerCast("struct S {};\n" "void g(S*& s);\n" - "void f(int i) {\n" + "void f(uintptr_t i) {\n" " g((S*&)i);\n" " S*& r = (S*&)i;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:7]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:5:13]: (style) C-style pointer casting [cstyleCast]\n", + ASSERT_EQUALS("", errout_str()); + checkOldStylePointerCast("struct S {};\n" + "void g(S*& s);\n" + "void f(uint8_t i) {\n" + " g((S*&)i);\n" + " S*& r = (S*&)i;\n" + "}\n"); + // TODO: these conversions are dangerous, but it's a different issue not covered by cstyleCast. A separate checker can be added which is executed for both C and C++ code. + // clang says: 1.cpp:5:18: warning: cast to 'unsigned char *' from smaller integer type 'uint8_t' (aka 'unsigned char') [-Wint-to-pointer-cast] + TODO_ASSERT_EQUALS("bad cast from uint8_t to pointer", "", errout_str()); + // #10823 checkOldStylePointerCast("void f(void* p) {\n" " auto h = reinterpret_cast(p);\n" @@ -2085,12 +2069,14 @@ class TestOther : public TestFixture { ASSERT_EQUALS("", errout_str()); // #5210 - checkOldStylePointerCast("void f(void* v1, void* v2) {\n" - " T** p1 = (T**)v1;\n" - " T*** p2 = (T***)v2;\n" + checkOldStylePointerCast("class Base {};\n" + "class Derived: public Base {};\n" + "void f(Derived** d1, Derived*** d2) {\n" + " Base** p1 = (Base**)d1;\n" + " Base*** p2 = (Base***)d2;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:2:14]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:3:15]: (style) C-style pointer casting [cstyleCast]\n", + ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:5:18]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #12446 @@ -2104,17 +2090,15 @@ class TestOther : public TestFixture { " auto pu = (union U*)p;\n" " auto pv = (std::vector*)(p);\n" "}\n"); - ASSERT_EQUALS("[test.cpp:7:15]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:8:16]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:9:15]: (style) C-style pointer casting [cstyleCast]\n", - errout_str()); + ASSERT_EQUALS("", errout_str()); // There are other checkers and compiler warnings that warn about all C style casts // #12447 - checkOldStylePointerCast("void f(const int& i) {\n" - " int& r = (int&)i;\n" - " r = 0;\n" + checkOldStylePointerCast("class Base {};\n" + "class Derived: public Base {};\n" + "void f(const Derived& derived) {\n" + " Base& b = (Base&)derived;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:2:12]: (style) C-style reference casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:13]: (style) C-style reference casting [cstyleCast]\n", errout_str()); // #11430 checkOldStylePointerCast("struct B {\n" @@ -2125,9 +2109,9 @@ class TestOther : public TestFixture { "}\n" "bool g(B& b) {\n" " using float_ptr = float*;\n" - " return N::f(float_ptr(b.data()));\n" + " return N::f(float_ptr(b.data()));\n" // <- the cast is safe "}\n"); - ASSERT_EQUALS("[test.cpp:9:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("", errout_str()); } #define checkInvalidPointerCast(...) checkInvalidPointerCast_(__FILE__, __LINE__, __VA_ARGS__) From 158e633585b523caffb0cb4ff4fe30040d5c9b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 20 May 2025 20:23:35 +0200 Subject: [PATCH 13/25] Fix CI --- test/testgarbage.cpp | 1 - test/testother.cpp | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 321d922f2f4..53d0a747a03 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1832,7 +1832,6 @@ class TestGarbage : public TestFixture { ASSERT_THROW_INTERNAL(checkCode("void f(){x=0,return return''[]()}"), SYNTAX); ASSERT_THROW_INTERNAL(checkCode("void f(){x='0'++'0'(return)[];}"), SYNTAX); // #9063 (void)checkCode("void f(){*(int *)42=0;}"); // no syntax error - ignore_errout(); // we are not interested in the output ASSERT_THROW_INTERNAL(checkCode("void f() { x= 'x' > typedef name5 | ( , ;){ } (); }"), SYNTAX); // #9067 ASSERT_THROW_INTERNAL(checkCode("void f() { x= {}( ) ( 'x')[ ] (); }"), SYNTAX); // #9068 ASSERT_THROW_INTERNAL(checkCode("void f() { x= y{ } name5 y[ ] + y ^ name5 ^ name5 for ( ( y y y && y y y && name5 ++ int )); }"), SYNTAX); // #9069 diff --git a/test/testother.cpp b/test/testother.cpp index 050defff22a..33f926cbbf7 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -3097,7 +3097,7 @@ class TestOther : public TestFixture { " x.dostuff();\n" " const U& y = (const U&)(x);\n" "}"); - ASSERT_EQUALS("[test.cpp:4:19]: (style) C-style reference casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:4:18]: (style) C-style reference casting [cstyleCast]\n" "[test.cpp:2:11]: (style) Parameter 'x' can be declared as reference to const [constParameterReference]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" @@ -3112,7 +3112,7 @@ class TestOther : public TestFixture { " x.dostuff();\n" " const U& y = (typename const U&)(x);\n" "}"); - ASSERT_EQUALS("[test.cpp:4:0]: (style) C-style reference casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:4:18]: (style) C-style reference casting [cstyleCast]\n" "[test.cpp:2:11]: (style) Parameter 'x' can be declared as reference to const [constParameterReference]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" @@ -3573,7 +3573,7 @@ class TestOther : public TestFixture { "void g(A* a) {\n" " const B* b = (const B*)a;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:19]: (style) C-style pointer casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:10:18]: (style) C-style pointer casting [cstyleCast]\n" "[test.cpp:6:11]: (style) Parameter 'a' can be declared as pointer to const [constParameterPointer]\n" "[test.cpp:9:11]: (style) Parameter 'a' can be declared as pointer to const [constParameterPointer]\n", errout_str()); @@ -4339,8 +4339,7 @@ class TestOther : public TestFixture { "void f(T* t) {\n" " S* s = (S*)t->p;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3:12]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:3:8]: (style) Variable 's' can be declared as pointer to const [constVariablePointer]\n", + ASSERT_EQUALS("[test.cpp:3:8]: (style) Variable 's' can be declared as pointer to const [constVariablePointer]\n", errout_str()); // don't crash check("struct S { int i; };\n" // #12205 @@ -10160,7 +10159,7 @@ class TestOther : public TestFixture { " *reg = 12;\n" " *reg = 34;\n" "}"); - ASSERT_EQUALS("[test.cpp:2:25]: style: C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("", errout_str()); check("void f(std::map& m, int key, int value) {\n" // #6379 " m[key] = value;\n" From 0bcd04e8bbee0d86778f487e0127f11ad4bc0e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 20 May 2025 21:53:11 +0200 Subject: [PATCH 14/25] fix make checkcfg --- test/cfg/googletest.cpp | 2 +- test/cfg/opencv2.cpp | 3 +-- test/cfg/posix.c | 1 + test/cfg/std.cpp | 14 +++++++------- test/cfg/windows.cpp | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/cfg/googletest.cpp b/test/cfg/googletest.cpp index eaec730136d..66a2c7f9b9b 100644 --- a/test/cfg/googletest.cpp +++ b/test/cfg/googletest.cpp @@ -44,7 +44,7 @@ namespace ExampleNamespace { TEST(ASSERT, ASSERT) { - int *a = (int*)calloc(10,sizeof(int)); // cppcheck-suppress cstyleCast + int *a = (int*)calloc(10,sizeof(int)); ASSERT_TRUE(a != nullptr); a[0] = 10; diff --git a/test/cfg/opencv2.cpp b/test/cfg/opencv2.cpp index 6e79caab8d0..a65b1f2cdb3 100644 --- a/test/cfg/opencv2.cpp +++ b/test/cfg/opencv2.cpp @@ -30,7 +30,7 @@ void validCode(const char* argStr) cvStr += " World"; std::cout << cvStr; - // cppcheck-suppress [cstyleCast, unusedAllocatedMemory] + // cppcheck-suppress unusedAllocatedMemory char * pBuf = (char *)cv::fastMalloc(20); cv::fastFree(pBuf); } @@ -43,7 +43,6 @@ void ignoredReturnValue() void memleak() { - // cppcheck-suppress cstyleCast const char * pBuf = (char *)cv::fastMalloc(1000); // cppcheck-suppress [uninitdata, valueFlowBailoutIncompleteVar, nullPointerOutOfMemory] std::cout << pBuf; diff --git a/test/cfg/posix.c b/test/cfg/posix.c index a8075b21bcd..f54453a471c 100644 --- a/test/cfg/posix.c +++ b/test/cfg/posix.c @@ -144,6 +144,7 @@ void nullPointer_pthread_attr_setstack(const pthread_attr_t *attr) { (void) pthread_attr_setstack(NULL, NULL, 0); (void) pthread_attr_setstack(attr, NULL, 0); // cppcheck-suppress nullPointer + // cppcheck-suppress intToPointerCast (void) pthread_attr_setstack(NULL, (void*) 1, 0); } diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 04216867f00..4dbd0d844f0 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -3027,7 +3027,7 @@ void uninitvar_longjmp(void) void uninitvar_malloc(void) { size_t size; - // cppcheck-suppress [uninitvar, cstyleCast, unusedAllocatedMemory] + // cppcheck-suppress [uninitvar, unusedAllocatedMemory] int *p = (int*)std::malloc(size); free(p); } @@ -3259,7 +3259,7 @@ void uninivar_bsearch(void) const void* base; size_t num; size_t size; - // cppcheck-suppress [uninitvar, cstyleCast] + // cppcheck-suppress uninitvar (void)std::bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); } @@ -3269,11 +3269,11 @@ void minsize_bsearch(const void* key, const void* base, { const int Base[3] = {42, 43, 44}; - (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast - (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast - (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); + (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); + (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); - (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); } void uninitvar_qsort(void) @@ -3282,7 +3282,7 @@ void uninitvar_qsort(void) size_t n; size_t size; // cppcheck-suppress uninitvar - (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); } void uninitvar_stable_sort(std::vector& v) diff --git a/test/cfg/windows.cpp b/test/cfg/windows.cpp index c43bbfeb747..fa9b24f270d 100644 --- a/test/cfg/windows.cpp +++ b/test/cfg/windows.cpp @@ -624,7 +624,7 @@ void memleak_HeapAlloc() void memleak_LocalAlloc() { LPTSTR pszBuf; - // cppcheck-suppress [LocalAllocCalled, cstyleCast, valueFlowBailoutIncompleteVar] + // cppcheck-suppress [LocalAllocCalled, valueFlowBailoutIncompleteVar] pszBuf = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*sizeof(TCHAR)); (void)LocalSize(pszBuf); (void)LocalFlags(pszBuf); @@ -700,7 +700,7 @@ void resourceLeak_LoadLibrary() HINSTANCE hInstLib; hInstLib = ::LoadLibrary(L"My.dll"); typedef BOOL (WINAPI *fpFunc)(); - // cppcheck-suppress [unreadVariable, cstyleCast] + // cppcheck-suppress unreadVariable fpFunc pFunc = (fpFunc)GetProcAddress(hInstLib, "name"); // cppcheck-suppress resourceLeak } @@ -825,7 +825,7 @@ void invalidFunctionArg() CloseHandle(hMutex); //Incorrect: 2. parameter to LoadLibraryEx() must be NULL - // cppcheck-suppress [invalidFunctionArg, cstyleCast] + // cppcheck-suppress [invalidFunctionArg, intToPointerCast] HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", HANDLE(1), 0); FreeLibrary(hInstLib); From e37d52fff217f5d2618de393f57df30acf17632b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 12:11:33 +0200 Subject: [PATCH 15/25] warn about suspicious casts of incomplete classes, do not warn about cast from derived pointer to base pointer --- lib/checkother.cpp | 12 +++++++++++- test/testother.cpp | 46 ++++++++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 58194131977..41cd1d5f93c 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -317,9 +317,19 @@ void CheckOther::warningOldStylePointerCast() continue; if (!tok->valueType() || !from->valueType()) continue; - if (tok->valueType()->type == from->valueType()->type && + if (tok->valueType()->typeScope != nullptr && tok->valueType()->typeScope == from->valueType()->typeScope) continue; + if (tok->valueType()->type == from->valueType()->type && + tok->valueType()->isPrimitive()) + continue; + // cast from derived object to base object is safe.. + if (tok->valueType()->typeScope && from->valueType()->typeScope) { + const Type* fromType = from->valueType()->typeScope->definedType; + const Type* toType = tok->valueType()->typeScope->definedType; + if (fromType && toType && fromType->isDerivedFrom(toType->name())) + continue; + } const bool refcast = (tok->valueType()->reference != Reference::None); if (!refcast && tok->valueType()->pointer == 0) continue; diff --git a/test/testother.cpp b/test/testother.cpp index 33f926cbbf7..b271469592d 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1912,37 +1912,51 @@ class TestOther : public TestFixture { } void oldStylePointerCast() { + checkOldStylePointerCast("class Base{};\n" + "class Derived: public Base {};\n" + "void foo(Base* base)\n" + "{\n" + " Derived * d = (Derived *) base;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" "void foo(Derived* derived)\n" "{\n" " Base * b = (Base *) derived;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("", errout_str()); // <- cast from derived to base is safe + + checkOldStylePointerCast("void foo(Base* base)\n" + "{\n" + " Derived * d = (Derived *) base;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" - "void foo(Derived* derived)\n" + "void foo(Base* base)\n" "{\n" - " Base * b = (const Base *) derived;\n" + " Derived * d = (const Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" "void foo()\n" "{\n" - " Base * b = (Base *) ( new Derived() );\n" + " Derived * d = (const Derived *) ( new Base() );\n" "}"); - ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" "void foo()\n" "{\n" - " Base * b = (Base *) new Derived();\n" + " Derived * d = (const Derived *) new Base();\n" "}"); - ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "void foo()\n" @@ -2071,12 +2085,12 @@ class TestOther : public TestFixture { // #5210 checkOldStylePointerCast("class Base {};\n" "class Derived: public Base {};\n" - "void f(Derived** d1, Derived*** d2) {\n" - " Base** p1 = (Base**)d1;\n" - " Base*** p2 = (Base***)d2;\n" + "void f(Base** b1, Base*** b2) {\n" + " Derived** p1 = (Derived**)b1;\n" + " Derived*** p2 = (Derived***)b2;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:17]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:5:18]: (style) C-style pointer casting [cstyleCast]\n", + ASSERT_EQUALS("[test.cpp:4:20]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:5:21]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #12446 @@ -2095,10 +2109,10 @@ class TestOther : public TestFixture { // #12447 checkOldStylePointerCast("class Base {};\n" "class Derived: public Base {};\n" - "void f(const Derived& derived) {\n" - " Base& b = (Base&)derived;\n" + "void f(const Base& base) {\n" + " d = (const Derived&)base;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:13]: (style) C-style reference casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:7]: (style) C-style reference casting [cstyleCast]\n", errout_str()); // #11430 checkOldStylePointerCast("struct B {\n" From 21cea1d4b6536d117db5bd5c90f38ee35f39100f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 14:14:14 +0200 Subject: [PATCH 16/25] split out intToPointer into a separate checker --- lib/checkother.cpp | 40 +++++++++++++++++++++++++++------------- lib/checkother.h | 3 +++ releasenotes.txt | 3 ++- test/testother.cpp | 36 +++++++++++++++++++++++++++++++++--- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 41cd1d5f93c..b03aa94ab7b 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -296,6 +296,10 @@ void CheckOther::suspiciousSemicolonError(const Token* tok) //--------------------------------------------------------------------------- void CheckOther::warningOldStylePointerCast() { + // Only valid on C++ code + if (!mTokenizer->isCPP()) + return; + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("cstyleCast")) return; @@ -333,19 +337,7 @@ void CheckOther::warningOldStylePointerCast() const bool refcast = (tok->valueType()->reference != Reference::None); if (!refcast && tok->valueType()->pointer == 0) continue; - if (!refcast && from->valueType()->pointer == 0) { - // casting non-zero integer literal (decimal/octal) to pointer is suspicious - if (mSettings->severity.isEnabled(Severity::portability) && - tok->valueType()->pointer > 0 && - from->isNumber() && - !MathLib::isIntHex(from->str()) && - from->getKnownIntValue() != 0) - intToPointerCastError(tok); - continue; - } - - // Use C++ cast: Only valid on C++ code - if (!mTokenizer->isCPP()) + if (!refcast && from->valueType()->pointer == 0) continue; if (tok->valueType()->type == ValueType::Type::VOID || from->valueType()->type == ValueType::Type::VOID) @@ -373,6 +365,27 @@ void CheckOther::cstyleCastError(const Token *tok, bool isPtr) "which kind of cast is expected.", CWE398, Certainty::normal); } +void CheckOther::warningIntToPointerCast() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("CheckOther::warningIntToPointerCast"); // portability + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { + // pointer casting.. + if (!tok->isCast()) + continue; + const Token* from = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); + if (!from || !from->isNumber()) + continue; + if (!tok->valueType() || tok->valueType()->pointer == 0) + continue; + if (!MathLib::isIntHex(from->str()) && from->getKnownIntValue() != 0) + intToPointerCastError(tok); + } +} void CheckOther::intToPointerCastError(const Token *tok) { @@ -4407,6 +4420,7 @@ void CheckOther::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) // Checks checkOther.warningOldStylePointerCast(); + checkOther.warningIntToPointerCast(); checkOther.suspiciousFloatingPointCast(); checkOther.invalidPointerCast(); checkOther.checkCharVariable(); diff --git a/lib/checkother.h b/lib/checkother.h index a94f301a862..320d1ac0490 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -80,6 +80,9 @@ class CPPCHECKLIB CheckOther : public Check { /** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast(); + /** @brief Casting non-hexadecimal integer literal to pointer */ + void warningIntToPointerCast(); + void suspiciousFloatingPointCast(); /** @brief Check for pointer casts to a type with an incompatible binary data representation */ diff --git a/releasenotes.txt b/releasenotes.txt index 28ed742ea86..71e4493c812 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -1,7 +1,7 @@ Release Notes for Cppcheck 2.18 New checks: -- +- Warn about cast from non-hexadecimal integer literal to pointer Improved checking: - @@ -25,4 +25,5 @@ Other: - CMake will now unconditionally use Boost.Containers if available. If CMake option `USE_BOOST` is specified it will now bail out when it is not found. - Fix checking a project that contains several project file entries for the same file. - Fixed --file-filter matching of looked up files in provided paths. +- Split up cstyleCast checker - diff --git a/test/testother.cpp b/test/testother.cpp index b271469592d..f380171ffff 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -109,6 +109,7 @@ class TestOther : public TestFixture { TEST_CASE(varScope43); TEST_CASE(oldStylePointerCast); + TEST_CASE(intToPointerCast); TEST_CASE(invalidPointerCast); TEST_CASE(passedByValue); @@ -1901,7 +1902,7 @@ class TestOther : public TestFixture { template void checkOldStylePointerCast_(const char* file, int line, const char (&code)[size], Standards::cppstd_t std = Standards::CPPLatest) { - const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::portability).cpp(std).build(); + const Settings settings = settingsBuilder().severity(Severity::style).cpp(std).build(); // Tokenize.. SimpleTokenizer tokenizerCpp(settings, *this); @@ -1982,9 +1983,9 @@ class TestOther : public TestFixture { // #3630 checkOldStylePointerCast("class SomeType{};\n" "class X : public Base {\n" - " X() : Base((SomeType*)7) {}\n" + " X() : Base((SomeType*)7) {}\n" // <- intToPointerCast "};"); - ASSERT_EQUALS("[test.cpp:3:16]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + ASSERT_EQUALS("", errout_str()); checkOldStylePointerCast("class SomeType{};\n" "class X : public Base {\n" @@ -2128,6 +2129,35 @@ class TestOther : public TestFixture { ASSERT_EQUALS("", errout_str()); } +#define checkIntToPointerCast(...) checkIntToPointerCast_(__FILE__, __LINE__, __VA_ARGS__) + template + void checkIntToPointerCast_(const char* file, int line, const char (&code)[size]) { + + const Settings settings = settingsBuilder().severity(Severity::portability).build(); + + // Tokenize.. + SimpleTokenizer tokenizerCpp(settings, *this); + ASSERT_LOC(tokenizerCpp.tokenize(code), file, line); + + CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); + checkOtherCpp.warningIntToPointerCast(); + } + + void intToPointerCast() { + // #3630 + checkIntToPointerCast("uint8_t* ptr = (uint8_t*)7;"); + ASSERT_EQUALS("[test.cpp:1:16]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + + checkIntToPointerCast("void* ptr = (void*)7;"); + ASSERT_EQUALS("[test.cpp:1:13]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + + checkIntToPointerCast("uint8_t* ptr = (uint8_t*)0;"); + ASSERT_EQUALS("", errout_str()); + + checkIntToPointerCast("uint8_t* ptr = (uint8_t*)0x7000;"); // <- it's common in embedded code to cast address + ASSERT_EQUALS("", errout_str()); + } + #define checkInvalidPointerCast(...) checkInvalidPointerCast_(__FILE__, __LINE__, __VA_ARGS__) template void checkInvalidPointerCast_(const char* file, int line, const char (&code)[size], bool portability = true, bool inconclusive = false) { From baced499aadfd24cfe2fe1cd23637122515ccf57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 16:05:56 +0200 Subject: [PATCH 17/25] restore cstyleCast, write dangerousOldStyleTypeCast for dangerous type casts --- lib/checkother.cpp | 135 +++++++++++++++++++++++++++++++--------- lib/checkother.h | 5 ++ test/cfg/googletest.cpp | 2 +- test/cfg/opencv2.cpp | 3 +- test/cfg/std.cpp | 14 ++--- test/cfg/windows.cpp | 6 +- test/testother.cpp | 62 +++++++++--------- 7 files changed, 159 insertions(+), 68 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index b03aa94ab7b..6b5ac26ffdf 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -290,6 +290,44 @@ void CheckOther::suspiciousSemicolonError(const Token* tok) "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal); } +/** @brief would it make sense to use dynamic_cast instead of C style cast? */ +static bool isDangerousTypeConversion(const Token* const tok) +{ + const Token* from = tok->astOperand1(); + if (!from) + return false; + if (!tok->valueType() || !from->valueType()) + return false; + if (tok->valueType()->typeScope != nullptr && + tok->valueType()->typeScope == from->valueType()->typeScope) + return false; + if (tok->valueType()->type == from->valueType()->type && + tok->valueType()->isPrimitive()) + return false; + // cast from derived object to base object is safe.. + if (tok->valueType()->typeScope && from->valueType()->typeScope) { + const Type* fromType = from->valueType()->typeScope->definedType; + const Type* toType = tok->valueType()->typeScope->definedType; + if (fromType && toType && fromType->isDerivedFrom(toType->name())) + return false; + } + const bool refcast = (tok->valueType()->reference != Reference::None); + if (!refcast && tok->valueType()->pointer == 0) + return false; + if (!refcast && from->valueType()->pointer == 0) + return false; + + if (tok->valueType()->type == ValueType::Type::VOID || from->valueType()->type == ValueType::Type::VOID) + return false; + if (tok->valueType()->pointer == 0 && tok->valueType()->isIntegral()) + // ok: (uintptr_t)ptr; + return false; + if (from->valueType()->pointer == 0 && from->valueType()->isIntegral()) + // ok: (int *)addr; + return false; + + return true; +} //--------------------------------------------------------------------------- // For C++ code, warn if C-style casts are used on pointer types @@ -316,40 +354,45 @@ void CheckOther::warningOldStylePointerCast() // Old style pointer casting.. if (!tok->isCast() || tok->isBinaryOp()) continue; - const Token* from = tok->astOperand1(); - if (!from) - continue; - if (!tok->valueType() || !from->valueType()) - continue; - if (tok->valueType()->typeScope != nullptr && - tok->valueType()->typeScope == from->valueType()->typeScope) + if (isDangerousTypeConversion(tok)) continue; - if (tok->valueType()->type == from->valueType()->type && - tok->valueType()->isPrimitive()) - continue; - // cast from derived object to base object is safe.. - if (tok->valueType()->typeScope && from->valueType()->typeScope) { - const Type* fromType = from->valueType()->typeScope->definedType; - const Type* toType = tok->valueType()->typeScope->definedType; - if (fromType && toType && fromType->isDerivedFrom(toType->name())) - continue; + const Token* const errtok = tok; + const Token* castTok = tok->next(); + while (Token::Match(castTok, "const|volatile|class|struct|union|%type%|::")) { + castTok = castTok->next(); + if (Token::simpleMatch(castTok, "<") && castTok->link()) + castTok = castTok->link()->next(); } - const bool refcast = (tok->valueType()->reference != Reference::None); - if (!refcast && tok->valueType()->pointer == 0) + if (castTok == tok->next()) continue; - if (!refcast && from->valueType()->pointer == 0) + bool isPtr = false, isRef = false; + while (Token::Match(castTok, "*|const|&")) { + if (castTok->str() == "*") + isPtr = true; + else if (castTok->str() == "&") + isRef = true; + castTok = castTok->next(); + } + if ((!isPtr && !isRef) || !Token::Match(castTok, ") (| %name%|%bool%|%char%|%str%|&")) continue; - if (tok->valueType()->type == ValueType::Type::VOID || from->valueType()->type == ValueType::Type::VOID) + if (Token::Match(tok->previous(), "%type%")) continue; - if (tok->valueType()->pointer == 0 && tok->valueType()->isIntegral()) - // ok: (uintptr_t)ptr; - continue; - if (from->valueType()->pointer == 0 && from->valueType()->isIntegral()) - // ok: (int *)addr; + + // skip first "const" in "const Type* const" + while (Token::Match(tok->next(), "const|volatile|class|struct|union")) + tok = tok->next(); + const Token* typeTok = tok->next(); + // skip second "const" in "const Type* const" + if (tok->strAt(3) == "const") + tok = tok->next(); + + const Token *p = tok->tokAt(4); + if (p->hasKnownIntValue() && p->getKnownIntValue()==0) // Casting nullpointers is safe continue; - cstyleCastError(tok, tok->valueType()->pointer > 0); + if (typeTok->tokType() == Token::eType || typeTok->tokType() == Token::eName) + cstyleCastError(errtok, isPtr); } } } @@ -365,14 +408,49 @@ void CheckOther::cstyleCastError(const Token *tok, bool isPtr) "which kind of cast is expected.", CWE398, Certainty::normal); } +void CheckOther::warningDangerousOldStyleTypeCast() +{ + // Only valid on C++ code + if (!mTokenizer->isCPP()) + return; + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("cstyleCast")) + return; + + logChecker("CheckOther::warningDangerousOldStyleTypeCast"); // warning,c++ + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + const Token* tok; + if (scope->function && scope->function->isConstructor()) + tok = scope->classDef; + else + tok = scope->bodyStart; + for (; tok && tok != scope->bodyEnd; tok = tok->next()) { + // Old style pointer casting.. + if (!tok->isCast() || tok->isBinaryOp()) + continue; + + if (isDangerousTypeConversion(tok)) + dangerousOldStyleTypeCastError(tok, tok->valueType()->pointer > 0); + } + } +} + +void CheckOther::dangerousOldStyleTypeCastError(const Token *tok, bool isPtr) +{ + const std::string type = isPtr ? "pointer" : "reference"; + reportError(tok, Severity::warning, "dangerousOldStyleTypeCast", + "Potentially dangerous C style type cast of " + type + " to object, use dynamic_cast or static_cast", + CWE398, Certainty::normal); +} + void CheckOther::warningIntToPointerCast() { - if (!mSettings->severity.isEnabled(Severity::portability)) + if (!mSettings->severity.isEnabled(Severity::portability) && !mSettings->isPremiumEnabled("cstyleCast")) return; logChecker("CheckOther::warningIntToPointerCast"); // portability - const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { // pointer casting.. if (!tok->isCast()) @@ -4487,6 +4565,7 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); + c.dangerousOldStyleTypeCastError(nullptr, true); c.intToPointerCastError(nullptr); c.suspiciousFloatingPointCastError(nullptr); c.passedByValueError(nullptr, false); diff --git a/lib/checkother.h b/lib/checkother.h index 320d1ac0490..405b6624c9f 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -80,6 +80,9 @@ class CPPCHECKLIB CheckOther : public Check { /** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast(); + /** @brief Dangerous type cast */ + void warningDangerousOldStyleTypeCast(); + /** @brief Casting non-hexadecimal integer literal to pointer */ void warningIntToPointerCast(); @@ -201,6 +204,7 @@ class CPPCHECKLIB CheckOther : public Check { void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok, bool isPtr = true); + void dangerousOldStyleTypeCastError(const Token *tok, bool isPtr); void intToPointerCastError(const Token *tok); void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); @@ -277,6 +281,7 @@ class CPPCHECKLIB CheckOther : public Check { // warning "- either division by zero or useless condition\n" "- access of moved or forwarded variable.\n" + "- potentially dangerous C style type cast of pointer/reference to object.\n" // performance "- redundant data copying for const variable\n" diff --git a/test/cfg/googletest.cpp b/test/cfg/googletest.cpp index 66a2c7f9b9b..eaec730136d 100644 --- a/test/cfg/googletest.cpp +++ b/test/cfg/googletest.cpp @@ -44,7 +44,7 @@ namespace ExampleNamespace { TEST(ASSERT, ASSERT) { - int *a = (int*)calloc(10,sizeof(int)); + int *a = (int*)calloc(10,sizeof(int)); // cppcheck-suppress cstyleCast ASSERT_TRUE(a != nullptr); a[0] = 10; diff --git a/test/cfg/opencv2.cpp b/test/cfg/opencv2.cpp index a65b1f2cdb3..6e79caab8d0 100644 --- a/test/cfg/opencv2.cpp +++ b/test/cfg/opencv2.cpp @@ -30,7 +30,7 @@ void validCode(const char* argStr) cvStr += " World"; std::cout << cvStr; - // cppcheck-suppress unusedAllocatedMemory + // cppcheck-suppress [cstyleCast, unusedAllocatedMemory] char * pBuf = (char *)cv::fastMalloc(20); cv::fastFree(pBuf); } @@ -43,6 +43,7 @@ void ignoredReturnValue() void memleak() { + // cppcheck-suppress cstyleCast const char * pBuf = (char *)cv::fastMalloc(1000); // cppcheck-suppress [uninitdata, valueFlowBailoutIncompleteVar, nullPointerOutOfMemory] std::cout << pBuf; diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 4dbd0d844f0..04216867f00 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -3027,7 +3027,7 @@ void uninitvar_longjmp(void) void uninitvar_malloc(void) { size_t size; - // cppcheck-suppress [uninitvar, unusedAllocatedMemory] + // cppcheck-suppress [uninitvar, cstyleCast, unusedAllocatedMemory] int *p = (int*)std::malloc(size); free(p); } @@ -3259,7 +3259,7 @@ void uninivar_bsearch(void) const void* base; size_t num; size_t size; - // cppcheck-suppress uninitvar + // cppcheck-suppress [uninitvar, cstyleCast] (void)std::bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); } @@ -3269,11 +3269,11 @@ void minsize_bsearch(const void* key, const void* base, { const int Base[3] = {42, 43, 44}; - (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); - (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); - (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); + (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast - (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); + (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast } void uninitvar_qsort(void) @@ -3282,7 +3282,7 @@ void uninitvar_qsort(void) size_t n; size_t size; // cppcheck-suppress uninitvar - (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); + (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast } void uninitvar_stable_sort(std::vector& v) diff --git a/test/cfg/windows.cpp b/test/cfg/windows.cpp index fa9b24f270d..c43bbfeb747 100644 --- a/test/cfg/windows.cpp +++ b/test/cfg/windows.cpp @@ -624,7 +624,7 @@ void memleak_HeapAlloc() void memleak_LocalAlloc() { LPTSTR pszBuf; - // cppcheck-suppress [LocalAllocCalled, valueFlowBailoutIncompleteVar] + // cppcheck-suppress [LocalAllocCalled, cstyleCast, valueFlowBailoutIncompleteVar] pszBuf = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*sizeof(TCHAR)); (void)LocalSize(pszBuf); (void)LocalFlags(pszBuf); @@ -700,7 +700,7 @@ void resourceLeak_LoadLibrary() HINSTANCE hInstLib; hInstLib = ::LoadLibrary(L"My.dll"); typedef BOOL (WINAPI *fpFunc)(); - // cppcheck-suppress unreadVariable + // cppcheck-suppress [unreadVariable, cstyleCast] fpFunc pFunc = (fpFunc)GetProcAddress(hInstLib, "name"); // cppcheck-suppress resourceLeak } @@ -825,7 +825,7 @@ void invalidFunctionArg() CloseHandle(hMutex); //Incorrect: 2. parameter to LoadLibraryEx() must be NULL - // cppcheck-suppress [invalidFunctionArg, intToPointerCast] + // cppcheck-suppress [invalidFunctionArg, cstyleCast] HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", HANDLE(1), 0); FreeLibrary(hInstLib); diff --git a/test/testother.cpp b/test/testother.cpp index f380171ffff..c30aa81cf79 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1902,7 +1902,7 @@ class TestOther : public TestFixture { template void checkOldStylePointerCast_(const char* file, int line, const char (&code)[size], Standards::cppstd_t std = Standards::CPPLatest) { - const Settings settings = settingsBuilder().severity(Severity::style).cpp(std).build(); + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).cpp(std).build(); // Tokenize.. SimpleTokenizer tokenizerCpp(settings, *this); @@ -1910,6 +1910,7 @@ class TestOther : public TestFixture { CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); checkOtherCpp.warningOldStylePointerCast(); + checkOtherCpp.warningDangerousOldStyleTypeCast(); } void oldStylePointerCast() { @@ -1919,21 +1920,21 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" "void foo(Derived* derived)\n" "{\n" - " Base * b = (Base *) derived;\n" + " Base * b = (Base *) derived;\n" // <- cast from derived to base is safe => cstyleCast "}"); - ASSERT_EQUALS("", errout_str()); // <- cast from derived to base is safe + ASSERT_EQUALS("[test.cpp:5:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("void foo(Base* base)\n" "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:3:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1941,7 +1942,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1949,7 +1950,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) ( new Base() );\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1957,14 +1958,14 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) new Base();\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "void foo()\n" "{\n" " Base * b = (Base *) new short[10];\n" "}"); - ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); checkOldStylePointerCast("class B;\n" "class A\n" @@ -1997,7 +1998,7 @@ class TestOther : public TestFixture { "class X : public Base {\n" " X() : Base((SomeType*)var) {}\n" "};"); - ASSERT_EQUALS("", errout_str()); + ASSERT_EQUALS("[test.cpp:3:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" @@ -2022,7 +2023,8 @@ class TestOther : public TestFixture { " std::vector v;\n" " v.push_back((Base*)new Derived);\n" "}"); - // FIXME ASSERT_EQUALS("[test.cpp:5:15]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + // FIXME write a dangerousTypeCast warning instead + ASSERT_EQUALS("[test.cpp:5:15]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #7709 checkOldStylePointerCast("typedef struct S S;\n" @@ -2045,16 +2047,16 @@ class TestOther : public TestFixture { " TT* tt = (TT*)i;\n" " TT2* tt2 = (TT2*)i;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:12]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:11:14]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:12:21]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:13:12]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:14:20]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:15:15]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:16:16]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:17:16]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:18:14]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:19:16]: (style) C-style pointer casting [cstyleCast]\n", + ASSERT_EQUALS("[test.cpp:10:12]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:11:14]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:12:21]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:13:12]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:14:20]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:15:15]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:16:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:17:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:18:14]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:19:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); // #8649 @@ -2064,7 +2066,8 @@ class TestOther : public TestFixture { " g((S*&)i);\n" " S*& r = (S*&)i;\n" "}\n"); - ASSERT_EQUALS("", + ASSERT_EQUALS("[test.cpp:4:7]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:5:13]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); checkOldStylePointerCast("struct S {};\n" @@ -2075,7 +2078,8 @@ class TestOther : public TestFixture { "}\n"); // TODO: these conversions are dangerous, but it's a different issue not covered by cstyleCast. A separate checker can be added which is executed for both C and C++ code. // clang says: 1.cpp:5:18: warning: cast to 'unsigned char *' from smaller integer type 'uint8_t' (aka 'unsigned char') [-Wint-to-pointer-cast] - TODO_ASSERT_EQUALS("bad cast from uint8_t to pointer", "", errout_str()); + ASSERT_EQUALS("[test.cpp:4:7]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:5:13]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #10823 checkOldStylePointerCast("void f(void* p) {\n" @@ -2090,8 +2094,8 @@ class TestOther : public TestFixture { " Derived** p1 = (Derived**)b1;\n" " Derived*** p2 = (Derived***)b2;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:20]: (style) C-style pointer casting [cstyleCast]\n" - "[test.cpp:5:21]: (style) C-style pointer casting [cstyleCast]\n", + ASSERT_EQUALS("[test.cpp:4:20]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" + "[test.cpp:5:21]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); // #12446 @@ -2105,7 +2109,9 @@ class TestOther : public TestFixture { " auto pu = (union U*)p;\n" " auto pv = (std::vector*)(p);\n" "}\n"); - ASSERT_EQUALS("", errout_str()); // There are other checkers and compiler warnings that warn about all C style casts + ASSERT_EQUALS("[test.cpp:7:15]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:8:15]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:9:15]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); // #12447 checkOldStylePointerCast("class Base {};\n" @@ -2113,7 +2119,7 @@ class TestOther : public TestFixture { "void f(const Base& base) {\n" " d = (const Derived&)base;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:7]: (style) C-style reference casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:7]: (warning) Potentially dangerous C style type cast of reference to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); // #11430 checkOldStylePointerCast("struct B {\n" @@ -2126,7 +2132,7 @@ class TestOther : public TestFixture { " using float_ptr = float*;\n" " return N::f(float_ptr(b.data()));\n" // <- the cast is safe "}\n"); - ASSERT_EQUALS("", errout_str()); + ASSERT_EQUALS("[test.cpp:9:17]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); } #define checkIntToPointerCast(...) checkIntToPointerCast_(__FILE__, __LINE__, __VA_ARGS__) From 756426d8f67a8245852c2bbe2257a66fc9dc9a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 16:16:32 +0200 Subject: [PATCH 18/25] tweak --- lib/checkother.cpp | 10 +-- lib/checkother.h | 2 +- .../{cstyleCast.md => dangerousTypeCast.md} | 0 test/testother.cpp | 87 +++++++++++++++---- 4 files changed, 74 insertions(+), 25 deletions(-) rename man/checkers/{cstyleCast.md => dangerousTypeCast.md} (100%) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 6b5ac26ffdf..3ae0a01c05a 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -431,16 +431,16 @@ void CheckOther::warningDangerousOldStyleTypeCast() continue; if (isDangerousTypeConversion(tok)) - dangerousOldStyleTypeCastError(tok, tok->valueType()->pointer > 0); + dangerousTypeCastError(tok, tok->valueType()->pointer > 0); } } } -void CheckOther::dangerousOldStyleTypeCastError(const Token *tok, bool isPtr) +void CheckOther::dangerousTypeCastError(const Token *tok, bool isPtr) { const std::string type = isPtr ? "pointer" : "reference"; - reportError(tok, Severity::warning, "dangerousOldStyleTypeCast", - "Potentially dangerous C style type cast of " + type + " to object, use dynamic_cast or static_cast", + reportError(tok, Severity::warning, "dangerousTypeCast", + "Potentially dangerous C style type cast of " + type + " to object", CWE398, Certainty::normal); } @@ -4565,7 +4565,7 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); - c.dangerousOldStyleTypeCastError(nullptr, true); + c.dangerousTypeCastError(nullptr, true); c.intToPointerCastError(nullptr); c.suspiciousFloatingPointCastError(nullptr); c.passedByValueError(nullptr, false); diff --git a/lib/checkother.h b/lib/checkother.h index 405b6624c9f..379c84f9471 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -204,7 +204,7 @@ class CPPCHECKLIB CheckOther : public Check { void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok, bool isPtr = true); - void dangerousOldStyleTypeCastError(const Token *tok, bool isPtr); + void dangerousTypeCastError(const Token *tok, bool isPtr); void intToPointerCastError(const Token *tok); void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); diff --git a/man/checkers/cstyleCast.md b/man/checkers/dangerousTypeCast.md similarity index 100% rename from man/checkers/cstyleCast.md rename to man/checkers/dangerousTypeCast.md diff --git a/test/testother.cpp b/test/testother.cpp index c30aa81cf79..c4703450638 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1920,7 +1920,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1934,7 +1934,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1942,7 +1942,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1950,7 +1950,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) ( new Base() );\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1958,14 +1958,63 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) new Base();\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "void foo()\n" "{\n" " Base * b = (Base *) new short[10];\n" "}"); - ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (volatile Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (volatile Base * const) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const volatile Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const volatile Base * const) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) ( new Derived() );\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) new Derived();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) new short[10];\n" + "}"); + ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class B;\n" "class A\n" @@ -2047,16 +2096,16 @@ class TestOther : public TestFixture { " TT* tt = (TT*)i;\n" " TT2* tt2 = (TT2*)i;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:12]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:11:14]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:12:21]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:13:12]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:14:20]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:15:15]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:16:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:17:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:18:14]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:19:16]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", + ASSERT_EQUALS("[test.cpp:10:12]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:11:14]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:12:21]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:13:12]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:14:20]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:15:15]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:16:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:17:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:18:14]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:19:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); // #8649 @@ -2094,8 +2143,8 @@ class TestOther : public TestFixture { " Derived** p1 = (Derived**)b1;\n" " Derived*** p2 = (Derived***)b2;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:20]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n" - "[test.cpp:5:21]: (warning) Potentially dangerous C style type cast of pointer to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", + ASSERT_EQUALS("[test.cpp:4:20]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" + "[test.cpp:5:21]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); // #12446 @@ -2119,7 +2168,7 @@ class TestOther : public TestFixture { "void f(const Base& base) {\n" " d = (const Derived&)base;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:7]: (warning) Potentially dangerous C style type cast of reference to object, use dynamic_cast or static_cast [dangerousOldStyleTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:7]: (warning) Potentially dangerous C style type cast of reference to object [dangerousTypeCast]\n", errout_str()); // #11430 checkOldStylePointerCast("struct B {\n" From 23ec0b71bc0eb3c51d74a05bb89bb8fa958439e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 16:24:38 +0200 Subject: [PATCH 19/25] tweak documentation --- man/checkers/dangerousTypeCast.md | 50 ++++--------------------------- 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/man/checkers/dangerousTypeCast.md b/man/checkers/dangerousTypeCast.md index 0f64ef38e56..3906b6e1e62 100644 --- a/man/checkers/dangerousTypeCast.md +++ b/man/checkers/dangerousTypeCast.md @@ -1,9 +1,9 @@ -# cstyleCast +# dangerousTypeCast -**Message**: C-style pointer casting
+**Message**: Potentially dangerous C style type cast of pointer to object
**Category**: Type Safety
-**Severity**: Style
+**Severity**: Warning
**Language**: C++, not applicable for C code ## Motivation @@ -16,58 +16,20 @@ C style casts can be dangerous in many ways: ## Philosophy -This checker warns about old style C casts that can be dangerous. +This checker warns about old style C casts that perform type conversions that can be invalid. This checker is not about readability. It is about safety. -This checker focus on invalid type conversions: -If there is loss of sign or precision in the cast; it's hard to know for Cppcheck if that is intentional. -If there is loss of constness; there are other checkers available and these apply to C code also. -Therefore we only warn about pointer/reference casts that could be invalid type conversions. - -## Other tools / checkers - -Extended checking; if you want more warnings that warn about all C style casts: - * Misra C++ 8.2.2, cover all C style casts. - * C style cast compiler warnings (i.e. clang reports -Wold-style-cast) - -Complementing; checking for loss of constness etc: - * Misra C 11.8 - * Loss of constness compiler warnings (i.e. clang reports -Wcast-qual). - -## Examples - -### Compliant C style cast - -The goal is to not warn about "safe" and/or "intentional" casts. - -For example: -```cpp -int *p = (int*)0; -``` -A static_cast can be used however technically it's the same as the C style cast. - -### Non compliant C style cast - -A cast from a base class pointer to a derived class pointer is potentially unsafe: -```cpp -struct Base{}; -struct Derived: public Base {}; -void foo(Base* base) { - Derived *p = (Derived*)base; // <- dangerous -} -``` - ## How to fix -You can use `dynamic_cast` or `static_cast` to fix warnings. +You can use `dynamic_cast` or `static_cast` to fix these warnings. Before: ```cpp struct Base{}; struct Derived: public Base {}; void foo(Base* base) { - Derived *p = (Derived*)base; // <- dangerous + Derived *p = (Derived*)base; // <- can be invalid } ``` From 6a082e05f8863d4c1dbc90d387bf68a800fc1478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 19:31:32 +0200 Subject: [PATCH 20/25] tweak --- lib/checkother.cpp | 25 ++++++++---- lib/checkother.h | 4 +- man/checkers/dangerousTypeCast.md | 2 +- test/testgarbage.cpp | 1 + test/testother.cpp | 63 ++++++++++++++++--------------- 5 files changed, 53 insertions(+), 42 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 3ae0a01c05a..29bd732a1ec 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -408,7 +408,7 @@ void CheckOther::cstyleCastError(const Token *tok, bool isPtr) "which kind of cast is expected.", CWE398, Certainty::normal); } -void CheckOther::warningDangerousOldStyleTypeCast() +void CheckOther::warningDangerousTypeCast() { // Only valid on C++ code if (!mTokenizer->isCPP()) @@ -416,7 +416,7 @@ void CheckOther::warningDangerousOldStyleTypeCast() if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("cstyleCast")) return; - logChecker("CheckOther::warningDangerousOldStyleTypeCast"); // warning,c++ + logChecker("CheckOther::warningDangerousTypeCast"); // warning,c++ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -440,7 +440,7 @@ void CheckOther::dangerousTypeCastError(const Token *tok, bool isPtr) { const std::string type = isPtr ? "pointer" : "reference"; reportError(tok, Severity::warning, "dangerousTypeCast", - "Potentially dangerous C style type cast of " + type + " to object", + "Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast", CWE398, Certainty::normal); } @@ -460,15 +460,23 @@ void CheckOther::warningIntToPointerCast() continue; if (!tok->valueType() || tok->valueType()->pointer == 0) continue; - if (!MathLib::isIntHex(from->str()) && from->getKnownIntValue() != 0) - intToPointerCastError(tok); + if (!MathLib::isIntHex(from->str()) && from->getKnownIntValue() != 0) { + std::string format; + if (MathLib::isDec(from->str())) + format = "decimal"; + else if (MathLib::isOct(from->str())) + format = "octal"; + else + continue; + intToPointerCastError(tok, format); + } } } -void CheckOther::intToPointerCastError(const Token *tok) +void CheckOther::intToPointerCastError(const Token *tok, const std::string& format) { reportError(tok, Severity::portability, "intToPointerCast", - "Casting non-zero integer literal in decimal or octal format to pointer.", + "Casting non-zero " + format + " integer literal to pointer.", CWE398, Certainty::normal); } @@ -4498,6 +4506,7 @@ void CheckOther::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) // Checks checkOther.warningOldStylePointerCast(); + checkOther.warningDangerousTypeCast(); checkOther.warningIntToPointerCast(); checkOther.suspiciousFloatingPointCast(); checkOther.invalidPointerCast(); @@ -4566,7 +4575,7 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); c.dangerousTypeCastError(nullptr, true); - c.intToPointerCastError(nullptr); + c.intToPointerCastError(nullptr, "decimal"); c.suspiciousFloatingPointCastError(nullptr); c.passedByValueError(nullptr, false); c.constVariableError(nullptr, nullptr); diff --git a/lib/checkother.h b/lib/checkother.h index 379c84f9471..db907785e45 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -81,7 +81,7 @@ class CPPCHECKLIB CheckOther : public Check { void warningOldStylePointerCast(); /** @brief Dangerous type cast */ - void warningDangerousOldStyleTypeCast(); + void warningDangerousTypeCast(); /** @brief Casting non-hexadecimal integer literal to pointer */ void warningIntToPointerCast(); @@ -205,7 +205,7 @@ class CPPCHECKLIB CheckOther : public Check { void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok, bool isPtr = true); void dangerousTypeCastError(const Token *tok, bool isPtr); - void intToPointerCastError(const Token *tok); + void intToPointerCastError(const Token *tok, const std::string& format); void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false); diff --git a/man/checkers/dangerousTypeCast.md b/man/checkers/dangerousTypeCast.md index 3906b6e1e62..79538103db5 100644 --- a/man/checkers/dangerousTypeCast.md +++ b/man/checkers/dangerousTypeCast.md @@ -1,7 +1,7 @@ # dangerousTypeCast -**Message**: Potentially dangerous C style type cast of pointer to object
+**Message**: Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast
**Category**: Type Safety
**Severity**: Warning
**Language**: C++, not applicable for C code diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 53d0a747a03..321d922f2f4 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1832,6 +1832,7 @@ class TestGarbage : public TestFixture { ASSERT_THROW_INTERNAL(checkCode("void f(){x=0,return return''[]()}"), SYNTAX); ASSERT_THROW_INTERNAL(checkCode("void f(){x='0'++'0'(return)[];}"), SYNTAX); // #9063 (void)checkCode("void f(){*(int *)42=0;}"); // no syntax error + ignore_errout(); // we are not interested in the output ASSERT_THROW_INTERNAL(checkCode("void f() { x= 'x' > typedef name5 | ( , ;){ } (); }"), SYNTAX); // #9067 ASSERT_THROW_INTERNAL(checkCode("void f() { x= {}( ) ( 'x')[ ] (); }"), SYNTAX); // #9068 ASSERT_THROW_INTERNAL(checkCode("void f() { x= y{ } name5 y[ ] + y ^ name5 ^ name5 for ( ( y y y && y y y && name5 ++ int )); }"), SYNTAX); // #9069 diff --git a/test/testother.cpp b/test/testother.cpp index c4703450638..c126675a84b 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1910,7 +1910,7 @@ class TestOther : public TestFixture { CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); checkOtherCpp.warningOldStylePointerCast(); - checkOtherCpp.warningDangerousOldStyleTypeCast(); + checkOtherCpp.warningDangerousTypeCast(); } void oldStylePointerCast() { @@ -1920,7 +1920,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1934,7 +1934,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1942,7 +1942,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) base;\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1950,7 +1950,7 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) ( new Base() );\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "class Derived: public Base {};\n" @@ -1958,14 +1958,14 @@ class TestOther : public TestFixture { "{\n" " Derived * d = (const Derived *) new Base();\n" "}"); - ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:5:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base{};\n" "void foo()\n" "{\n" " Base * b = (Base *) new short[10];\n" "}"); - ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" @@ -2014,7 +2014,7 @@ class TestOther : public TestFixture { "{\n" " Base * b = (const Base *) new short[10];\n" "}"); - ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:16]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); checkOldStylePointerCast("class B;\n" "class A\n" @@ -2096,16 +2096,16 @@ class TestOther : public TestFixture { " TT* tt = (TT*)i;\n" " TT2* tt2 = (TT2*)i;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:12]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:11:14]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:12:21]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:13:12]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:14:20]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:15:15]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:16:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:17:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:18:14]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:19:16]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", + ASSERT_EQUALS("[test.cpp:10:12]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:11:14]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:12:21]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:13:12]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:14:20]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:15:15]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:16:16]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:17:16]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:18:14]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:19:16]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); // #8649 @@ -2143,8 +2143,8 @@ class TestOther : public TestFixture { " Derived** p1 = (Derived**)b1;\n" " Derived*** p2 = (Derived***)b2;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:20]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n" - "[test.cpp:5:21]: (warning) Potentially dangerous C style type cast of pointer to object [dangerousTypeCast]\n", + ASSERT_EQUALS("[test.cpp:4:20]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" + "[test.cpp:5:21]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); // #12446 @@ -2168,7 +2168,7 @@ class TestOther : public TestFixture { "void f(const Base& base) {\n" " d = (const Derived&)base;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4:7]: (warning) Potentially dangerous C style type cast of reference to object [dangerousTypeCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:7]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); // #11430 checkOldStylePointerCast("struct B {\n" @@ -2201,10 +2201,10 @@ class TestOther : public TestFixture { void intToPointerCast() { // #3630 checkIntToPointerCast("uint8_t* ptr = (uint8_t*)7;"); - ASSERT_EQUALS("[test.cpp:1:16]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:1:16]: (portability) Casting non-zero decimal integer literal to pointer. [intToPointerCast]\n", errout_str()); checkIntToPointerCast("void* ptr = (void*)7;"); - ASSERT_EQUALS("[test.cpp:1:13]: (portability) Casting non-zero integer literal in decimal or octal format to pointer. [intToPointerCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:1:13]: (portability) Casting non-zero decimal integer literal to pointer. [intToPointerCast]\n", errout_str()); checkIntToPointerCast("uint8_t* ptr = (uint8_t*)0;"); ASSERT_EQUALS("", errout_str()); @@ -3196,7 +3196,7 @@ class TestOther : public TestFixture { " x.dostuff();\n" " const U& y = (const U&)(x);\n" "}"); - ASSERT_EQUALS("[test.cpp:4:18]: (style) C-style reference casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:4:18]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" "[test.cpp:2:11]: (style) Parameter 'x' can be declared as reference to const [constParameterReference]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" @@ -3205,13 +3205,13 @@ class TestOther : public TestFixture { " U& y = (U&)(x);\n" " y.mutate();\n" // to avoid warnings that y can be const "}"); - ASSERT_EQUALS("[test.cpp:4:12]: (style) C-style reference casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:12]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = (typename const U&)(x);\n" "}"); - ASSERT_EQUALS("[test.cpp:4:18]: (style) C-style reference casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:4:18]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" "[test.cpp:2:11]: (style) Parameter 'x' can be declared as reference to const [constParameterReference]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" @@ -3220,14 +3220,14 @@ class TestOther : public TestFixture { " U& y = (typename U&)(x);\n" " y.mutate();\n" // to avoid warnings that y can be const "}"); - ASSERT_EQUALS("[test.cpp:4:12]: (style) C-style reference casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:12]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U* y = (U*)(&x);\n" " y->mutate();\n" // to avoid warnings that y can be const "}"); - ASSERT_EQUALS("[test.cpp:4:12]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4:12]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); check("struct C { void f() const; };\n" // #9875 - crash "\n" @@ -3672,7 +3672,7 @@ class TestOther : public TestFixture { "void g(A* a) {\n" " const B* b = (const B*)a;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:10:18]: (style) C-style pointer casting [cstyleCast]\n" + ASSERT_EQUALS("[test.cpp:10:18]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n" "[test.cpp:6:11]: (style) Parameter 'a' can be declared as pointer to const [constParameterPointer]\n" "[test.cpp:9:11]: (style) Parameter 'a' can be declared as pointer to const [constParameterPointer]\n", errout_str()); @@ -4438,7 +4438,8 @@ class TestOther : public TestFixture { "void f(T* t) {\n" " S* s = (S*)t->p;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3:8]: (style) Variable 's' can be declared as pointer to const [constVariablePointer]\n", + ASSERT_EQUALS("[test.cpp:3:12]: (style) C-style pointer casting [cstyleCast]\n" + "[test.cpp:3:8]: (style) Variable 's' can be declared as pointer to const [constVariablePointer]\n", errout_str()); // don't crash check("struct S { int i; };\n" // #12205 @@ -12743,7 +12744,7 @@ class TestOther : public TestFixture { "int f(S s) {\n" " return &s.i - (int*)&s;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3:19]: (style) C-style pointer casting [cstyleCast]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:3:19]: (warning) Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast [dangerousTypeCast]\n", errout_str()); check("struct S { int i; };\n" "int f(S s1, S s2) {\n" From 6aafa3e523914b485c9771f7e28ca8a52d343981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 20:46:29 +0200 Subject: [PATCH 21/25] checkcfg --- test/cfg/windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cfg/windows.cpp b/test/cfg/windows.cpp index c43bbfeb747..5951cb74c07 100644 --- a/test/cfg/windows.cpp +++ b/test/cfg/windows.cpp @@ -825,7 +825,7 @@ void invalidFunctionArg() CloseHandle(hMutex); //Incorrect: 2. parameter to LoadLibraryEx() must be NULL - // cppcheck-suppress [invalidFunctionArg, cstyleCast] + // cppcheck-suppress [invalidFunctionArg, intToPointerCast] HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", HANDLE(1), 0); FreeLibrary(hInstLib); From 365c47dd4ff732e9cebd11691b0e2f7d95e781c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 21 May 2025 21:14:42 +0200 Subject: [PATCH 22/25] clang-tidy --- lib/checkother.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 29bd732a1ec..123a9fce2ee 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -438,7 +438,8 @@ void CheckOther::warningDangerousTypeCast() void CheckOther::dangerousTypeCastError(const Token *tok, bool isPtr) { - const std::string type = isPtr ? "pointer" : "reference"; + //const std::string type = isPtr ? "pointer" : "reference"; + (void)isPtr; reportError(tok, Severity::warning, "dangerousTypeCast", "Potentially invalid type conversion in old-style C cast, clarify/fix with C++ cast", CWE398, Certainty::normal); From 8b93b8acb8cda6a615eaf11c1e561fb19dcc64df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 22 May 2025 09:04:31 +0200 Subject: [PATCH 23/25] add rewritten cstyleCast.md document [ci skip] --- man/checkers/cstyleCast.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 man/checkers/cstyleCast.md diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md new file mode 100644 index 00000000000..8b6ea882ff6 --- /dev/null +++ b/man/checkers/cstyleCast.md @@ -0,0 +1,37 @@ + +# cstyleCast + +**Message**: C-style pointer casting
+**Category**: Modernization
+**Severity**: Style
+**Language**: C++, not applicable for C code + +## Description + +Many developers feel that it's best practice to replace C casts with C++ casts. + +This checker is about C casts that converts to/from a pointer or reference. + +Potential safety issues are covered by other warnings so this ID `cstyleCast` is primarily about writing warnings for casts that are safe. + +## How to fix + +You can use C++ casts such as `static_cast` to fix these warnings. + +Before: +```cpp +struct Base{}; +struct Derived: public Base {}; +void foo(Base* base) { + Base *p = (Base*)derived; // <- cstyleCast, safe cast from derived object to base object +} +``` + +After: +```cpp +struct Base{}; +struct Derived: public Base {}; +void foo(Base* base) { + Derived *p = static_cast(base); +} +``` From 1241ddb0782e7e231c801dc6fb4d4b58fe93df11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 22 May 2025 09:26:11 +0200 Subject: [PATCH 24/25] update cstyleCast [ci skip] --- man/checkers/cstyleCast.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/man/checkers/cstyleCast.md b/man/checkers/cstyleCast.md index 8b6ea882ff6..8bf8d0724d9 100644 --- a/man/checkers/cstyleCast.md +++ b/man/checkers/cstyleCast.md @@ -8,22 +8,34 @@ ## Description -Many developers feel that it's best practice to replace C casts with C++ casts. +Many developers feel that it's best to replace C casts with C++ casts. + +There are several advantages with C++ casts: + * they permit only certain conversions + * they express the intent + * they are easy to identify This checker is about C casts that converts to/from a pointer or reference. -Potential safety issues are covered by other warnings so this ID `cstyleCast` is primarily about writing warnings for casts that are safe. +**Note:** More "noisy" warnings exists that warn about all C casts. For instance Clang has +`-Wold-style-cast` and there is also such checking in Misra C++. + +Dangerous conversions are covered by other warnings so this ID `cstyleCast` is primarily about +writing warnings for casts that are currently safe. ## How to fix You can use C++ casts such as `static_cast` to fix these warnings. +The `dynamic_cast` should rarelly be used to fix these warnings because dangerousTypeCast is +reported when that can be a good idea. + Before: ```cpp struct Base{}; struct Derived: public Base {}; void foo(Base* base) { - Base *p = (Base*)derived; // <- cstyleCast, safe cast from derived object to base object + Base *p = (Base*)derived; // <- cstyleCast, cast from derived object to base object is safe now } ``` @@ -35,3 +47,4 @@ void foo(Base* base) { Derived *p = static_cast(base); } ``` +The `static_cast` ensures that there will not be loss of constness in the future. From ce6381a42894c950beeda0d62517b549e58ba056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 23 May 2025 11:20:32 +0200 Subject: [PATCH 25/25] releasenotes [ci skip] --- releasenotes.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes.txt b/releasenotes.txt index 71e4493c812..792d2dcbc8c 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -1,7 +1,7 @@ Release Notes for Cppcheck 2.18 New checks: -- Warn about cast from non-hexadecimal integer literal to pointer +- Improved checking: - @@ -25,5 +25,5 @@ Other: - CMake will now unconditionally use Boost.Containers if available. If CMake option `USE_BOOST` is specified it will now bail out when it is not found. - Fix checking a project that contains several project file entries for the same file. - Fixed --file-filter matching of looked up files in provided paths. -- Split up cstyleCast checker +- Split up cstyleCast checker; dangerous casts produce portability/warning reports, safe casts produce style reports. -