cppcheck/test/testio.cpp

750 lines
33 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2013 Daniel Marjamäki and 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 <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);
TEST_CASE(wrongMode_simple);
TEST_CASE(wrongMode_complex);
TEST_CASE(useClosedFile);
TEST_CASE(fileIOwithoutPositioning);
TEST_CASE(fflushOnInputStream);
TEST_CASE(testScanf1); // Scanf without field limiters
TEST_CASE(testScanf2);
TEST_CASE(testScanf3);
TEST_CASE(testScanf4); // #ticket 2553
TEST_CASE(testScanfArgument);
TEST_CASE(testPrintfArgument);
}
void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified) {
// Clear the error buffer..
errout.str("");
Settings settings;
settings.addEnabled("warning");
settings.addEnabled("style");
if (portability)
settings.addEnabled("portability");
settings.inconclusive = inconclusive;
settings.platform(platform);
// 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();
checkIO.checkFileUsage();
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());
}
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"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str());
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"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str());
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"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str());
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"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str());
// 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"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str());
// freopen and tmpfile
check("void foo(FILE*& f) {\n"
" f = freopen(name, \"r\", f);\n"
" fwrite(buffer, 5, 6, f);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str());
// Crash tests
check("void foo(FILE*& f) {\n"
" f = fopen(name, mode);\n" // No assertion failure (#3830)
" fwrite(buffer, 5, 6, f);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void fopen(std::string const &filepath, std::string const &mode);"); // #3832
}
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"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Read operation on a file that was opened only for writing.\n", errout.str());
}
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());
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());
check("void foo() {\n" // #3965
" FILE* f[3];\n"
" f[0] = fopen(name, mode);\n"
" fclose(f[0]);\n"
"}");
ASSERT_EQUALS("", errout.str());
// #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());
// #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());
// #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());
}
void fileIOwithoutPositioning() {
check("void foo(FILE* f) {\n"
" fwrite(buffer, 5, 6, f);\n"
" fread(buffer, 5, 6, f);\n"
"}");
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());
check("void foo(FILE* f) {\n"
" fread(buffer, 5, 6, f);\n"
" fwrite(buffer, 5, 6, f);\n"
"}");
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());
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"
"}");
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());
}
void fflushOnInputStream() {
check("void foo()\n"
"{\n"
" fflush(stdin);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) fflush() called on input stream 'stdin' results in undefined behaviour.\n", errout.str());
check("void foo()\n"
"{\n"
" fflush(stdout);\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void testScanf1() {
check("void foo() {\n"
" int a, b;\n"
" FILE *file = fopen(\"test\", \"r\");\n"
" 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"
" fclose(file);\n"
"}");
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());
}
void testScanf2() {
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
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string has 0 parameters but 1 are given.\n", errout.str());
}
void testScanf3() {
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());
check("void foo() {\n"
" scanf(\"%d\", &a);\n"
"}", false, true, Settings::Win32A);
ASSERT_EQUALS("", errout.str());
}
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());
}
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
" sscanf(ip_port, \"%*[^:]:%4d\", &port);\n" // #3468
"}");
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"
"}");
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());
check("void foo() {\n"
" scanf(\"%1d\");\n"
" scanf(\"%1u%1u\", bar());\n"
" sscanf(bar, \"%1d%1d\", &foo);\n"
" scanf(\"%*1x %1x %29s\", &count);\n"
"}");
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());
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);
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());
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);
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"
"[test.cpp:4]: (warning) scanf without field width limits can crash with huge input data.\n", errout.str());
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());
}
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
"}");
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());
check("void foo(char *str) {\n"
" printf(\"\", 0);\n"
" printf(\"%u\", 123, bar());\n"
" printf(\"%u%s\", 0, bar(), 43123);\n"
"}");
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());
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"
"}");
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());
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"
"}");
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());
check("class foo {};\n"
"void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) {\n"
" printf(\"%X\", f);\n"
" printf(\"%c\", \"s4\");\n"
" printf(\"%o\", d);\n"
" printf(\"%x\", cpi);\n"
" printf(\"%o\", b);\n"
" printf(\"%X\", bp);\n"
" printf(\"%X\", u);\n"
" printf(\"%X\", i);\n"
"}");
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());
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
"}");
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());
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
"}");
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());
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"
"}");
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());
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"
"}");
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());
check("class foo;\n"
"void foo(foo f) {\n"
" printf(\"%u\", f);\n"
" printf(\"%f\", f);\n"
" printf(\"%p\", f);\n"
"}");
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());
// 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());
}
};
REGISTER_TEST(TestIO)