2014-08-27 09:42:09 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2014-08-27 09:42:09 +02:00
*
* 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 < http : //www.gnu.org/licenses/>.
*/
# include "checkstring.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2023-10-08 11:29:52 +02:00
# include "helpers.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2023-01-27 08:18:32 +01:00
# include "fixture.h"
2017-05-27 04:33:47 +02:00
# include "tokenize.h"
2014-08-27 09:42:09 +02:00
2023-08-22 19:33:24 +02:00
# include <simplecpp.h>
2022-09-16 07:15:49 +02:00
# include <sstream> // IWYU pragma: keep
2022-01-27 19:03:20 +01:00
2014-08-27 09:42:09 +02:00
class TestString : public TestFixture {
public :
2021-08-07 20:51:18 +02:00
TestString ( ) : TestFixture ( " TestString " ) { }
2014-08-27 09:42:09 +02:00
private :
2023-05-02 11:48:24 +02:00
const Settings settings = settingsBuilder ( ) . severity ( Severity : : warning ) . severity ( Severity : : style ) . build ( ) ;
2014-08-27 09:42:09 +02:00
2022-02-10 23:02:24 +01:00
void run ( ) override {
2015-05-03 10:44:40 +02:00
TEST_CASE ( stringLiteralWrite ) ;
2014-08-27 09:42:09 +02:00
TEST_CASE ( alwaysTrueFalseStringCompare ) ;
TEST_CASE ( suspiciousStringCompare ) ;
TEST_CASE ( suspiciousStringCompare_char ) ;
TEST_CASE ( strPlusChar1 ) ; // "/usr" + '/'
TEST_CASE ( strPlusChar2 ) ; // "/usr" + ch
TEST_CASE ( strPlusChar3 ) ; // ok: path + "/sub" + '/'
2019-04-06 06:54:38 +02:00
TEST_CASE ( strPlusChar4 ) ; // L"/usr" + L'/'
2014-08-27 09:42:09 +02:00
2019-04-06 06:54:38 +02:00
TEST_CASE ( snprintf1 ) ; // Dangerous usage of snprintf
2014-08-27 09:42:09 +02:00
TEST_CASE ( sprintf1 ) ; // Dangerous usage of sprintf
TEST_CASE ( sprintf2 ) ;
TEST_CASE ( sprintf3 ) ;
TEST_CASE ( sprintf4 ) ; // struct member
2019-07-05 12:27:39 +02:00
TEST_CASE ( sprintf5 ) ; // another struct member
TEST_CASE ( sprintf6 ) ; // (char*)
TEST_CASE ( sprintf7 ) ; // (char*)(void*)
2019-04-06 06:54:38 +02:00
TEST_CASE ( wsprintf1 ) ; // Dangerous usage of wsprintf
2014-08-27 09:42:09 +02:00
TEST_CASE ( incorrectStringCompare ) ;
2017-11-27 23:32:20 +01:00
2017-12-11 22:10:00 +01:00
TEST_CASE ( deadStrcmp ) ;
2014-08-27 09:42:09 +02:00
}
2023-10-08 11:29:52 +02:00
# define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
void check_ ( const char * file , int line , const char code [ ] , const char filename [ ] = " test.cpp " ) {
2014-08-27 09:42:09 +02:00
// Clear the error buffer..
errout . str ( " " ) ;
2023-08-22 19:33:24 +02:00
std : : vector < std : : string > files ( 1 , filename ) ;
2023-10-08 11:29:52 +02:00
Tokenizer tokenizer ( & settings , this ) ;
PreprocessorHelper : : preprocess ( code , files , tokenizer ) ;
2023-08-22 19:33:24 +02:00
2014-08-27 09:42:09 +02:00
// Tokenize..
2023-10-08 11:29:52 +02:00
ASSERT_LOC ( tokenizer . simplifyTokens1 ( " " ) , file , line ) ;
2014-08-27 09:42:09 +02:00
// Check char variable usage..
2023-08-18 12:03:50 +02:00
runChecks < CheckString > ( tokenizer , this ) ;
2014-08-27 09:42:09 +02:00
}
2015-05-03 10:44:40 +02:00
void stringLiteralWrite ( ) {
check ( " void f() { \n "
" char *abc = \" abc \" ; \n "
" abc[0] = 'a'; \n "
" } " ) ;
2015-10-18 20:55:30 +02:00
ASSERT_EQUALS ( " [test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \" abc \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2015-05-03 10:44:40 +02:00
check ( " void f() { \n "
" char *abc = \" abc \" ; \n "
" *abc = 'a'; \n "
" } " ) ;
2015-10-18 20:55:30 +02:00
ASSERT_EQUALS ( " [test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \" abc \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2015-05-03 10:44:40 +02:00
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
check ( " void f() { \n "
" char *abc = \" A very long string literal \" ; \n "
" abc[0] = 'a'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \" A very long stri.. \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2015-05-03 10:44:40 +02:00
check ( " void f() { \n "
2015-05-03 12:34:27 +02:00
" QString abc = \" abc \" ; \n "
" abc[0] = 'a'; \n "
2015-05-03 10:44:40 +02:00
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2016-07-07 19:38:15 +02:00
check ( " void foo_FP1(char *p) { \n "
" p[1] = 'B'; \n "
" } \n "
" void foo_FP2(void) { \n "
" char* s = \" Y \" ; \n "
" foo_FP1(s); \n "
" } " ) ;
2019-08-05 16:26:32 +02:00
ASSERT_EQUALS (
" [test.cpp:2] -> [test.cpp:5]: (error) Modifying string literal \" Y \" directly or indirectly is undefined behaviour. \n " ,
errout . str ( ) ) ;
2016-07-07 19:38:15 +02:00
check ( " void foo_FP1(char *p) { \n "
" p[1] = 'B'; \n "
" } \n "
" void foo_FP2(void) { \n "
" char s[10] = \" Y \" ; \n "
" foo_FP1(s); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " void f() { \n "
" wchar_t *abc = L \" abc \" ; \n "
" abc[0] = u'a'; \n "
" } " ) ;
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
ASSERT_EQUALS ( " [test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal L \" abc \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " void f() { \n "
" char16_t *abc = u \" abc \" ; \n "
" abc[0] = 'a'; \n "
" } " ) ;
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
ASSERT_EQUALS ( " [test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal u \" abc \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2022-04-06 08:12:41 +02:00
check ( " void foo() { \n " // #8332
" int i; \n "
" char *p = \" string literal \" ; \n "
" for( i = 0; i < strlen(p); i++) { \n "
" p[i] = \' X \' ; \n " // <<
" } \n "
" printf( \" %s \\ n \" , p); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:5] -> [test.cpp:3]: (error) Modifying string literal \" string literal \" directly or indirectly is undefined behaviour. \n " , errout . str ( ) ) ;
2015-05-03 10:44:40 +02:00
}
2014-11-20 14:20:09 +01:00
void alwaysTrueFalseStringCompare ( ) {
2015-01-02 14:04:55 +01:00
check ( " void f() { \n "
2015-01-02 12:58:04 +01:00
" if (strcmp( \" A \" , \" A \" )){} \n "
" if (strncmp( \" A \" , \" A \" ,1)){} \n "
" if (strcasecmp( \" A \" , \" A \" )){} \n "
" if (strncasecmp( \" A \" , \" A \" ,1)){} \n "
" if (memcmp( \" A \" , \" A \" ,1)){} \n "
" if (strverscmp( \" A \" , \" A \" )){} \n "
" if (bcmp( \" A \" , \" A \" ,1)){} \n "
" if (wcsncasecmp(L \" A \" ,L \" A \" ,1)){} \n "
" if (wcsncmp(L \" A \" ,L \" A \" ,1)){} \n "
" if (wmemcmp(L \" A \" ,L \" A \" ,1)){} \n "
" if (wcscmp(L \" A \" ,L \" A \" )){} \n "
" if (wcscasecmp(L \" A \" ,L \" A \" )){} \n "
2015-01-02 14:04:55 +01:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:3]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:4]: (warning) Unnecessary comparison of static strings. \n "
2015-01-02 12:58:04 +01:00
" [test.cpp:5]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:6]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:7]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:8]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:9]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:10]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:11]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:12]: (warning) Unnecessary comparison of static strings. \n "
" [test.cpp:13]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2015-01-02 14:04:55 +01:00
2015-01-07 08:30:05 +01:00
// avoid false positives when the address is modified #6415
check ( " void f(void *p, int offset) { \n "
2015-01-07 08:38:39 +01:00
" if (!memcmp(p, p + offset, 42)){} \n "
" if (!memcmp(p + offset, p, 42)){} \n "
" if (!memcmp(offset + p, p, 42)){} \n "
" if (!memcmp(p, offset + p, 42)){} \n "
2015-10-07 14:05:55 +02:00
" } " ) ;
2015-01-07 08:30:05 +01:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// avoid false positives when the address is modified #6415
check ( " void f(char *c, int offset) { \n "
" if (!memcmp(c, c + offset, 42)){} \n "
2015-01-07 08:38:39 +01:00
" if (!memcmp(c + offset, c, 42)){} \n "
" if (!memcmp(offset + c, c, 42)){} \n "
" if (!memcmp(c, offset + c, 42)){} \n "
2015-10-07 14:05:55 +02:00
" } " ) ;
2015-01-07 08:30:05 +01:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// avoid false positives when the address is modified #6415
check ( " void f(std::string s, int offset) { \n "
" if (!memcmp(s.c_str(), s.c_str() + offset, 42)){} \n "
" if (!memcmp(s.c_str() + offset, s.c_str(), 42)){} \n "
2015-01-07 08:38:39 +01:00
" if (!memcmp(offset + s.c_str(), s.c_str(), 42)){} \n "
" if (!memcmp(s.c_str(), offset + s.c_str(), 42)){} \n "
2015-10-07 14:05:55 +02:00
" } " ) ;
2015-01-07 08:30:05 +01:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() \n "
" { \n "
" if (strcmp( \" 00FF00 \" , \" 00FF00 \" ) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2016-01-13 20:32:40 +01:00
check ( " void f() { \n "
" if (strcmp($ \" 00FF00 \" , \" 00FF00 \" ) == 0) {} "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void f() { \n "
" if ($strcmp( \" 00FF00 \" , \" 00FF00 \" ) == 0) {} "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() \n "
" { \n "
" if (stricmp( \" hotdog \" , \" HOTdog \" ) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
check ( " int main() \n "
" { \n "
" if (QString::compare( \" Hamburger \" , \" Hotdog \" ) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() \n "
" { \n "
" if (QString::compare(argv[2], \" hotdog \" ) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() \n "
" { \n "
" if (strncmp( \" hotdog \" , \" hotdog \" , 6) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int foo(const char *buf) \n "
" { \n "
" if (strcmp(buf, buf) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Comparison of identical string variables. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int foo(const std::string& buf) \n "
" { \n "
" if (stricmp(buf.c_str(), buf.c_str()) == 0) "
" { "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; "
2015-10-07 14:05:55 +02:00
" } "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Comparison of identical string variables. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() { \n "
" if ( \" str \" == \" str \" ) { \n "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; \n "
2015-10-07 14:05:55 +02:00
" } \n "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() { \n "
" if ( \" str \" != \" str \" ) { \n "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; \n "
2015-10-07 14:05:55 +02:00
" } \n "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Unnecessary comparison of static strings. \n " , errout . str ( ) ) ;
2015-10-07 14:05:55 +02:00
check ( " int main() { \n "
" if (a+ \" str \" != \" str \" +b) { \n "
2017-05-18 21:50:45 +02:00
" std::cout << \" Equal \" ; \n "
2015-10-07 14:05:55 +02:00
" } \n "
" } " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void suspiciousStringCompare ( ) {
2014-08-27 09:42:09 +02:00
check ( " bool foo(char* c) { \n "
" return c == \" x \" ; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2021-02-20 12:42:48 +01:00
check ( " bool foo(char** c) { \n "
" return c[3] == \" x \" ; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal compared with variable 'c[3]'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " bool foo(wchar_t* c) { \n "
" return c == L \" x \" ; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use wcscmp() instead? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " bool foo(const char* c) { \n "
" return \" x \" == c; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal compared with variable 'c'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
check ( " bool foo(char* c) { \n "
" return foo+ \" x \" == c; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(char* c) { \n "
" return \" x \" == c+foo; \n "
" } " , " test.cpp " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(char* c) { \n "
" return \" x \" == c+foo; \n "
" } " , " test.c " ) ;
2021-02-20 13:29:14 +01:00
ASSERT_EQUALS ( " [test.c:2]: (warning) String literal compared with variable 'c+foo'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " bool foo(Foo c) { \n "
" return \" x \" == c.foo; \n "
" } " , " test.cpp " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(Foo c) { \n "
" return \" x \" == c.foo; \n "
" } " , " test.c " ) ;
ASSERT_EQUALS ( " [test.c:2]: (warning) String literal compared with variable 'c.foo'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
check ( " bool foo(const std::string& c) { \n "
" return \" x \" == c; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(const Foo* c) { \n "
" return \" x \" == c->bar(); \n " // #4314
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Ticket #4257
check ( " bool foo() { \n "
" MyString *str=Getter(); \n "
" return *str== \" bug \" ; } \n " , " test.c " ) ;
ASSERT_EQUALS ( " [test.c:3]: (warning) String literal compared with variable '*str'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
// Ticket #4257
check ( " bool foo() { \n "
" MyString *str=Getter(); \n "
" return *str== \" bug \" ; } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Ticket #4257
check ( " bool foo() { \n "
" MyString **str=OtherGetter(); \n "
2021-02-20 12:42:48 +01:00
" return *str== \" bug \" ; } " , " test.c " ) ;
ASSERT_EQUALS ( " [test.c:3]: (warning) String literal compared with variable '*str'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
// Ticket #4257
check ( " bool foo() { \n "
" MyString str=OtherGetter2(); \n "
2021-02-20 12:42:48 +01:00
" return &str== \" bug \" ; } " , " test.c " ) ;
ASSERT_EQUALS ( " [test.c:3]: (warning) String literal compared with variable '&str'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
// Ticket #5734
check ( " int foo(char c) { \n "
2015-11-20 10:10:38 +01:00
" return c == '4';} " , " test.cpp " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int foo(char c) { \n "
2015-11-20 10:10:38 +01:00
" return c == '4';} " , " test.c " ) ;
2014-08-27 09:42:09 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int foo(char c) { \n "
" return c == \" 42 \" [0];} " , " test.cpp " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int foo(char c) { \n "
" return c == \" 42 \" [0];} " , " test.c " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// 5639 String literal compared with char buffer in a struct
check ( " struct Example { \n "
" char buffer[200]; \n "
" }; \n "
" void foo() { \n "
" struct Example example; \n "
" if (example.buffer == \" test \" ) ; \n "
" } \n " , " test.cpp " ) ;
ASSERT_EQUALS ( " [test.cpp:6]: (warning) String literal compared with variable 'example.buffer'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
check ( " struct Example { \n "
" char buffer[200]; \n "
" }; \n "
" void foo() { \n "
" struct Example example; \n "
" if (example.buffer == \" test \" ) ; \n "
" } \n " , " test.c " ) ;
ASSERT_EQUALS ( " [test.c:6]: (warning) String literal compared with variable 'example.buffer'. Did you intend to use strcmp() instead? \n " , errout . str ( ) ) ;
2020-06-06 17:47:30 +02:00
// #9726
check ( " void f(std::vector<std::string> theArgs) { \n "
" std::string arg1(*theArgs.begin()); \n "
" if(arg1 == \" aaa \" ) {} \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
}
2014-11-20 14:20:09 +01:00
void suspiciousStringCompare_char ( ) {
2014-08-27 09:42:09 +02:00
check ( " bool foo(char* c) { \n "
2016-10-09 11:39:20 +02:00
" return c == 'x'; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " bool foo(wchar_t* c) { \n "
" return c == L'x'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " bool foo(char* c) { \n "
" return ' \\ 0' != c; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
check ( " bool foo(char c) { \n "
" return c == ' \\ 0'; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2021-02-20 13:29:14 +01:00
check ( " bool foo(char* c) { \n "
" return c[0] == ' \\ 0'; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(char** c) { \n "
" return c[0] == ' \\ 0'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer 'c[0]'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
check ( " bool foo(char** c) { \n "
" return *c == ' \\ 0'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer '*c'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " bool foo(char c) { \n "
" return c == 0; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(char* c) { \n "
" return *c == 0; \n "
" } " , " test.c " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(char* c) { \n "
" return *c == 0; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " bool foo(Foo* c) { \n "
" return 0 == c->x; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(char* c) { \n "
" if(c == ' \\ 0') bar(); \n "
" } " ) ;
2015-01-31 20:12:02 +01:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Char literal compared with pointer 'c'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " void f() { \n "
" struct { struct { char *str; } x; } a; \n "
" return a.x.str == ' \\ 0'; "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Char literal compared with pointer 'a.x.str'. Did you intend to dereference it? \n " , errout . str ( ) ) ;
check ( " void f() { \n "
" struct { struct { char *str; } x; } a; \n "
" return *a.x.str == ' \\ 0'; "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2019-04-06 06:54:38 +02:00
void snprintf1 ( ) {
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" snprintf(buf,100, \" %s \" ,buf); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in snprintf(). \n " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void sprintf1 ( ) {
2014-08-27 09:42:09 +02:00
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" sprintf(buf, \" %s \" ,buf); \n "
" } " ) ;
2019-04-06 06:54:38 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in sprintf(). \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
}
2014-11-20 14:20:09 +01:00
void sprintf2 ( ) {
2014-08-27 09:42:09 +02:00
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" sprintf(buf, \" %i \" ,sizeof(buf)); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void sprintf3 ( ) {
2014-08-27 09:42:09 +02:00
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" sprintf(buf, \" %i \" ,sizeof(buf)); \n "
" if (buf[0]); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void sprintf4 ( ) {
2014-08-27 09:42:09 +02:00
check ( " struct A \n "
" { \n "
" char filename[128]; \n "
" }; \n "
" \n "
" void foo() \n "
" { \n "
" const char* filename = \" hello \" ; \n "
" struct A a; \n "
" snprintf(a.filename, 128, \" %s \" , filename); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2019-07-05 12:27:39 +02:00
void sprintf5 ( ) {
check ( " struct A \n "
" { \n "
" char filename[128]; \n "
" }; \n "
" \n "
" void foo(struct A *a) \n "
" { \n "
" snprintf(a->filename, 128, \" %s \" , a->filename); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:8]: (error) Undefined behavior: Variable 'a->filename' is used as parameter and destination in snprintf(). \n " , errout . str ( ) ) ;
}
void sprintf6 ( ) {
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" sprintf((char*)buf, \" %s \" ,(char*)buf); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in sprintf(). \n " , errout . str ( ) ) ;
}
void sprintf7 ( ) {
check ( " void foo() \n "
" { \n "
" char buf[100]; \n "
" sprintf((char*)(void*)buf, \" %s \" ,(void*)(char*)buf); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in sprintf(). \n " , errout . str ( ) ) ;
}
2019-04-06 06:54:38 +02:00
void wsprintf1 ( ) {
check ( " void foo() \n "
" { \n "
" wchar_t buf[100]; \n "
" swprintf(buf,10, \" %s \" ,buf); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in swprintf(). \n " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void strPlusChar1 ( ) {
2014-08-27 09:42:09 +02:00
// Strange looking pointer arithmetic..
check ( " void foo() \n "
" { \n "
" const char *p = \" /usr \" + '/'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Unusual pointer arithmetic. A value of type 'char' is added to a string literal. \n " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void strPlusChar2 ( ) {
2014-08-27 09:42:09 +02:00
// Strange looking pointer arithmetic..
check ( " void foo() \n "
" { \n "
" char ch = 1; \n "
" const char *p = ch + \" /usr \" ; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Strange looking pointer arithmetic..
check ( " void foo() \n "
" { \n "
" int i = 1; \n "
" const char* psz = \" Bla \" ; \n "
" const std::string str = i + psz; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void strPlusChar3 ( ) {
2014-08-27 09:42:09 +02:00
// Strange looking pointer arithmetic..
check ( " void foo() \n "
" { \n "
" std::string temp = \" /tmp \" ; \n "
" std::string path = temp + '/' + \" sub \" + '/'; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2019-04-06 06:54:38 +02:00
void strPlusChar4 ( ) {
// Strange looking pointer arithmetic, wide char..
check ( " void foo() \n "
" { \n "
" const wchar_t *p = L \" /usr \" + L'/'; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Unusual pointer arithmetic. A value of type 'wchar_t' is added to a string literal. \n " , errout . str ( ) ) ;
check ( " void foo(wchar_t c) \n "
" { \n "
" const wchar_t *p = L \" /usr \" + c; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Unusual pointer arithmetic. A value of type 'wchar_t' is added to a string literal. \n " , errout . str ( ) ) ;
}
2014-08-27 09:42:09 +02:00
2014-11-20 14:20:09 +01:00
void incorrectStringCompare ( ) {
2014-08-27 09:42:09 +02:00
check ( " int f() { \n "
2019-04-06 06:54:38 +02:00
" return test.substr( 0 , 4 ) == \" Hello \" ? 0 : 1 ; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal \" Hello \" doesn't match length argument for substr(). \n " , errout . str ( ) ) ;
check ( " int f() { \n "
2019-04-06 06:54:38 +02:00
" return test.substr( 0 , 4 ) == L \" Hello \" ? 0 : 1 ; \n "
" } " ) ;
2019-10-16 11:41:33 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal L \" Hello \" doesn't match length argument for substr(). \n " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " int f() { \n "
" return test.substr( 0 , 5 ) == \" Hello \" ? 0 : 1 ; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int f() { \n "
2019-04-06 06:54:38 +02:00
" return \" Hello \" == test.substr( 0 , 4 ) ? 0 : 1 ; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal \" Hello \" doesn't match length argument for substr(). \n " , errout . str ( ) ) ;
check ( " int f() { \n "
2019-04-06 06:54:38 +02:00
" return \" Hello \" == foo.bar<int>().z[1].substr(i+j*4, 4) ? 0 : 1 ; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) String literal \" Hello \" doesn't match length argument for substr(). \n " , errout . str ( ) ) ;
check ( " int f() { \n "
2019-04-06 06:54:38 +02:00
" return \" Hello \" == test.substr( 0 , 5 ) ? 0 : 1 ; \n "
2014-08-27 09:42:09 +02:00
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int f() { \n "
" if ( \" Hello \" ) { } \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
check ( " int f() { \n "
" if ( \" Hello \" && test) { } \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
check ( " int f() { \n "
" if (test && \" Hello \" ) { } \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
check ( " int f() { \n "
" while ( \" Hello \" ) { } \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
2019-04-06 06:54:38 +02:00
check ( " int f() { \n "
" return \" Hello \" ? 1 : 2; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
check ( " int f() { \n "
" assert (test || \" Hello \" ); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
check ( " int f() { \n "
" assert (test && \" Hello \" ); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int f() { \n "
" assert ( \" Hello \" || test); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of string literal \" Hello \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
check ( " int f() { \n "
" assert ( \" Hello \" && test); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int f() { \n "
" BOOST_ASSERT ( \" Hello \" && test); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " int f() { \n "
" return f2( \" Hello \" ); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2018-07-21 18:40:06 +02:00
// #7750 warn about char literals in boolean expressions
check ( " void f() { \n "
" if('a'){} \n "
" if(L'b'){} \n "
" if(1 && 'c'){} \n "
2019-04-06 06:54:38 +02:00
" int x = 'd' ? 1 : 2; \n "
2018-07-21 18:40:06 +02:00
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of char literal 'a' to bool always evaluates to true. \n "
2019-10-16 11:41:33 +02:00
" [test.cpp:3]: (warning) Conversion of char literal L'b' to bool always evaluates to true. \n "
2018-07-21 18:40:06 +02:00
" [test.cpp:4]: (warning) Conversion of char literal 'c' to bool always evaluates to true. \n "
2019-04-06 06:54:38 +02:00
" [test.cpp:5]: (warning) Conversion of char literal 'd' to bool always evaluates to true. \n "
2018-07-21 18:40:06 +02:00
, errout . str ( ) ) ;
2018-08-08 19:04:10 +02:00
check ( " void f() { \n "
" if(' \\ 0'){} \n "
2019-04-06 06:54:38 +02:00
" if(L' \\ 0'){} \n "
2018-08-08 19:04:10 +02:00
" } " ) ;
2023-08-21 10:43:54 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of char literal ' \\ 0' to bool always evaluates to false. \n "
" [test.cpp:3]: (warning) Conversion of char literal L' \\ 0' to bool always evaluates to false. \n " ,
errout . str ( ) ) ;
2018-08-12 08:01:15 +02:00
check ( " void f() { \n "
" if(' \\ 0' || cond){} \n "
2019-04-06 06:54:38 +02:00
" if(L' \\ 0' || cond){} \n "
2018-08-12 08:01:15 +02:00
" } " ) ;
2019-04-06 06:54:38 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) Conversion of char literal ' \\ 0' to bool always evaluates to false. \n "
2019-10-16 11:41:33 +02:00
" [test.cpp:3]: (warning) Conversion of char literal L' \\ 0' to bool always evaluates to false. \n " , errout . str ( ) ) ;
2023-08-18 10:32:52 +02:00
check ( " void f(bool b); \n " // #9450
" void f(std::string s); \n "
" void g() { \n "
" f( \" abc \" ); \n "
" } \n " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (warning) Conversion of string literal \" abc \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
2023-08-21 10:43:54 +02:00
check ( " void g(bool); \n "
" void f(std::map<std::string, std::vector<int>>&m) { \n "
" if (m.count( \" abc \" )) \n "
" g(m[ \" abc \" ][0] ? true : false); \n "
" } \n " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void g(bool b); \n "
" void f() { \n "
" g(' \\ 0'); \n "
" g('a'); \n "
" } \n " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) Conversion of char literal ' \\ 0' to bool always evaluates to false. \n "
" [test.cpp:4]: (warning) Conversion of char literal 'a' to bool always evaluates to true. \n " ,
errout . str ( ) ) ;
2023-08-22 19:33:24 +02:00
check ( " #define ERROR(msg) if (msg) printf( \" %s \\ n \" , msg); \n "
" void f() { \n "
" ERROR( \" abc \" ) \n "
" } \n " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2023-08-23 18:06:41 +02:00
check ( " void g(int, bool); \n "
" void f() { \n "
" MyAssert(! \" abc \" ); \n "
" g(2, ! \" def \" ); \n "
" } \n " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (warning) Conversion of string literal \" def \" to bool always evaluates to true. \n " , errout . str ( ) ) ;
2023-08-24 10:36:01 +02:00
check ( " bool f(const char *p) { \n "
" if (*p == ' \\ 0') \n "
" return *p == ' \\ 0'; \n "
" return false; \n "
" } \n " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2023-09-12 10:05:51 +02:00
check ( " void f(const int* p, const int* q) { \n "
" assert((p != NULL && q != NULL) || ! \" abc \" ); \n "
" ASSERT((void*)( \" def \" ) == 0); \n "
" } \n " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-27 09:42:09 +02:00
}
2017-11-27 12:55:20 +01:00
2017-12-11 22:10:00 +01:00
void deadStrcmp ( ) {
2017-11-27 23:32:20 +01:00
check ( " void f(const char *str) { \n "
2017-11-27 12:55:20 +01:00
" if (strcmp(str, \" abc \" ) == 0 || strcmp(str, \" def \" )) {} \n "
" } " ) ;
2017-12-13 15:28:50 +01:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) The expression 'strcmp(str, \" def \" ) != 0' is suspicious. It overlaps 'strcmp(str, \" abc \" ) == 0'. \n " , errout . str ( ) ) ;
2017-11-27 12:55:20 +01:00
2019-04-06 06:54:38 +02:00
check ( " void f(const wchar_t *str) { \n "
" if (wcscmp(str, L \" abc \" ) == 0 || wcscmp(str, L \" def \" )) {} \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) The expression 'wcscmp(str,L \" def \" ) != 0' is suspicious. It overlaps 'wcscmp(str,L \" abc \" ) == 0'. \n " , errout . str ( ) ) ;
2017-11-27 23:32:20 +01:00
check ( " struct X { \n "
2017-11-27 12:55:20 +01:00
" char *str; \n "
" }; \n "
" \n "
" void f(const struct X *x) { \n "
" if (strcmp(x->str, \" abc \" ) == 0 || strcmp(x->str, \" def \" )) {} \n "
" } " ) ;
2017-12-13 15:28:50 +01:00
ASSERT_EQUALS ( " [test.cpp:6]: (warning) The expression 'strcmp(x->str, \" def \" ) != 0' is suspicious. It overlaps 'strcmp(x->str, \" abc \" ) == 0'. \n " , errout . str ( ) ) ;
2017-11-27 23:32:20 +01:00
}
2014-08-27 09:42:09 +02:00
} ;
REGISTER_TEST ( TestString )