From 5fa3789a4e6b165954262c30bb4cdd41cd7ea11d Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Sun, 12 Apr 2026 23:34:12 +0200 Subject: [PATCH 1/4] Fix #1473: warn when fclose() is used as a while loop condition 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 --- lib/checkio.cpp | 13 +++++++++++++ test/testio.cpp | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 0e1d0395146..bf5d4bf8200 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -245,6 +245,19 @@ void CheckIO::checkFileUsage() } else if (tok->str() == "fclose") { fileTok = tok->tokAt(2); operation = Filepointer::Operation::CLOSE; + + // #1473 Check if fclose is in a while loop condition + const Token* tmp = tok->astParent(); + const Token* loopTok = nullptr; + while (tmp) { + if (Token::simpleMatch(tmp->previous(), "while (")) { + loopTok = tmp->previous(); + break; + } + tmp = tmp->astParent(); + } + if (loopTok) + useClosedFileError(tok); } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) diff --git a/test/testio.cpp b/test/testio.cpp index ec6a87a1e16..6c7ddbe244c 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -544,7 +544,7 @@ class TestIO : public TestFixture { " FILE *a = fopen(\"aa\", \"r\");\n" " while (fclose(a)) {}\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:3:5]: (error) Used file that is not opened. [useClosedFile]\n", "", errout_str()); + ASSERT_EQUALS("[test.cpp:3:12]: (error) Used file that is not opened. [useClosedFile]\n", errout_str()); // #6823 check("void foo() {\n" From 969864ca984737df20f88e6e95086235aaae2a12 Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Mon, 27 Apr 2026 21:49:57 +0200 Subject: [PATCH 2/4] fixup! Fix #1473: warn when fclose() is used as a while loop condition --- lib/checkio.cpp | 26 +++++++++++++++++--------- test/testio.cpp | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index bf5d4bf8200..5543f8999fd 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -247,17 +247,25 @@ void CheckIO::checkFileUsage() operation = Filepointer::Operation::CLOSE; // #1473 Check if fclose is in a while loop condition - const Token* tmp = tok->astParent(); - const Token* loopTok = nullptr; - while (tmp) { - if (Token::simpleMatch(tmp->previous(), "while (")) { - loopTok = tmp->previous(); - break; + if (fileTok) { + const Token* tmp = tok->astParent(); + const Token* loopTok = nullptr; + while (tmp) { + if (Token::simpleMatch(tmp->previous(), "while (")) { + loopTok = tmp->previous(); + break; + } + tmp = tmp->astParent(); + } + if (loopTok) { + const Token* bodyStart = loopTok->linkAt(1)->next(); + const Token* bodyEnd = bodyStart->link(); + + // Do not trigger a warning if the loop always exits or if the file is opened again in the loop. + if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% = fopen|freopen", bodyEnd, fileTok->varId()) == nullptr) + useClosedFileError(tok); } - tmp = tmp->astParent(); } - if (loopTok) - useClosedFileError(tok); } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) diff --git a/test/testio.cpp b/test/testio.cpp index 6c7ddbe244c..0514b0eb64f 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -546,6 +546,22 @@ class TestIO : public TestFixture { "}"); ASSERT_EQUALS("[test.cpp:3:12]: (error) Used file that is not opened. [useClosedFile]\n", errout_str()); + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " while (fclose(a)) {\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " while (fclose(a)) {\n" + " a = fopen(\"aa\", \"r\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + // #6823 check("void foo() {\n" " FILE f[2];\n" From a81456cc1858fd2ccbfb4a751109b9ba0804007c Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Mon, 27 Apr 2026 22:33:56 +0200 Subject: [PATCH 3/4] fixup! fixup! Fix #1473: warn when fclose() is used as a while loop condition --- lib/checkio.cpp | 12 ++++++++++-- test/testio.cpp | 6 ++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 5543f8999fd..009bcad6df3 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -258,8 +258,16 @@ void CheckIO::checkFileUsage() tmp = tmp->astParent(); } if (loopTok) { - const Token* bodyStart = loopTok->linkAt(1)->next(); - const Token* bodyEnd = bodyStart->link(); + const Token* bodyEnd = nullptr; + const Token* bodyStart = nullptr; + + if (Token::simpleMatch(loopTok->previous(), "}")) { // Handle do-while loops + bodyEnd = loopTok->previous(); + bodyStart = bodyEnd->link(); + } else { + bodyStart = loopTok->linkAt(1)->next(); + bodyEnd = bodyStart->link(); + } // Do not trigger a warning if the loop always exits or if the file is opened again in the loop. if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% = fopen|freopen", bodyEnd, fileTok->varId()) == nullptr) diff --git a/test/testio.cpp b/test/testio.cpp index 0514b0eb64f..7cfc657180d 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -562,6 +562,12 @@ class TestIO : public TestFixture { "}"); ASSERT_EQUALS("", errout_str()); + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " do {} while (fclose(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3:18]: (error) Used file that is not opened. [useClosedFile]\n", errout_str()); + // #6823 check("void foo() {\n" " FILE f[2];\n" From 93f68cf8b4bd333f4e3e1740a7a6f70b927f998c Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Tue, 28 Apr 2026 10:31:03 +0200 Subject: [PATCH 4/4] fixup! fixup! fixup! Fix #1473: warn when fclose() is used as a while loop condition --- lib/checkio.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 009bcad6df3..683f591e810 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -247,7 +247,7 @@ void CheckIO::checkFileUsage() operation = Filepointer::Operation::CLOSE; // #1473 Check if fclose is in a while loop condition - if (fileTok) { + if (fileTok && fileTok->isVariable()) { const Token* tmp = tok->astParent(); const Token* loopTok = nullptr; while (tmp) { @@ -261,7 +261,7 @@ void CheckIO::checkFileUsage() const Token* bodyEnd = nullptr; const Token* bodyStart = nullptr; - if (Token::simpleMatch(loopTok->previous(), "}")) { // Handle do-while loops + if (Token::simpleMatch(loopTok->previous(), "}") && Token::simpleMatch(loopTok->previous()->link()->previous(), "do")) { // Handle do-while loops bodyEnd = loopTok->previous(); bodyStart = bodyEnd->link(); } else { @@ -270,7 +270,7 @@ void CheckIO::checkFileUsage() } // Do not trigger a warning if the loop always exits or if the file is opened again in the loop. - if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% = fopen|freopen", bodyEnd, fileTok->varId()) == nullptr) + if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% =", bodyEnd, fileTok->varId()) == nullptr) useClosedFileError(tok); } }