From 7dd394a16c51d11cd3ce91e288253ad137190994 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 25 Aug 2014 14:21:30 +0200 Subject: [PATCH] Add a macro to disable only some 8 bit/16 bit string conversions This is an opt-in trade-off between type safety and user code convenience. QT_NO_CAST_FROM_ASCII is highly beneficial to avoid unintended conversions from 8 bit data with potentially "unsuitable" encodings to QString. However, it has the undesirable side-effect to require user code to wrap character and string literals in QLatin1Char(...) and QLatin1String(...) or use similar construction, cluttering the code significantly. This patch introduces a QT_RESTRICTED_CAST_FROM_ASCII macro that works almost as QT_NO_CAST_FROM_ASCII, except that it enables the QChar(char) constructor and adds an additional QString(const char (&ch)[N]) constructor that matches C++ string literals, but no arbitrary character pointers. This avoids a significant share of the need to clutter the user code by only a slight relaxation of the type-safety. [ChangeLog][QtCore][QString] Added QT_RESTRICTED_CAST_FROM_ASCII macro as less intrusive alternative to QT_NO_CAST_FROM_ASCII. Change-Id: Iac72f1f90f81fbcae9bfb1fe68b0fec6ffb36c50 Reviewed-by: Oswald Buddenhagen Reviewed-by: Kai Koehne Reviewed-by: Eike Ziller Reviewed-by: Thiago Macieira Reviewed-by: Olivier Goffart --- src/corelib/tools/qchar.h | 2 ++ src/corelib/tools/qstring.cpp | 40 +++++++++++++++++++++++++++++------ src/corelib/tools/qstring.h | 31 +++++++++++++++++++-------- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/corelib/tools/qchar.h b/src/corelib/tools/qchar.h index 4db09e0f82..dcbe3fb0c2 100644 --- a/src/corelib/tools/qchar.h +++ b/src/corelib/tools/qchar.h @@ -83,7 +83,9 @@ public: #ifndef QT_NO_CAST_FROM_ASCII QT_ASCII_CAST_WARN Q_DECL_CONSTEXPR explicit QChar(char c) : ucs(uchar(c)) { } +#ifndef QT_RESTRICTED_CAST_FROM_ASCII QT_ASCII_CAST_WARN Q_DECL_CONSTEXPR explicit QChar(uchar c) : ucs(c) { } +#endif #endif // Unicode information diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 2067605646..95e45c8d93 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -731,13 +731,31 @@ inline char qToLower(char ch) const QString::Null QString::null = { }; +/*! + \macro QT_RESTRICTED_CAST_FROM_ASCII + \relates QString + + Defining this macro disables most automatic conversions from source + literals and 8-bit data to unicode QStrings, but allows the use of + the \c{QChar(char)} and \c{QString(const char (&ch)[N]} constructors, + and the \c{QString::operator=(const char (&ch)[N])} assignment operator + giving most of the type-safety benefits of QT_NO_CAST_FROM_ASCII + but does not require user code to wrap character and string literals + with QLatin1Char, QLatin1String or similar. + + Using this macro together with source strings outside the 7-bit range, + non-literals, or literals with embedded NUL characters is undefined. + + \sa QT_NO_CAST_FROM_ASCII, QT_NO_CAST_TO_ASCII +*/ + /*! \macro QT_NO_CAST_FROM_ASCII \relates QString Disables automatic conversions from 8-bit strings (char *) to unicode QStrings - \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_BYTEARRAY + \sa QT_NO_CAST_TO_ASCII, QT_RESTRICTED_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY */ /*! @@ -746,7 +764,7 @@ const QString::Null QString::null = { }; disables automatic conversion from QString to 8-bit strings (char *) - \sa QT_NO_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY + \sa QT_NO_CAST_FROM_ASCII, QT_RESTRICTED_CAST_FROM_ASCII, QT_NO_CAST_FROM_BYTEARRAY */ /*! @@ -760,7 +778,7 @@ const QString::Null QString::null = { }; Note: This only works for compilers that support warnings for deprecated API. - \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_ASCII + \sa QT_NO_CAST_TO_ASCII, QT_NO_CAST_FROM_ASCII, QT_RESTRICTED_CAST_FROM_ASCII */ /*! @@ -994,6 +1012,9 @@ const QString::Null QString::null = { }; \list \li \c QT_NO_CAST_FROM_ASCII disables automatic conversions from C string literals and pointers to Unicode. + \li \c QT_RESTRICTED_CAST_FROM_ASCII allows automatic conversions + from C characters and character arrays, but disables automatic + conversions from character pointers to Unicode. \li \c QT_NO_CAST_TO_ASCII disables automatic conversion from QString to C strings. \endlist @@ -1311,6 +1332,12 @@ const QString::Null QString::null = { }; can be useful if you want to ensure that all user-visible strings go through QObject::tr(), for example. + \note Defining QT_RESTRICTED_CAST_FROM_ASCII also disables + this constructor, but enables a \c{QString(const char (&ch)[N])} + constructor instead. Using non-literal input, or input with + embedded NUL characters, or non-7-bit characters is undefined + in this case. + \sa fromLatin1(), fromLocal8Bit(), fromUtf8() */ @@ -1749,10 +1776,11 @@ QString &QString::operator=(const QString &other) Assigns \a str to this string. The const char pointer is converted to Unicode using the fromUtf8() function. - You can disable this operator by defining \c - QT_NO_CAST_FROM_ASCII when you compile your applications. This - can be useful if you want to ensure that all user-visible strings + You can disable this operator by defining \c QT_NO_CAST_FROM_ASCII + or \c QT_RESTRICTED_CAST_FROM_ASCII when you compile your applications. + This can be useful if you want to ensure that all user-visible strings go through QObject::tr(), for example. + */ /*! \fn QString &QString::operator=(char ch) diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index ddee6e9fc0..c0bfafc9a7 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -34,6 +34,10 @@ #ifndef QSTRING_H #define QSTRING_H +#if defined(QT_NO_CAST_FROM_ASCII) && defined(QT_RESTRICTED_CAST_FROM_ASCII) +#error QT_NO_CAST_FROM_ASCII and QT_RESTRICTED_CAST_FROM_ASCII must not be defined at the same time +#endif + #include #include #include @@ -93,7 +97,7 @@ public: inline bool operator>=(const QString &s) const; inline bool operator<=(const QString &s) const; -#ifndef QT_NO_CAST_FROM_ASCII +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline QT_ASCII_CAST_WARN bool operator==(const char *s) const; inline QT_ASCII_CAST_WARN bool operator!=(const char *s) const; inline QT_ASCII_CAST_WARN bool operator<(const char *s) const; @@ -107,7 +111,7 @@ public: inline QT_ASCII_CAST_WARN bool operator>(const QByteArray &s) const; inline QT_ASCII_CAST_WARN bool operator<=(const QByteArray &s) const; inline QT_ASCII_CAST_WARN bool operator>=(const QByteArray &s) const; -#endif // QT_NO_CAST_FROM_ASCII +#endif // !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) private: int m_size; @@ -623,7 +627,16 @@ public: inline bool operator>=(QLatin1String s) const { return !operator<(s); } // ASCII compatibility -#ifndef QT_NO_CAST_FROM_ASCII +#if defined(QT_RESTRICTED_CAST_FROM_ASCII) + template + inline QString(const char (&ch)[N]) + : d(fromAscii_helper(ch, N - 1)) + {} + template + inline QString &operator=(const char (&ch)[N]) + { return (*this = fromLatin1(ch, N - 1)); } +#endif +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline QT_ASCII_CAST_WARN QString(const char *ch) : d(fromAscii_helper(ch, ch ? int(strlen(ch)) : -1)) {} @@ -1116,7 +1129,7 @@ inline bool QLatin1String::operator>=(const QString &s) const inline bool QLatin1String::operator<=(const QString &s) const { return s >= *this; } -#ifndef QT_NO_CAST_FROM_ASCII +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline bool QString::operator==(const char *s) const { return QString::compare_helper(constData(), size(), s, -1) == 0; } inline bool QString::operator!=(const char *s) const @@ -1207,7 +1220,7 @@ inline bool QByteArray::operator<=(const QString &s) const { return QString::compare_helper(s.constData(), s.size(), constData(), qstrnlen(constData(), size())) <= 0; } inline bool QByteArray::operator>=(const QString &s) const { return QString::compare_helper(s.constData(), s.size(), constData(), qstrnlen(constData(), size())) >= 0; } -#endif // QT_NO_CAST_FROM_ASCII +#endif // !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) #ifndef QT_NO_CAST_TO_ASCII inline QByteArray &QByteArray::append(const QString &s) @@ -1235,7 +1248,7 @@ inline const QString operator+(const QString &s1, QChar s2) { QString t(s1); t += s2; return t; } inline const QString operator+(QChar s1, const QString &s2) { QString t(s1); t += s2; return t; } -# ifndef QT_NO_CAST_FROM_ASCII +# if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline QT_ASCII_CAST_WARN const QString operator+(const QString &s1, const char *s2) { QString t(s1); t += QString::fromUtf8(s2); return t; } inline QT_ASCII_CAST_WARN const QString operator+(const char *s1, const QString &s2) @@ -1388,7 +1401,7 @@ public: inline const QChar at(int i) const { Q_ASSERT(uint(i) < uint(size())); return m_string->at(i + m_position); } -#ifndef QT_NO_CAST_FROM_ASCII +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) // ASCII compatibility inline QT_ASCII_CAST_WARN bool operator==(const char *s) const; inline QT_ASCII_CAST_WARN bool operator!=(const char *s) const; @@ -1462,7 +1475,7 @@ inline bool operator<=(const QStringRef &s1, const QStringRef &s2) inline bool operator>=(const QStringRef &s1, const QStringRef &s2) { return !(s1 < s2); } -#ifndef QT_NO_CAST_FROM_ASCII +#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline QT_ASCII_CAST_WARN bool QStringRef::operator==(const char *s) const { return QString::compare_helper(constData(), size(), s, -1) == 0; } inline QT_ASCII_CAST_WARN bool QStringRef::operator!=(const char *s) const @@ -1488,7 +1501,7 @@ inline QT_ASCII_CAST_WARN bool operator>(const char *s1, const QStringRef &s2) { return QString::compare_helper(s2.constData(), s2.size(), s1, -1) <= 0; } inline QT_ASCII_CAST_WARN bool operator>=(const char *s1, const QStringRef &s2) { return QString::compare_helper(s2.constData(), s2.size(), s1, -1) >= 0; } -#endif // QT_NO_CAST_FROM_ASCII +#endif // !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_RESTRICTED_CAST_FROM_ASCII) inline int QString::compare(const QStringRef &s, Qt::CaseSensitivity cs) const { return QString::compare_helper(constData(), length(), s.constData(), s.length(), cs); }