2012-05-20 21:26:48 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2013-01-01 17:29:08 +01:00
* Copyright ( C ) 2007 - 2013 Daniel Marjamäki and Cppcheck team .
2012-05-20 21:26:48 +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 "checkio.h"
# include "testsuite.h"
# include "tokenize.h"
# include <sstream>
extern std : : ostringstream errout ;
class TestIO : public TestFixture {
public :
TestIO ( ) : TestFixture ( " TestIO " )
{ }
private :
void run ( ) {
TEST_CASE ( coutCerrMisusage ) ;
2012-05-22 14:30:22 +02:00
TEST_CASE ( wrongMode_simple ) ;
TEST_CASE ( wrongMode_complex ) ;
TEST_CASE ( useClosedFile ) ;
TEST_CASE ( fileIOwithoutPositioning ) ;
TEST_CASE ( fflushOnInputStream ) ;
2012-05-20 21:26:48 +02:00
TEST_CASE ( testScanf1 ) ; // Scanf without field limiters
TEST_CASE ( testScanf2 ) ;
TEST_CASE ( testScanf3 ) ;
2012-07-24 15:54:38 +02:00
TEST_CASE ( testScanf4 ) ; // #ticket 2553
2012-05-20 21:26:48 +02:00
TEST_CASE ( testScanfArgument ) ;
TEST_CASE ( testPrintfArgument ) ;
}
2012-07-07 13:34:37 +02:00
void check ( const char code [ ] , bool inconclusive = false , bool portability = false , Settings : : PlatformType platform = Settings : : Unspecified ) {
2012-05-20 21:26:48 +02:00
// Clear the error buffer..
errout . str ( " " ) ;
Settings settings ;
2013-03-03 11:41:59 +01:00
settings . addEnabled ( " warning " ) ;
2012-05-20 21:26:48 +02:00
settings . addEnabled ( " style " ) ;
2012-07-07 13:34:37 +02:00
if ( portability )
settings . addEnabled ( " portability " ) ;
2012-06-23 07:52:52 +02:00
settings . inconclusive = inconclusive ;
2012-07-07 13:34:37 +02:00
settings . platform ( platform ) ;
2012-05-20 21:26:48 +02:00
// Tokenize..
Tokenizer tokenizer ( & settings , this ) ;
std : : istringstream istr ( code ) ;
tokenizer . tokenize ( istr , " test.cpp " ) ;
// Check..
CheckIO checkIO ( & tokenizer , & settings , this ) ;
checkIO . checkWrongPrintfScanfArguments ( ) ;
// Simplify token list..
tokenizer . simplifyTokenList ( ) ;
checkIO . checkCoutCerrMisusage ( ) ;
2012-05-22 14:30:22 +02:00
checkIO . checkFileUsage ( ) ;
2012-05-20 21:26:48 +02:00
checkIO . invalidScanf ( ) ;
}
void coutCerrMisusage ( ) {
check (
" void foo() { \n "
" std::cout << std::cout; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'. \n " , errout . str ( ) ) ;
check (
" void foo() { \n "
" std::cout << \" xyz \" << std::cout; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'. \n " , errout . str ( ) ) ;
check (
" void foo(int i) { \n "
" std::cout << i << std::cerr; \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (error) Invalid usage of output stream: '<< std::cerr'. \n " , errout . str ( ) ) ;
check (
" void foo() { \n "
" std::cout << \" xyz \" ; \n "
" std::cout << \" xyz \" ; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check (
" void foo() { \n "
" std::cout << std::cout.good(); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check (
" void foo() { \n "
" MACRO(std::cout <<, << std::cout) \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2012-05-22 14:30:22 +02:00
void wrongMode_simple ( ) {
// Read mode
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" r \" ); \n "
" fread(buffer, 5, 6, f); \n "
" rewind(f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:5]: (error) Write operation on a file that was opened only for reading. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" r+ \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Write mode
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" w \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" rewind(f); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:5]: (error) Read operation on a file that was opened only for writing. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" w+ \" ); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Append mode
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" a \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" rewind(f); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:5]: (error) Read operation on a file that was opened only for writing. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" a+ \" ); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// Variable declared locally
check ( " void foo() { \n "
" FILE* f = fopen(name, \" r \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" fclose(f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (error) Write operation on a file that was opened only for reading. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
// Call unknown function
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" a \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" bar(f); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" a \" ); \n "
" fwrite(buffer, 5, 6, f); \n "
" clearerr(f); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:5]: (error) Read operation on a file that was opened only for writing. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
// freopen and tmpfile
check ( " void foo(FILE*& f) { \n "
" f = freopen(name, \" r \" , f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (error) Write operation on a file that was opened only for reading. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
2012-05-23 10:04:21 +02:00
// Crash tests
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE*& f) { \n "
2012-05-23 10:04:21 +02:00
" f = fopen(name, mode); \n " // No assertion failure (#3830)
2012-05-22 14:30:22 +02:00
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-05-23 10:04:21 +02:00
check ( " void fopen(std::string const &filepath, std::string const &mode); " ) ; // #3832
2012-05-22 14:30:22 +02:00
}
void wrongMode_complex ( ) {
check ( " void foo(FILE* f) { \n "
" if(a) f = fopen(name, \" w \" ); \n "
" else f = fopen(name, \" r \" ); \n "
" if(a) fwrite(buffer, 5, 6, f); \n "
" else fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo() { \n "
" FILE* f; \n "
" if(a) f = fopen(name, \" w \" ); \n "
" else f = fopen(name, \" r \" ); \n "
" if(a) fwrite(buffer, 5, 6, f); \n "
" else fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo() { \n "
" FILE* f = fopen(name, \" w \" ); \n "
" if(a) fwrite(buffer, 5, 6, f); \n "
" else fread(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) Read operation on a file that was opened only for writing. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
}
void useClosedFile ( ) {
check ( " void foo(FILE*& f) { \n "
" fclose(f); \n "
" fwrite(buffer, 5, 6, f); \n "
" clearerr(f); \n "
" fread(buffer, 5, 6, f); \n "
" ungetc('a', f); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Used file that is not opened. \n "
" [test.cpp:4]: (error) Used file that is not opened. \n "
" [test.cpp:5]: (error) Used file that is not opened. \n "
" [test.cpp:6]: (error) Used file that is not opened. \n " , errout . str ( ) ) ;
check ( " void foo(FILE*& f) { \n "
" if(!ferror(f)) { \n "
" fclose(f); \n "
" return; "
" } \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE*& f) { \n "
" fclose(f); \n "
" f = fopen(name, \" r \" ); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE*& f) { \n "
" f = fopen(name, \" r \" ); \n "
" f = g; \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-06-24 18:57:17 +02:00
check ( " void foo() { \n "
" FILE* f; \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Used file that is not opened. \n " , errout . str ( ) ) ;
check ( " void foo() { \n "
" FILE* f(stdout); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-07-29 13:04:12 +02:00
check ( " void foo() { \n " // #3965
" FILE* f[3]; \n "
" f[0] = fopen(name, mode); \n "
" fclose(f[0]); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-12-08 09:26:10 +01:00
// #4368: multiple functions
check ( " static FILE *fp = NULL; \n "
" \n "
" void close() \n "
" { \n "
" fclose(fp); \n "
" } \n "
" \n "
" void dump() \n "
" { \n "
" if (fp == NULL) return; \n "
" fprintf(fp, \" Here's the output. \\ n \" ); \n "
" } \n "
" \n "
" int main() \n "
" { \n "
" fp = fopen( \" test.txt \" , \" w \" ); \n "
" dump(); \n "
" close(); \n "
" return 0; \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " static FILE *fp = NULL; \n "
" \n "
" void close() \n "
" { \n "
" fclose(fp); \n "
" } \n "
" \n "
" void dump() \n "
" { \n "
" fclose(fp); \n "
" fprintf(fp, \" Here's the output. \\ n \" ); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:11]: (error) Used file that is not opened. \n " , errout . str ( ) ) ;
2013-02-15 17:30:43 +01:00
// #4466
check ( " void chdcd_parse_nero(FILE *infile) { \n "
" switch (mode) { \n "
" case 0x0300: \n "
" fclose(infile); \n "
" return; \n "
" case 0x0500: \n "
" fclose(infile); \n "
" return; \n "
" } \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2013-03-15 06:42:46 +01:00
// #4649
check ( " void foo() { \n "
" struct {FILE *f1; FILE *f2;} a; \n "
" a.f1 = fopen(name,mode); \n "
" a.f2 = fopen(name,mode); \n "
" fclose(a.f1); \n "
" fclose(a.f2); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
}
void fileIOwithoutPositioning ( ) {
check ( " void foo(FILE* f) { \n "
" fwrite(buffer, 5, 6, f); \n "
" fread(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-21 15:54:04 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-21 15:54:04 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
check ( " void foo(FILE* f, bool read) { \n "
" if(read) \n "
" fread(buffer, 5, 6, f); \n "
" else \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" fflush(f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" rewind(f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" fsetpos(f, pos); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" fseek(f, 0, SEEK_SET); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(FILE* f) { \n "
" fread(buffer, 5, 6, f); \n "
" long pos = ftell(f); \n "
" fwrite(buffer, 5, 6, f); \n "
" } " ) ;
2012-07-21 15:54:04 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour. \n " , errout . str ( ) ) ;
2012-05-22 14:30:22 +02:00
}
void fflushOnInputStream ( ) {
2012-05-20 21:26:48 +02:00
check ( " void foo() \n "
" { \n "
" fflush(stdin); \n "
2012-05-22 14:30:22 +02:00
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (error) fflush() called on input stream 'stdin' results in undefined behaviour. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " void foo() \n "
" { \n "
" fflush(stdout); \n "
2012-05-22 14:30:22 +02:00
" } " ) ;
2012-05-20 21:26:48 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
void testScanf1 ( ) {
2012-07-07 13:34:37 +02:00
check ( " void foo() { \n "
2012-05-20 21:26:48 +02:00
" int a, b; \n "
" FILE *file = fopen( \" test \" , \" r \" ); \n "
2012-07-07 13:34:37 +02:00
" a = fscanf(file, \" aa %s \" , bar); \n "
" b = scanf( \" aa %S \" , bar); \n "
" b = scanf( \" aa %ls \" , bar); \n "
" sscanf(foo, \" %[^~] \" , bar); \n "
" scanf( \" %dx%s \" , &b, bar); \n "
2012-05-20 21:26:48 +02:00
" fclose(file); \n "
" } " ) ;
2012-07-07 13:34:37 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (warning) scanf without field width limits can crash with huge input data. \n "
" [test.cpp:5]: (warning) scanf without field width limits can crash with huge input data. \n "
" [test.cpp:6]: (warning) scanf without field width limits can crash with huge input data. \n "
" [test.cpp:7]: (warning) scanf without field width limits can crash with huge input data. \n "
" [test.cpp:8]: (warning) scanf without field width limits can crash with huge input data. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
}
void testScanf2 ( ) {
2012-07-07 13:34:37 +02:00
check ( " void foo() { \n "
" scanf( \" %5s \" , bar); \n " // Width specifier given
" scanf( \" %5[^~] \" , bar); \n " // Width specifier given
" scanf( \" aa%%s \" , bar); \n " // No %s
" scanf( \" aa%d \" , &a); \n " // No %s
" scanf( \" aa%ld \" , &a); \n " // No %s
" scanf( \" %*[^~] \" ); \n " // Ignore input
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (warning) scanf format string has 0 parameters but 1 are given. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
}
void testScanf3 ( ) {
2012-07-07 13:34:37 +02:00
check ( " void foo() { \n "
" scanf( \" %d \" , &a); \n "
" scanf( \" %n \" , &a); \n " // No warning on %n, since it doesn't expect user input
" scanf( \" %c \" , &c); \n " // No warning on %c; it expects only one character
" } " , false , true , Settings : : Unspecified ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (portability) scanf without field width limits can crash with huge input data on some versions of libc. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
2012-07-07 13:34:37 +02:00
check ( " void foo() { \n "
" scanf( \" %d \" , &a); \n "
" } " , false , true , Settings : : Win32A ) ;
2012-05-20 21:26:48 +02:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2012-07-24 15:54:38 +02:00
void testScanf4 ( ) { // ticket #2553
check ( " void f() \n "
" { \n "
" char str [8]; \n "
" scanf ( \" %70s \" ,str); \n "
" } \n " ) ;
ASSERT_EQUALS ( " [test.cpp:4]: (error) Width 70 given in format string (no. 1) is larger than destination buffer 'str[8]', use %7s to prevent overflowing it. \n " , errout . str ( ) ) ;
}
2012-05-20 21:26:48 +02:00
void testScanfArgument ( ) {
check ( " void foo() { \n "
" scanf( \" %1d \" , &foo); \n "
" sscanf(bar, \" %1d \" , &foo); \n "
" scanf( \" %1u%1u \" , &foo, bar()); \n "
" scanf( \" %*1x %1x %29s \" , &count, KeyName); \n " // #3373
" fscanf(f, \" %7ms \" , &ref); \n " // #3461
2012-07-07 13:34:37 +02:00
" sscanf(ip_port, \" %*[^:]:%4d \" , &port); \n " // #3468
2012-05-20 21:26:48 +02:00
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo() { \n "
" scanf( \" \" , &foo); \n "
" scanf( \" %1d \" , &foo, &bar); \n "
" fscanf(bar, \" %1d \" , &foo, &bar); \n "
" scanf( \" %*1x %1x %29s \" , &count, KeyName, foo); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) scanf format string has 0 parameters but 1 are given. \n "
" [test.cpp:3]: (warning) scanf format string has 1 parameters but 2 are given. \n "
" [test.cpp:4]: (warning) fscanf format string has 1 parameters but 2 are given. \n "
" [test.cpp:5]: (warning) scanf format string has 2 parameters but 3 are given. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " void foo() { \n "
" scanf( \" %1d \" ); \n "
" scanf( \" %1u%1u \" , bar()); \n "
" sscanf(bar, \" %1d%1d \" , &foo); \n "
" scanf( \" %*1x %1x %29s \" , &count); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (error) scanf format string has 1 parameters but only 0 are given. \n "
" [test.cpp:3]: (error) scanf format string has 2 parameters but only 1 are given. \n "
" [test.cpp:4]: (error) sscanf format string has 2 parameters but only 1 are given. \n "
" [test.cpp:5]: (error) scanf format string has 2 parameters but only 1 are given. \n " , errout . str ( ) ) ;
2012-06-23 07:52:52 +02:00
check ( " void foo() { \n "
" char input[10]; \n "
" char output[5]; \n "
" sscanf(input, \" %3s \" , output); \n "
" sscanf(input, \" %4s \" , output); \n "
" sscanf(input, \" %5s \" , output); \n "
" } " , false ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:6]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it. \n " , errout . str ( ) ) ;
2012-06-23 07:52:52 +02:00
check ( " void foo() { \n "
" char input[10]; \n "
" char output[5]; \n "
" sscanf(input, \" %s \" , output); \n "
" sscanf(input, \" %3s \" , output); \n "
" sscanf(input, \" %4s \" , output); \n "
" sscanf(input, \" %5s \" , output); \n "
" } " , true ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:5]: (warning, inconclusive) Width 3 given in format string (no. 1) is smaller than destination buffer 'output[5]'. \n "
" [test.cpp:7]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it. \n "
2012-07-07 13:34:37 +02:00
" [test.cpp:4]: (warning) scanf without field width limits can crash with huge input data. \n " , errout . str ( ) ) ;
2012-09-10 16:23:00 +02:00
check ( " void foo() { \n "
" const size_t BUFLENGTH(2048); \n "
" typedef char bufT[BUFLENGTH]; \n "
" bufT line= {0}; \n "
" bufT projectId= {0}; \n "
" const int scanrc=sscanf(line, \" Project( \\ \" {%36s} \\ \" ) \" , projectId); \n "
" sscanf(input, \" %5s \" , output); \n "
" } " , true ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
}
void testPrintfArgument ( ) {
check ( " void foo() { \n "
" printf( \" %u \" ); \n "
" printf( \" %u%s \" , 123); \n "
" printf( \" %u%s%d \" , 0, bar()); \n "
" printf( \" %u%%%s%d \" , 0, bar()); \n "
" printf( \" %udfd%%dfa%s%d \" , 0, bar()); \n "
" fprintf(stderr, \" %u%s \" ); \n "
" snprintf(str,10, \" %u%s \" ); \n "
" sprintf(string1, \" %-*.*s \" , 32, string2); \n " // #3364
" snprintf(a, 9, \" %s%d \" , \" 11223344 \" ); \n " // #3655
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (error) printf format string has 1 parameters but only 0 are given. \n "
" [test.cpp:3]: (error) printf format string has 2 parameters but only 1 are given. \n "
" [test.cpp:4]: (error) printf format string has 3 parameters but only 2 are given. \n "
" [test.cpp:5]: (error) printf format string has 3 parameters but only 2 are given. \n "
" [test.cpp:6]: (error) printf format string has 3 parameters but only 2 are given. \n "
" [test.cpp:7]: (error) fprintf format string has 2 parameters but only 0 are given. \n "
" [test.cpp:8]: (error) snprintf format string has 2 parameters but only 0 are given. \n "
" [test.cpp:9]: (error) sprintf format string has 3 parameters but only 2 are given. \n "
" [test.cpp:10]: (error) snprintf format string has 2 parameters but only 1 are given. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " void foo(char *str) { \n "
" printf( \" \" , 0); \n "
" printf( \" %u \" , 123, bar()); \n "
" printf( \" %u%s \" , 0, bar(), 43123); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) printf format string has 0 parameters but 1 are given. \n "
" [test.cpp:3]: (warning) printf format string has 1 parameters but 2 are given. \n "
" [test.cpp:4]: (warning) printf format string has 2 parameters but 3 are given. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " void foo(char *str) { \n "
" printf( \" %u \" , 0); \n "
" printf( \" %u%s \" , 123, bar()); \n "
" printf( \" %u%s%d \" , 0, bar(), 43123); \n "
" printf( \" %u%%%s%d \" , 0, bar(), 43123); \n "
" printf( \" %udfd%%dfa%s%d \" , 0, bar(), 43123); \n "
" printf( \" % \" PRId64 \" \n \" , 123); \n "
" fprintf(stderr, \" % \" PRId64 \" \n \" , 123); \n "
" snprintf(str,10, \" % \" PRId64 \" \n \" , 123); \n "
" fprintf(stderr, \" error: %m \n \" ); \n " // #3339
" printf( \" string: %.*s \n \" , len, string); \n " // #3311
" fprintf(stderr, \" %*cText. \n \" , indent, ' '); \n " // #3313
" sprintf(string1, \" %* \" , 32); \n " // #3364
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(char* s, const char* s2, std::string s3, int i) { \n "
" printf( \" %s%s \" , s, s2); \n "
" printf( \" %s \" , i); \n "
" printf( \" %i%s \" , i, i); \n "
" printf( \" %s \" , s3); \n "
" printf( \" %s \" , \" s4 \" ); \n "
" printf( \" %u \" , s); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %s in format string (no. 1) requires a char* given in the argument list. \n "
" [test.cpp:4]: (warning) %s in format string (no. 2) requires a char* given in the argument list. \n "
" [test.cpp:5]: (warning) %s in format string (no. 1) requires a char* given in the argument list. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " void foo(const int* cpi, const int ci, int i, int* pi, std::string s) { \n "
" printf( \" %n \" , cpi); \n "
" printf( \" %n \" , ci); \n "
" printf( \" %n \" , i); \n "
" printf( \" %n \" , pi); \n "
" printf( \" %n \" , s); \n "
" printf( \" %n \" , \" s4 \" ); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:2]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list. \n "
" [test.cpp:3]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list. \n "
" [test.cpp:4]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list. \n "
" [test.cpp:6]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list. \n "
" [test.cpp:7]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " class foo {}; \n "
2012-07-11 19:46:35 +02:00
" void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) { \n "
" printf( \" %X \" , f); \n "
2012-05-20 21:26:48 +02:00
" printf( \" %c \" , \" s4 \" ); \n "
" printf( \" %o \" , d); \n "
2012-07-11 19:46:35 +02:00
" printf( \" %x \" , cpi); \n "
" printf( \" %o \" , b); \n "
" printf( \" %X \" , bp); \n "
" printf( \" %X \" , u); \n "
" printf( \" %X \" , i); \n "
2012-05-20 21:26:48 +02:00
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %X in format string (no. 1) requires an integer given in the argument list. \n "
" [test.cpp:4]: (warning) %c in format string (no. 1) requires an integer given in the argument list. \n "
" [test.cpp:5]: (warning) %o in format string (no. 1) requires an integer given in the argument list. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
2012-07-11 19:46:35 +02:00
check ( " class foo {}; \n "
" void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) { \n "
" printf( \" %i \" , f); \n "
" printf( \" %d \" , \" s4 \" ); \n "
" printf( \" %d \" , d); \n "
" printf( \" %d \" , u); \n "
" printf( \" %d \" , cpi); \n "
" printf( \" %i \" , b); \n "
" printf( \" %i \" , bp); \n "
" printf( \" %i \" , uc); \n " // char is smaller than int, so there shouldn't be a problem
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %i in format string (no. 1) requires a signed integer given in the argument list. \n "
" [test.cpp:4]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list. \n "
" [test.cpp:5]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list. \n "
" [test.cpp:6]: (warning) %d in format string (no. 1) requires a signed integer given in the argument list. \n " , errout . str ( ) ) ;
2012-07-11 19:46:35 +02:00
check ( " class foo {}; \n "
" void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) { \n "
" printf( \" %u \" , f); \n "
" printf( \" %u \" , \" s4 \" ); \n "
" printf( \" %u \" , d); \n "
" printf( \" %u \" , i); \n "
" printf( \" %u \" , cpi); \n "
" printf( \" %u \" , b); \n "
" printf( \" %u \" , bp); \n "
" printf( \" %u \" , bo); \n " // bool shouldn't have a negative sign
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list. \n "
" [test.cpp:4]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list. \n "
" [test.cpp:5]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list. \n "
" [test.cpp:6]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list. \n " , errout . str ( ) ) ;
2012-07-11 19:46:35 +02:00
2012-05-20 21:26:48 +02:00
check ( " class foo {}; \n "
" void foo(const int* cpi, foo f, bar b, bar* bp, char c) { \n "
" printf( \" %p \" , f); \n "
" printf( \" %p \" , c); \n "
" printf( \" %p \" , bp); \n "
" printf( \" %p \" , cpi); \n "
" printf( \" %p \" , b); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %p in format string (no. 1) requires an address given in the argument list. \n "
" [test.cpp:4]: (warning) %p in format string (no. 1) requires an address given in the argument list. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " class foo {}; \n "
" void foo(const int* cpi, foo f, bar b, bar* bp, double d) { \n "
" printf( \" %e \" , f); \n "
" printf( \" %E \" , \" s4 \" ); \n "
" printf( \" %f \" , cpi); \n "
" printf( \" %G \" , bp); \n "
" printf( \" %f \" , d); \n "
" printf( \" %f \" , b); \n "
" printf( \" %f \" , (float)cpi); \n "
" } " ) ;
2012-07-13 19:36:58 +02:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %e in format string (no. 1) requires a floating point number given in the argument list. \n "
" [test.cpp:4]: (warning) %E in format string (no. 1) requires a floating point number given in the argument list. \n "
" [test.cpp:5]: (warning) %f in format string (no. 1) requires a floating point number given in the argument list. \n "
" [test.cpp:6]: (warning) %G in format string (no. 1) requires a floating point number given in the argument list. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
check ( " class foo; \n "
" void foo(foo f) { \n "
" printf( \" %u \" , f); \n "
" printf( \" %f \" , f); \n "
" printf( \" %p \" , f); \n "
" } " ) ;
2012-11-30 06:03:58 +01:00
ASSERT_EQUALS ( " [test.cpp:3]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list. \n "
" [test.cpp:4]: (warning) %f in format string (no. 1) requires a floating point number given in the argument list. \n "
" [test.cpp:5]: (warning) %p in format string (no. 1) requires an address given in the argument list. \n " , errout . str ( ) ) ;
2012-10-21 08:50:29 +02:00
// Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7)
// False positive tests
check ( " void foo(signed char sc, unsigned char uc, short int si, unsigned short int usi) { \n "
" printf( \" %hhx %hhd \" , sc, uc); \n "
" printf( \" %hd %hi \" , si, usi); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) { \n "
" printf( \" %llo %llx \" , lli, ulli); \n "
" printf( \" %ld %lu \" , li, uli); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld) { \n "
" printf( \" %jd %jo \" , im, uim); \n "
" printf( \" %zx \" , s); \n "
" printf( \" %ti \" , p); \n "
" printf( \" %La \" , ld); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
// False negative test
check ( " void foo(unsigned int i) { \n "
" printf( \" %h \" , i); \n "
" printf( \" %hh \" , i); \n "
" printf( \" %l \" , i); \n "
" printf( \" %ll \" , i); \n "
" printf( \" %j \" , i); \n "
" printf( \" %z \" , i); \n "
" printf( \" %t \" , i); \n "
" printf( \" %L \" , i); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n "
" [test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier. \n " , errout . str ( ) ) ;
2012-05-20 21:26:48 +02:00
}
} ;
REGISTER_TEST ( TestIO )