You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix#958: warn when feof() is used as a while loop condition (#8422)
feof() only returns true after a read has already failed, causing the
loop body to execute once more after the last successful read. Read
errors also go undetected since feof() does not distinguish them from
EOF.
---------
Signed-off-by: Francois Berder <fberder@outlook.fr>
**Message**: Using feof() as a loop condition causes the last line to be processed twice.<br/>
4
+
**Category**: Correctness<br/>
5
+
**Severity**: Warning<br/>
6
+
**Language**: C/C++
7
+
8
+
## Description
9
+
10
+
`feof()` returns non-zero only after a read operation has failed because the end of file was reached. When used as the sole condition of a loop, the loop body executes one extra time after the last successful read: the read fails silently (or returns partial data), and only then does `feof()` return true and terminate the loop.
11
+
12
+
This checker matches `while (!feof(fp))` and `do { ... } while (!feof(fp))` loops and warns when all of the following are true:
13
+
14
+
- The loop body contains at least one file-read call (`fgets`, `fgetc`, `getc`, `fread`, or `fscanf`) on the same file pointer.
15
+
- The destination (return value or output buffer) of the **last** file-read call in the loop is used after that call within the same loop iteration.
16
+
17
+
The checker skips loops that contain a control-flow statement (`return`, `break`, `goto`, `continue`, `throw`) as those are too complex to analyze reliably, and loops where the file pointer appears in a context other than a recognised I/O function (`fgets`, `fgetc`, `getc`, `fread`, `fscanf`, `fprintf`, `fwrite`, `fputs`, `fputc`, `putc`).
18
+
19
+
## How to fix
20
+
21
+
Check the return value of the read function directly in the loop condition.
22
+
23
+
Before:
24
+
```c
25
+
voidprocess(FILE *fp) {
26
+
char line[256];
27
+
while (!feof(fp)) { /* wrong: processes last line twice */
Copy file name to clipboardExpand all lines: test/testio.cpp
+50Lines changed: 50 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -45,6 +45,7 @@ class TestIO : public TestFixture {
45
45
TEST_CASE(seekOnAppendedFile);
46
46
TEST_CASE(fflushOnInputStream);
47
47
TEST_CASE(incompatibleFileOpen);
48
+
TEST_CASE(testWrongfeofUsage); // #958
48
49
49
50
TEST_CASE(testScanf1); // Scanf without field limiters
50
51
TEST_CASE(testScanf2);
@@ -766,6 +767,55 @@ class TestIO : public TestFixture {
766
767
ASSERT_EQUALS("[test.cpp:3:16]: (warning) The file '\"tmp\"' is opened for read and write access at the same time on different streams [incompatibleFileOpen]\n", errout_str());
767
768
}
768
769
770
+
voidtestWrongfeofUsage() { // ticket #958
771
+
check("void foo(FILE * fp) {\n"
772
+
" while (!feof(fp)) \n"
773
+
" {\n"
774
+
" char line[100];\n"
775
+
" fgets(line, sizeof(line), fp);\n"
776
+
" dostuff(line);\n"
777
+
" }\n"
778
+
"}");
779
+
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());
780
+
781
+
check("int foo(FILE *fp) {\n"
782
+
" char line[100];\n"
783
+
" while (fgets(line, sizeof(line), fp)) {}\n"
784
+
" if (!feof(fp))\n"
785
+
" return 1;\n"
786
+
" return 0;\n"
787
+
"}");
788
+
ASSERT_EQUALS("", errout_str());
789
+
790
+
check("void foo(FILE *fp){\n"
791
+
" char line[100];\n"
792
+
" fgets(line, sizeof(line), fp);\n"
793
+
" while (!feof(fp)){\n"
794
+
" dostuff(line);\n"
795
+
" fgets(line, sizeof(line), fp);"
796
+
" }\n"
797
+
"}");
798
+
ASSERT_EQUALS("", errout_str());
799
+
800
+
check("void foo(FILE *fp) {\n"
801
+
" char line[100];\n"
802
+
" do {\n"
803
+
" fgets(line, sizeof(line), fp);\n"
804
+
" dostuff(line);\n"
805
+
" } while (!feof(fp));\n"
806
+
"}");
807
+
ASSERT_EQUALS("[test.cpp:6:12]: (warning) Using feof() as a loop condition causes the last line to be processed twice. [wrongfeofUsage]\n", errout_str());
0 commit comments