GitHub Viewer
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2026 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "checkfunctions.h"
#include "errortypes.h"
#include "fixture.h"
#include "helpers.h"
#include "platform.h"
#include "settings.h"
#include "standards.h"
#include
#include
class TestFunctions : public TestFixture {
public:
TestFunctions() : TestFixture("TestFunctions") {}
private:
const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::warning).severity(Severity::performance).severity(Severity::portability).
certainty(Certainty::inconclusive).c(Standards::C11).cpp(Standards::CPP11).library("std.cfg").library("posix.cfg").build();
void run() override {
mNewTemplate = true;
// Prohibited functions
TEST_CASE(prohibitedFunctions_posix);
TEST_CASE(prohibitedFunctions_index);
TEST_CASE(prohibitedFunctions_qt_index); // FP when using the Qt function 'index'?
TEST_CASE(prohibitedFunctions_rindex);
TEST_CASE(prohibitedFunctions_var); // no false positives for variables
TEST_CASE(prohibitedFunctions_gets); // dangerous function
TEST_CASE(prohibitedFunctions_alloca);
TEST_CASE(prohibitedFunctions_declaredFunction); // declared function ticket #3121
TEST_CASE(prohibitedFunctions_std_gets); // test std::gets
TEST_CASE(prohibitedFunctions_multiple); // multiple use of obsolete functions
TEST_CASE(prohibitedFunctions_c_declaration); // c declared function
TEST_CASE(prohibitedFunctions_functionWithBody); // function with body
TEST_CASE(prohibitedFunctions_crypt); // Non-reentrant function
TEST_CASE(prohibitedFunctions_namespaceHandling);
// Invalid function usage
TEST_CASE(invalidFunctionUsage1);
TEST_CASE(invalidFunctionUsageStrings);
// Invalid function argument
TEST_CASE(invalidFunctionArg1);
// Math function usage
TEST_CASE(mathfunctionCall_fmod);
TEST_CASE(mathfunctionCall_sqrt);
TEST_CASE(mathfunctionCall_log);
TEST_CASE(mathfunctionCall_acos);
TEST_CASE(mathfunctionCall_asin);
TEST_CASE(mathfunctionCall_pow);
TEST_CASE(mathfunctionCall_atan2);
TEST_CASE(mathfunctionCall_precision);
// Ignored return value
TEST_CASE(checkIgnoredReturnValue);
TEST_CASE(checkIgnoredErrorCode);
// memset..
TEST_CASE(memsetZeroBytes);
TEST_CASE(memsetInvalid2ndParam);
// missing "return"
TEST_CASE(checkMissingReturn1);
TEST_CASE(checkMissingReturn2); // #11798
TEST_CASE(checkMissingReturn3);
TEST_CASE(checkMissingReturn4);
TEST_CASE(checkMissingReturn5);
TEST_CASE(checkMissingReturn6); // #13180
TEST_CASE(checkMissingReturn7); // #14370 - FN try/catch
TEST_CASE(checkMissingReturn8);
TEST_CASE(checkMissingReturn9);
TEST_CASE(checkMissingReturnStdInt); // #14482 - FN std::int32_t
// std::move for locar variable
TEST_CASE(returnLocalStdMove1);
TEST_CASE(returnLocalStdMove2);
TEST_CASE(returnLocalStdMove3);
TEST_CASE(returnLocalStdMove4);
TEST_CASE(returnLocalStdMove5);
TEST_CASE(negativeMemoryAllocationSizeError); // #389
TEST_CASE(checkLibraryMatchFunctions);
TEST_CASE(checkUseStandardLibrary1);
TEST_CASE(checkUseStandardLibrary2);
TEST_CASE(checkUseStandardLibrary3);
TEST_CASE(checkUseStandardLibrary4);
TEST_CASE(checkUseStandardLibrary5);
TEST_CASE(checkUseStandardLibrary6);
TEST_CASE(checkUseStandardLibrary7);
TEST_CASE(checkUseStandardLibrary8);
TEST_CASE(checkUseStandardLibrary9);
TEST_CASE(checkUseStandardLibrary10);
TEST_CASE(checkUseStandardLibrary11);
TEST_CASE(checkUseStandardLibrary12);
TEST_CASE(checkUseStandardLibrary13);
TEST_CASE(checkUseStandardLibrary14);
}
struct CheckOptions
{
bool cpp = true;
};
#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
template
void check_(const char* file, int line, const char (&code)[size], const CheckOptions& options = make_default_obj()) {
check_(file, line, code, settings, options.cpp);
}
template
void check_(const char* file, int line, const char (&code)[size], const Settings& s, bool cpp = true) {
SimpleTokenizer tokenizer(s, *this, cpp);
ASSERT_LOC(tokenizer.tokenize(code), file, line);
CheckFunctions check;
runChecks(check, tokenizer, *this);
}
void prohibitedFunctions_posix() {
check("void f()\n"
"{\n"
" bsd_signal(SIGABRT, SIG_IGN);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:5]: (style) Obsolescent function 'bsd_signal' called. It is recommended to use 'sigaction' instead. [bsd_signalCalled]\n", errout_str());
check("int f()\n"
"{\n"
" int bsd_signal(0);\n"
" return bsd_signal;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("void f()\n"
"{\n"
" struct hostent *hp;\n"
" if(!hp = gethostbyname(\"127.0.0.1\")) {\n"
" exit(1);\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4:14]: (style) Obsolescent function 'gethostbyname' called. It is recommended to use 'getaddrinfo' instead. [gethostbynameCalled]\n", errout_str());
check("void f()\n"
"{\n"
" long addr;\n"
" addr = inet_addr(\"127.0.0.1\");\n"
" if(!hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) {\n"
" exit(1);\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5:14]: (style) Obsolescent function 'gethostbyaddr' called. It is recommended to use 'getnameinfo' instead. [gethostbyaddrCalled]\n", errout_str());
check("void f()\n"
"{\n"
" usleep( 1000 );\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:5]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead. [usleepCalled]\n", errout_str());
}
void prohibitedFunctions_index() {
check("namespace n1 {\n"
" int index(){ return 1; };\n"
"}\n"
"int main()\n"
"{\n"
" n1::index();\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("std::size_t f()\n"
"{\n"
" std::size_t index(0);\n"
" index++;\n"
" return index;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("int f()\n"
"{\n"
" return this->index();\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("void f()\n"
"{\n"
" int index( 0 );\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("const char f()\n"
"{\n"
" const char var[6] = \"index\";\n"
" const char i = index(var, 0);\n"
" return i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4:20]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead. [indexCalled]\n",
errout_str());
}
void prohibitedFunctions_qt_index() {
check("void TDataModel::forceRowRefresh(int row) {\n"
" emit dataChanged(index(row, 0), index(row, columnCount() - 1));\n"
"}\n");
ASSERT_EQUALS(
"[test.cpp:2:22]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead. [indexCalled]\n"
"[test.cpp:2:37]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead. [indexCalled]\n",
errout_str());
}
void prohibitedFunctions_rindex() {
check("void f()\n"
"{\n"
" int rindex( 0 );\n"
"}\n");
ASSERT_EQUALS("", errout_str());
check("void f()\n"
"{\n"
" const char var[7] = \"rindex\";\n"
" print(rindex(var, 0));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4:11]: (style) Obsolescent function 'rindex' called. It is recommended to use 'strrchr' instead. [rindexCalled]\n", errout_str());
}
void prohibitedFunctions_var() {
check("class Fred {\n"
"public:\n"
" Fred() : index(0) { }\n"
" int index;\n"
"};\n");
ASSERT_EQUALS("", errout_str());
}
void prohibitedFunctions_gets() {
check("void f()\n"
"{\n"
" char *x = gets(a);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:15]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n", errout_str());
check("void f()\n"
"{\n"
" foo(x, gets(a));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:12]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n", errout_str());
}
void prohibitedFunctions_alloca() {
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
"}\n"); // #4382 - there are no VLAs in C++
ASSERT_EQUALS("[test.cpp:3:15]: (warning) Obsolete function 'alloca' called. [allocaCalled]\n", errout_str());
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
"}\n", dinit(CheckOptions, $.cpp = false));
ASSERT_EQUALS("[test.c:3:15]: (warning) Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead. [allocaCalled]\n", errout_str());
const Settings s = settingsBuilder(settings).c(Standards::C89).cpp(Standards::CPP03).build();
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
"}\n", s); // #4382 - there are no VLAs in C++
ASSERT_EQUALS("", errout_str());
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
"}\n", s, false); // #7558 - no alternative to alloca in C89
ASSERT_EQUALS("", errout_str());
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
"}\n", s, false);
ASSERT_EQUALS("", errout_str());
}
// ticket #3121
void prohibitedFunctions_declaredFunction() {
check("int ftime ( int a )\n"
"{\n"
" return a;\n"
"}\n"
"int main ()\n"
"{\n"
" int b ; b = ftime ( 1 ) ;\n"
" return 0 ;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
// test std::gets
void prohibitedFunctions_std_gets() {
check("void f(char * str)\n"
"{\n"
" char *x = std::gets(str);\n"
" char *y = gets(str);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:20]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n"
"[test.cpp:4:15]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n", errout_str());
}
// multiple use
void prohibitedFunctions_multiple() {
check("void f(char * str)\n"
"{\n"
" char *x = std::gets(str);\n"
" usleep( 1000 );\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:20]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n"
"[test.cpp:4:5]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead. [usleepCalled]\n", errout_str());
}
void prohibitedFunctions_c_declaration() {
check("char * gets ( char * c ) ;\n"
"int main ()\n"
"{\n"
" char s [ 10 ] ;\n"
" gets ( s ) ;\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5:5]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. [getsCalled]\n", errout_str());
check("int getcontext(ucontext_t *ucp);\n"
"void f (ucontext_t *ucp)\n"
"{\n"
" getcontext ( ucp ) ;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4:5]: (portability) Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads. [getcontextCalled]\n", errout_str());
}
void prohibitedFunctions_functionWithBody() {
check("char * gets ( char * c ) { return c; }\n"
"int main ()\n"
"{\n"
" char s [ 10 ] ;\n"
" gets ( s ) ;\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
void prohibitedFunctions_crypt() {
check("void f(char *pwd)\n"
"{\n"
" char *cpwd;"
" crypt(pwd, cpwd);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:20]: (warning) Return value of function crypt() is not used. [ignoredReturnValue]\n"
"[test.cpp:3:20]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'. [cryptCalled]\n", errout_str());
check("void f()\n"
"{\n"
" char *pwd = getpass(\"Password:\");"
" char *cpwd;"
" crypt(pwd, cpwd);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:57]: (warning) Return value of function crypt() is not used. [ignoredReturnValue]\n"
"[test.cpp:3:57]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'. [cryptCalled]\n", errout_str());
check("int f()\n"
"{\n"
" int crypt = 0;"
" return crypt;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
void prohibitedFunctions_namespaceHandling() {
check("void f()\n"
"{\n"
" time_t t = 0;"
" auto lt = std::localtime(&t);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:37]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'. [localtimeCalled]\n", errout_str());
// Passed as function argument
check("void f()\n"
"{\n"
" printf(\"Magic guess: %d\", getpwent());\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:31]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'. [getpwentCalled]\n", errout_str());
// Pass return value
check("void f()\n"
"{\n"
" time_t t = 0;"
" struct tm *foo = localtime(&t);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:39]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'. [localtimeCalled]\n", errout_str());
// Access via global namespace
check("void f()\n"
"{\n"
" ::getpwent();\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3:7]: (warning) Return value of function ::getpwent() is not used. [ignoredReturnValue]\n"
"[test.cpp:3:7]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'. [getpwentCalled]\n", errout_str());
// Be quiet on function definitions
check("int getpwent()\n"
"{\n"
" return 123;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
// Be quiet on other namespaces
check("void f()\n"
"{\n"
" foobar::getpwent();\n"
"}\n");
ASSERT_EQUALS("", errout_str());
// Be quiet on class member functions
check("void f()\n"
"{\n"
" foobar.getpwent();\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
void invalidFunctionUsage1() {
check("void f() { memset(a,b,sizeof(a)!=12); }\n");
ASSERT_EQUALS("[test.cpp:1:32]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
check("void f() { memset(a,b,sizeof(a)!=0); }\n");
ASSERT_EQUALS("[test.cpp:1:32]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
check("void f() { memset(a,b,!c); }\n");
ASSERT_EQUALS("[test.cpp:1:23]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
// Ticket #6990
check("void f(bool c) { memset(a,b,c); }\n");
ASSERT_EQUALS("[test.cpp:1:29]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
check("void f() { memset(a,b,true); }\n");
ASSERT_EQUALS("[test.cpp:1:23]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
// Ticket #6588 (c mode)
check("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}\n", dinit(CheckOptions, $.cpp = false));
ASSERT_EQUALS("[test.c:2:20]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
// Ticket #6588 (c++ mode)
check("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}\n");
ASSERT_EQUALS("[test.cpp:2:20]: (error) Invalid memset() argument nr 3. A non-boolean value is required. [invalidFunctionArgBool]\n", errout_str());
check("int boolArgZeroIsInvalidButOneIsValid(int a, int param) {\n"
" return div(a, param > 0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2:23]: (error) Invalid div() argument nr 2. The value is 0 or 1 (boolean) but the valid values are ':-1,1:'. [invalidFunctionArg]\n", errout_str());
check("void boolArgZeroIsValidButOneIsInvalid(int param) {\n"
" strtol(a, b, param > 0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2:22]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (boolean) but the valid values are '0,2:36'. [invalidFunctionArg]\n", errout_str());
check("void f() { strtol(a,b,1); }\n");
ASSERT_EQUALS("[test.cpp:1:23]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'. [invalidFunctionArg]\n", errout_str());
check("void f() { strtol(a,b,10); }\n");
ASSERT_EQUALS("", errout_str());
check("void f(std::vector& v) {\n" // #10754
" int N = -1;\n"
" for (long i = 0; i < g(); i++)\n"
" N = h(N);\n"
" v.resize(N);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5:14]: (warning) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'. [invalidFunctionArg]\n", errout_str());
check("void f(std::vector& v, int N) {\n"
" if (N < -1)\n"
" return;\n"
" v.resize(N);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:4:14]: (warning) Either the condition 'N