summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <[email protected]>2024-03-22 19:06:08 +0100
committerGiuseppe D'Angelo <[email protected]>2024-04-17 20:07:47 +0200
commit7466831509fe163f3fd1e3a6bbf38f6f5a32ef00 (patch)
treec6c84e1f2762ba2d003a1eb017f12b8bdb6c0907
parent067e3ec1bf3f18a1ca79f7241241d3cf021c3715 (diff)
Long live [[nodiscard]] QFile::open
Having already caught some bugs in real code because of unchecked calls to QFile::open, this commit marks QFile::open (and open() in other file-I/O classes) as [[nodiscard]]. Since it's going to raise warnings, the plan is to keep the existing behavior up to and including the next LTS. Then the warnings will switch on by default. All of this is protected by system of macros to opt-in or opt-out the behavioral change at any time. A possible counter-argument for doing this is that QFile::open is also used for opening files in the the resource system, and that opening "cannot fail". It clearly can, if the resource is moved away or renamed; code should at a minimum use a Q_ASSERT in debug builds. Another counter-argument is the opening of file handles or descriptors; but again, that opening may fail in case the handle has been closed or if the flags are incompatible. --- Why not marking *every* open() override? Because some are not meant to be called directly -- for instance sockets are supposed to be open via calls to `connectToHost` or similar. One notable exception is QIODevice::open() itself. Although rarely called directly by user code (which just calls open() on a specific subclass, which likely has an override), it may be called: 1) By code that just takes a `QIODevice *` and does something with it. That code is arguably more rare than code using QFile directly. Still, being "generic" code, they have an extra responsibility when making sure to handle a possible opening failure. 2) By QIODevice subclasses, which are even more rare. However, they usually ignore the return from QIODevice::open() as it's unconditionally true. (QIODevice::open() doesn't use the protected virtual pattern.) I'll try and tackle QIODevice in a future commit. [ChangeLog][QtCore][QFileDevice] The open() functions of file-related I/O classes (such as QFile, QSaveFile, QTemporaryFile) can now be marked with the "nodiscard" attribute, in order to prevent a category of bugs where the return value of open() is not checked and the file is then used. In order to avoid warnings in existing code, the marking can be opted in or out, by defining QT_USE_NODISCARD_FILE_OPEN or the QT_NO_USE_NODISCARD_FILE_OPEN macros. By default, Qt will automatically enable nodiscard on these functions starting from Qt 6.10. Change-Id: Ied940e1c0a37344f5200b2c51b05cd1afcb2557d Reviewed-by: Thiago Macieira <[email protected]> Reviewed-by: Edward Welbourne <[email protected]>
-rw-r--r--src/concurrent/CMakeLists.txt1
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/global/qglobal.cpp1
-rw-r--r--src/corelib/global/qtconfigmacros.h3
-rw-r--r--src/corelib/io/qfile.cpp8
-rw-r--r--src/corelib/io/qfile.h8
-rw-r--r--src/corelib/io/qfiledevice.cpp29
-rw-r--r--src/corelib/io/qfiledevice.h16
-rw-r--r--src/corelib/io/qsavefile.cpp2
-rw-r--r--src/corelib/io/qsavefile.h2
-rw-r--r--src/corelib/io/qtemporaryfile.cpp2
-rw-r--r--src/corelib/io/qtemporaryfile.h2
-rw-r--r--src/dbus/CMakeLists.txt1
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/opengl/CMakeLists.txt1
-rw-r--r--src/openglwidgets/CMakeLists.txt1
-rw-r--r--src/printsupport/CMakeLists.txt1
-rw-r--r--src/sql/CMakeLists.txt1
-rw-r--r--src/testlib/CMakeLists.txt1
-rw-r--r--src/tools/moc/CMakeLists.txt1
-rw-r--r--src/tools/qlalr/CMakeLists.txt1
-rw-r--r--src/tools/rcc/CMakeLists.txt1
-rw-r--r--src/tools/uic/CMakeLists.txt1
-rw-r--r--src/widgets/CMakeLists.txt1
-rw-r--r--src/xml/CMakeLists.txt1
26 files changed, 78 insertions, 11 deletions
diff --git a/src/concurrent/CMakeLists.txt b/src/concurrent/CMakeLists.txt
index 7dfc9e911e6..504f8545345 100644
--- a/src/concurrent/CMakeLists.txt
+++ b/src/concurrent/CMakeLists.txt
@@ -28,6 +28,7 @@ qt_internal_add_module(Concurrent
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index c9e788054f9..8c2f62ef03b 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -323,6 +323,7 @@ qt_internal_add_module(Core
QT_NO_QPAIR
QT_NO_USING_NAMESPACE
QT_TYPESAFE_FLAGS
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_BINARY_DIR}/global"
"${CMAKE_CURRENT_BINARY_DIR}/kernel" # for moc_qobject.cpp to be found by qobject.cpp
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index eb06a950146..222c008f8a2 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -346,6 +346,7 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
\row \li 6.6.0 \li The qExchange() function (see \l{QT_NO_QEXCHANGE})
\row \li 6.7.0 \li Overloads of QObject::connect that do not take a context object (see \l{QT_NO_CONTEXTLESS_CONNECT})
\row \li 6.8.0 \li The qAsConst() function (see \l{QT_NO_QASCONST})
+ \row \li 6.8.0 \li File-related I/O classes have their \c{open()} functions marked \c{[[nodiscard]]} (see \l{QT_USE_NODISCARD_FILE_OPEN})
\endtable
Moreover, individual APIs may also get disabled as part of the
diff --git a/src/corelib/global/qtconfigmacros.h b/src/corelib/global/qtconfigmacros.h
index ee7ebca52bd..018161eac4c 100644
--- a/src/corelib/global/qtconfigmacros.h
+++ b/src/corelib/global/qtconfigmacros.h
@@ -199,6 +199,9 @@ namespace QT_NAMESPACE {}
#if QT_ENABLE_STRICT_MODE_UP_TO >= QT_VERSION_CHECK(6, 8, 0)
# define QT_NO_QASCONST
+# if !defined(QT_USE_NODISCARD_FILE_OPEN) && !defined(QT_NO_USE_NODISCARD_FILE_OPEN)
+# define QT_USE_NODISCARD_FILE_OPEN
+# endif
#endif // 6.8.0
#endif // QT_ENABLE_STRICT_MODE_UP_TO
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
index 6da428f58c4..52188dde516 100644
--- a/src/corelib/io/qfile.cpp
+++ b/src/corelib/io/qfile.cpp
@@ -897,6 +897,8 @@ QFile::copy(const QString &fileName, const QString &newName)
of the file name, otherwise, it won't be possible to create this
non-existing file.
+ \sa QT_USE_NODISCARD_FILE_OPEN
+
\sa QIODevice::OpenMode, setFileName()
*/
bool QFile::open(OpenMode mode)
@@ -941,7 +943,7 @@ bool QFile::open(OpenMode mode)
such permissions will generate warnings when the Security tab of the Properties dialog
is opened. Granting the group all permissions granted to others avoids such warnings.
- \sa QIODevice::OpenMode, setFileName()
+ \sa QIODevice::OpenMode, setFileName(), QT_USE_NODISCARD_FILE_OPEN
\since 6.3
*/
bool QFile::open(OpenMode mode, QFile::Permissions permissions)
@@ -998,7 +1000,7 @@ bool QFile::open(OpenMode mode, QFile::Permissions permissions)
you cannot use this QFile with a QFileInfo.
\endlist
- \sa close()
+ \sa close(), QT_USE_NODISCARD_FILE_OPEN
\b{Note for the Windows Platform}
@@ -1064,7 +1066,7 @@ bool QFile::open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
\warning Since this function opens the file without specifying the file name,
you cannot use this QFile with a QFileInfo.
- \sa close()
+ \sa close(), QT_USE_NODISCARD_FILE_OPEN
*/
bool QFile::open(int fd, OpenMode mode, FileHandleFlags handleFlags)
{
diff --git a/src/corelib/io/qfile.h b/src/corelib/io/qfile.h
index 1d18dd55c03..058b2fa2363 100644
--- a/src/corelib/io/qfile.h
+++ b/src/corelib/io/qfile.h
@@ -282,10 +282,10 @@ public:
}
#endif // QT_CONFIG(cxx17_filesystem)
- bool open(OpenMode flags) override;
- bool open(OpenMode flags, Permissions permissions);
- bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);
- bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);
+ QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override;
+ QFILE_MAYBE_NODISCARD bool open(OpenMode flags, Permissions permissions);
+ QFILE_MAYBE_NODISCARD bool open(FILE *f, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);
+ QFILE_MAYBE_NODISCARD bool open(int fd, OpenMode ioFlags, FileHandleFlags handleFlags=DontCloseHandle);
qint64 size() const override;
diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp
index d3b493a1ccb..431dc65f5b8 100644
--- a/src/corelib/io/qfiledevice.cpp
+++ b/src/corelib/io/qfiledevice.cpp
@@ -168,6 +168,35 @@ void QFileDevicePrivate::setError(QFileDevice::FileError err, int errNum)
handle is left open when the QFile object is destroyed.
*/
+/*!
+ \macro QT_USE_NODISCARD_FILE_OPEN
+ \macro QT_NO_USE_NODISCARD_FILE_OPEN
+ \relates QFileDevice
+ \since 6.8
+
+ File-related I/O classes (such as QFile, QSaveFile, QTemporaryFile)
+ have an \c{open()} method to open the file they act upon. It is
+ important to check the return value of the call to \c{open()}
+ before proceeding with reading or writing data into the file.
+
+ For this reason, starting with Qt 6.8, some overloads of \c{open()}
+ have been marked with the \c{[[nodiscard]]} attribute. Since this
+ change may raise warnings in existing codebases, user code can
+ opt-in or opt-out from having the attribute applied by defining
+ certain macros:
+
+ \list
+ \li If the \c{QT_USE_NODISCARD_FILE_OPEN} macro is defined,
+ overloads of \c{open()} are marked as \c{[[nodiscard]]}.
+ \li If the \c{QT_NO_USE_NODISCARD_FILE_OPEN} is defined, the
+ overloads of \c{open()} are \e{not} marked as \c{[[nodiscard]]}.
+ \li If neither macro is defined, then the default up to and
+ including Qt 6.9 is not to have the attribute. Starting from Qt 6.10,
+ the attribute is automatically applied.
+ \li If both macros are defined, the program is ill-formed.
+ \endlist
+*/
+
#ifdef QT_NO_QOBJECT
QFileDevice::QFileDevice()
: QIODevice(*new QFileDevicePrivate)
diff --git a/src/corelib/io/qfiledevice.h b/src/corelib/io/qfiledevice.h
index 79788e2aafb..52740257154 100644
--- a/src/corelib/io/qfiledevice.h
+++ b/src/corelib/io/qfiledevice.h
@@ -12,6 +12,22 @@ QT_BEGIN_NAMESPACE
class QDateTime;
class QFileDevicePrivate;
+#if !defined(QT_USE_NODISCARD_FILE_OPEN) && !defined(QT_NO_USE_NODISCARD_FILE_OPEN)
+# if QT_VERSION < QT_VERSION_CHECK(6, 10, 0)
+# define QT_NO_USE_NODISCARD_FILE_OPEN
+# else
+# define QT_USE_NODISCARD_FILE_OPEN
+# endif
+#endif
+
+#if defined(QT_USE_NODISCARD_FILE_OPEN) && defined(QT_NO_USE_NODISCARD_FILE_OPEN)
+#error "Inconsistent macro definition for nodiscard QFile::open"
+#elif defined(QT_USE_NODISCARD_FILE_OPEN)
+#define QFILE_MAYBE_NODISCARD [[nodiscard]]
+#else /* QT_NO_USE_NODISCARD_FILE_OPEN */
+#define QFILE_MAYBE_NODISCARD
+#endif
+
class Q_CORE_EXPORT QFileDevice : public QIODevice
{
#ifndef QT_NO_QOBJECT
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp
index 9041775d99c..6d8918c29c6 100644
--- a/src/corelib/io/qsavefile.cpp
+++ b/src/corelib/io/qsavefile.cpp
@@ -152,7 +152,7 @@ void QSaveFile::setFileName(const QString &name)
QIODevice::ReadWrite, QIODevice::Append, QIODevice::NewOnly and
QIODevice::ExistingOnly are not supported at the moment.
- \sa QIODevice::OpenMode, setFileName()
+ \sa QIODevice::OpenMode, setFileName(), QT_USE_NODISCARD_FILE_OPEN
*/
bool QSaveFile::open(OpenMode mode)
{
diff --git a/src/corelib/io/qsavefile.h b/src/corelib/io/qsavefile.h
index db8a25507c7..4dd712d4b60 100644
--- a/src/corelib/io/qsavefile.h
+++ b/src/corelib/io/qsavefile.h
@@ -39,7 +39,7 @@ public:
QString fileName() const override;
void setFileName(const QString &name);
- bool open(OpenMode flags) override;
+ QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override;
bool commit();
void cancelWriting();
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
index 59f52bd082b..116c351962e 100644
--- a/src/corelib/io/qtemporaryfile.cpp
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -725,7 +725,7 @@ QTemporaryFile::~QTemporaryFile()
return true upon success and will set the fileName() to the unique
filename used.
- \sa fileName()
+ \sa fileName(), QT_USE_NODISCARD_FILE_OPEN
*/
/*!
diff --git a/src/corelib/io/qtemporaryfile.h b/src/corelib/io/qtemporaryfile.h
index 9d07e1633bb..9a4476f9d21 100644
--- a/src/corelib/io/qtemporaryfile.h
+++ b/src/corelib/io/qtemporaryfile.h
@@ -48,7 +48,7 @@ public:
void setAutoRemove(bool b);
// ### Hides open(flags)
- bool open() { return open(QIODevice::ReadWrite); }
+ QFILE_MAYBE_NODISCARD bool open() { return open(QIODevice::ReadWrite); }
QString fileName() const override;
QString fileTemplate() const;
diff --git a/src/dbus/CMakeLists.txt b/src/dbus/CMakeLists.txt
index cefd12b986d..9c3f6d23d2c 100644
--- a/src/dbus/CMakeLists.txt
+++ b/src/dbus/CMakeLists.txt
@@ -47,6 +47,7 @@ qt_internal_add_module(DBus
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_QPAIR
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 5f5ed6d66b5..87621e4c680 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -266,6 +266,7 @@ qt_internal_add_module(Gui
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
QT_QPA_DEFAULT_PLATFORM_NAME="${QT_QPA_DEFAULT_PLATFORM}"
INCLUDE_DIRECTORIES
../3rdparty/VulkanMemoryAllocator
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 38e653ce93b..f68198df588 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -62,6 +62,7 @@ qt_internal_add_module(Network
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_URL_CAST_FROM_STRING
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
kernel
LIBRARIES
diff --git a/src/opengl/CMakeLists.txt b/src/opengl/CMakeLists.txt
index cbb2a2b338e..825e4cb71b5 100644
--- a/src/opengl/CMakeLists.txt
+++ b/src/opengl/CMakeLists.txt
@@ -39,6 +39,7 @@ qt_internal_add_module(OpenGL
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::CorePrivate
Qt::GuiPrivate
diff --git a/src/openglwidgets/CMakeLists.txt b/src/openglwidgets/CMakeLists.txt
index fff233dd206..af3efdf30f7 100644
--- a/src/openglwidgets/CMakeLists.txt
+++ b/src/openglwidgets/CMakeLists.txt
@@ -13,6 +13,7 @@ qt_internal_add_module(OpenGLWidgets
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::OpenGLPrivate
Qt::WidgetsPrivate
diff --git a/src/printsupport/CMakeLists.txt b/src/printsupport/CMakeLists.txt
index 0b13146cf9f..25aad04cafd 100644
--- a/src/printsupport/CMakeLists.txt
+++ b/src/printsupport/CMakeLists.txt
@@ -23,6 +23,7 @@ qt_internal_add_module(PrintSupport
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
dialogs
widgets
diff --git a/src/sql/CMakeLists.txt b/src/sql/CMakeLists.txt
index dbd7cf99bfe..0a51f62c4bc 100644
--- a/src/sql/CMakeLists.txt
+++ b/src/sql/CMakeLists.txt
@@ -26,6 +26,7 @@ qt_internal_add_module(Sql
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
diff --git a/src/testlib/CMakeLists.txt b/src/testlib/CMakeLists.txt
index bafef623d0b..2c33883c96f 100644
--- a/src/testlib/CMakeLists.txt
+++ b/src/testlib/CMakeLists.txt
@@ -65,6 +65,7 @@ qt_internal_add_module(Test
QT_NO_CONTEXTLESS_CONNECT
QT_NO_DATASTREAM
QT_NO_FOREACH
+ QT_USE_NODISCARD_FILE_OPEN
# Ensure uniform location info between release and debug builds
QT_NO_MESSAGELOGCONTEXT
LIBRARIES
diff --git a/src/tools/moc/CMakeLists.txt b/src/tools/moc/CMakeLists.txt
index f56156d39d3..b98b7ab4e9b 100644
--- a/src/tools/moc/CMakeLists.txt
+++ b/src/tools/moc/CMakeLists.txt
@@ -30,6 +30,7 @@ qt_internal_add_tool(${target_name}
QT_NO_CAST_FROM_BYTEARRAY
QT_NO_FOREACH
QT_NO_QPAIR
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
../../3rdparty/tinycbor/src
diff --git a/src/tools/qlalr/CMakeLists.txt b/src/tools/qlalr/CMakeLists.txt
index 89aecf2bdf1..da8b3518893 100644
--- a/src/tools/qlalr/CMakeLists.txt
+++ b/src/tools/qlalr/CMakeLists.txt
@@ -22,6 +22,7 @@ qt_internal_add_tool(${target_name}
DEFINES
QT_NO_FOREACH
QT_NO_QPAIR
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::Core
Qt::CorePrivate
diff --git a/src/tools/rcc/CMakeLists.txt b/src/tools/rcc/CMakeLists.txt
index 55d4de2e285..35a72c43fe5 100644
--- a/src/tools/rcc/CMakeLists.txt
+++ b/src/tools/rcc/CMakeLists.txt
@@ -18,6 +18,7 @@ qt_internal_add_tool(${target_name}
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
QT_RCC
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
)
diff --git a/src/tools/uic/CMakeLists.txt b/src/tools/uic/CMakeLists.txt
index 48c1a535464..9f47ec8b4b0 100644
--- a/src/tools/uic/CMakeLists.txt
+++ b/src/tools/uic/CMakeLists.txt
@@ -32,6 +32,7 @@ qt_internal_add_tool(${target_name}
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
+ QT_USE_NODISCARD_FILE_OPEN
QT_UIC
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 2ba8e4719a7..fdef309a4ae 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -55,6 +55,7 @@ qt_internal_add_module(Widgets
QT_NO_CONTEXTLESS_CONNECT
QT_NO_USING_NAMESPACE
QT_NO_FOREACH
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
dialogs
LIBRARIES
diff --git a/src/xml/CMakeLists.txt b/src/xml/CMakeLists.txt
index 58c43f23849..38b52e3a086 100644
--- a/src/xml/CMakeLists.txt
+++ b/src/xml/CMakeLists.txt
@@ -14,6 +14,7 @@ qt_internal_add_module(Xml
QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_USE_NODISCARD_FILE_OPEN
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES