Skip to content

Commit 5523c33

Browse files
fixup! fixup! fixup! Fix #958: warn when feof() is used as a while loop condition
1 parent 368df3b commit 5523c33

3 files changed

Lines changed: 40 additions & 16 deletions

File tree

lib/checkio.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,20 @@ void CheckIO::invalidScanfError(const Token *tok)
481481
CWE119, Certainty::normal);
482482
}
483483

484+
static bool findFileReadCall(const Token *start, const Token *end, int varid)
485+
{
486+
const Token* found = Token::findmatch(start, "fgets|fgetc|getc|fread|fscanf (", end);
487+
const std::vector<const Token*> args = getArguments(found);
488+
if (args.empty())
489+
return false;
490+
491+
if (found->str() == "fscanf") {
492+
return args.front()->varId() == varid;
493+
} else {
494+
return args.back()->varId() == varid;
495+
}
496+
}
497+
484498
void CheckIO::checkWrongfeofUsage()
485499
{
486500
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
@@ -489,14 +503,17 @@ void CheckIO::checkWrongfeofUsage()
489503

490504
for (const Scope * scope : symbolDatabase->functionScopes) {
491505
for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
492-
if (Token::simpleMatch(tok, "while (")) {
493-
const Token* cond = getCondTok(tok);
494-
if (!cond)
495-
continue;
496-
if (Token::simpleMatch(cond, "! feof (")) {
497-
wrongfeofUsage(cond);
498-
}
499-
}
506+
if (!Token::Match(tok, "while ( ! feof ("))
507+
continue;
508+
509+
// Usage of feof is correct if a read happens before and within the loop
510+
const Token *endCond = tok->linkAt(1);
511+
const Token *endBody = endCond->linkAt(1);
512+
const int fpVarId = tok->tokAt(5)->varId();
513+
if (findFileReadCall(scope->bodyStart, tok, fpVarId) && findFileReadCall(tok, endBody, fpVarId))
514+
continue;
515+
516+
wrongfeofUsage(getCondTok(tok));
500517
}
501518
}
502519
}
@@ -505,7 +522,7 @@ void CheckIO::wrongfeofUsage(const Token * tok)
505522
{
506523
reportError(tok, Severity::warning,
507524
"wrongfeofUsage",
508-
"Using feof() as a loop condition may cause the last line to be processed twice.\n"
525+
"Using feof() as a loop condition causes the last line to be processed twice.\n"
509526
"feof() returns true only after a read has failed due to end-of-file, so the loop "
510527
"body executes once more after the last successful read. Check the return value of "
511528
"the read function instead (e.g. fgets, fread, fscanf).");

releasenotes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Major bug fixes & crashes:
66

77
New checks:
88
- MISRA C 2012 rule 10.3 now warns on assigning integer literals 0 and 1 to bool in C99 and later while preserving the existing C89 behavior.
9+
- Warn when feof() is used as a while loop condition (wrongfeofUsage).
910

1011
C/C++ support:
1112
-

test/testio.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -745,27 +745,33 @@ class TestIO : public TestFixture {
745745
}
746746

747747
void testWrongfeofUsage() { // ticket #958
748-
check("void foo() {\n"
749-
" FILE * fp = fopen(\"test.txt\", \"r\");\n"
748+
check("void foo(FILE * fp) {\n"
750749
" while (!feof(fp)) \n"
751750
" {\n"
752751
" char line[100];\n"
753752
" fgets(line, sizeof(line), fp);\n"
754753
" }\n"
755-
" fclose(fp);\n"
756754
"}");
757-
ASSERT_EQUALS("[test.cpp:3:10]: (warning) Using feof() as a loop condition may cause the last line to be processed twice. [wrongfeofUsage]\n", errout_str());
755+
ASSERT_EQUALS("[test.cpp:2:10]: (warning) Using feof() as a loop condition causes the last line to be processed twice. [wrongfeofUsage]\n", errout_str());
758756

759-
check("int foo() {\n"
760-
" FILE * fp = fopen(\"test.txt\", \"r\");\n"
757+
check("int foo(FILE *fp) {\n"
761758
" char line[100];\n"
762759
" while (fgets(line, sizeof(line), fp)) {}\n"
763760
" if (!feof(fp))\n"
764761
" return 1;\n"
765-
" fclose(fp);\n"
766762
" return 0;\n"
767763
"}");
768764
ASSERT_EQUALS("", errout_str());
765+
766+
check("void foo(FILE *fp){\n"
767+
" char line[100];\n"
768+
" fgets(line, sizeof(line), fp);\n"
769+
" while (!feof(fp)){\n"
770+
" dostuff(line);\n"
771+
" fgets(line, sizeof(line), fp);"
772+
" }\n"
773+
"}");
774+
ASSERT_EQUALS("", errout_str());
769775
}
770776

771777

0 commit comments

Comments
 (0)