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#1473: warn when fclose() is used as a while loop condition (#8444)
Using fclose() as a while loop condition closes the file on every
iteration and operates on an already-closed file handle from the second
iteration onward.
---------
Signed-off-by: Francois Berder <fberder@outlook.fr>
"fclose() used as loop condition may skip loop body or double-close file handle.\n"
417
+
"fclose() closes '" + varname + "' each time it is evaluated. On success the loop body might never execute, on failure fclose() might be called again on the already-closed file handle.",
**Message**: fclose() used as loop condition may skip loop body or double-close file handle.<br/>
4
+
**Category**: Resource Management<br/>
5
+
**Severity**: Warning<br/>
6
+
**Language**: C and C++
7
+
8
+
## Description
9
+
10
+
Using `fclose()` as a loop condition leads to two unwanted outcomes:
11
+
12
+
- On **success**, the condition is false and the loop body never executes. The intent was likely to process the file inside the loop, but it is already closed.
13
+
- On **failure**, the condition is true and the loop body executes but `fclose()` is called again on the already-closed file handle on the next iteration, which is undefined behaviour.
14
+
15
+
This pattern is almost always a misunderstanding of what `fclose()` returns, or confusion with a function that reads/processes data incrementally (like `fgets` or `fread`). Unlike those functions, `fclose()` is a one-shot teardown operation and has no meaningful retry or loop-until-done semantic.
16
+
17
+
## How to fix
18
+
19
+
Call `fclose()` outside the loop condition. If you need to check whether the close succeeded, store the return value and test it separately.
20
+
21
+
Before:
22
+
```c
23
+
FILE *fp = fopen("data.txt", "r");
24
+
while (fclose(fp)) {
25
+
/* process file */
26
+
}
27
+
```
28
+
29
+
After:
30
+
```c
31
+
FILE *fp = fopen("data.txt", "r");
32
+
/* process file */
33
+
if (fclose(fp) != 0) {
34
+
/* handle close error */
35
+
}
36
+
```
37
+
38
+
## Related checkers
39
+
40
+
-`useClosedFile` - for using a file handle that has already been closed
Copy file name to clipboardExpand all lines: test/testio.cpp
+23-1Lines changed: 23 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -545,7 +545,29 @@ class TestIO : public TestFixture {
545
545
" FILE *a = fopen(\"aa\", \"r\");\n"
546
546
" while (fclose(a)) {}\n"
547
547
"}");
548
-
TODO_ASSERT_EQUALS("[test.cpp:3:5]: (error) Used file that is not opened. [useClosedFile]\n", "", errout_str());
548
+
ASSERT_EQUALS("[test.cpp:3:12]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str());
549
+
550
+
check("void foo() {\n"
551
+
" FILE *a = fopen(\"aa\", \"r\");\n"
552
+
" while (fclose(a)) {\n"
553
+
" break;\n"
554
+
" }\n"
555
+
"}");
556
+
ASSERT_EQUALS("", errout_str());
557
+
558
+
check("void foo() {\n"
559
+
" FILE *a = fopen(\"aa\", \"r\");\n"
560
+
" while (fclose(a)) {\n"
561
+
" a = fopen(\"aa\", \"r\");\n"
562
+
" }\n"
563
+
"}");
564
+
ASSERT_EQUALS("", errout_str());
565
+
566
+
check("void foo() {\n"
567
+
" FILE *a = fopen(\"aa\", \"r\");\n"
568
+
" do {} while (fclose(a));\n"
569
+
"}");
570
+
ASSERT_EQUALS("[test.cpp:3:18]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str());
0 commit comments