Teach qmake about digit-grouping apostrophes in numeric literals

It was previously understanding them as character literal delimiters,
with unfortunate consequences if a numeric literal contained an odd
number of them. Recognize that an apostrophe with a digit on each side
of it isn't the opening quote of a character literal (unless the digit
before it is preceded by a u). Extend the findMocs test to trigger the
bug, prior to the fix; verified it passes with the fix.

Fixes: QTBUG-98845
Change-Id: I5db3ac59aaeade7c2d6c1fb680ba97261ec0e8a9
Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Edward Welbourne 2022-06-13 17:10:44 +02:00
parent 077eddb3e1
commit cfcbf38f76
4 changed files with 39 additions and 5 deletions

View File

@ -365,13 +365,21 @@ static bool matchWhileUnsplitting(const char *buffer, int buffer_len, int start,
return true;
}
/* Advance from an opening quote at buffer[offset] to the matching close quote. */
/* Advance from an opening quote at buffer[offset] to the matching close quote.
If an apostrophe turns out to be a digit-separator in a numeric literal,
rather than the start of a character literal, treat it as both the open and
the close quote of the "string" that isn't there.
*/
static int scanPastString(char *buffer, int buffer_len, int offset, int *lines)
{
// http://en.cppreference.com/w/cpp/language/string_literal
// It might be a C++11 raw string.
bool israw = false;
if (buffer[offset] == '"' && offset > 0) {
Q_ASSERT(offset < buffer_len);
if (offset <= 0) {
// skip, neither of these special cases applies here
} else if (buffer[offset] == '"') {
int explore = offset - 1;
bool prefix = false; // One of L, U, u or u8 may appear before R
bool saw8 = false; // Partial scan of u8
@ -415,6 +423,19 @@ static int scanPastString(char *buffer, int buffer_len, int offset, int *lines)
&& (isalnum(buffer[explore]) || buffer[explore] == '_')) {
israw = false;
}
} else {
// Is this apostrophe a digit separator rather than the start of a
// character literal ? If so, there was no string to scan past, so
// treat the apostrophe as both open and close.
Q_ASSERT(buffer[offset] == '\'' && offset > 0);
// Wrap std::isdigit() to package the casting to unsigned char.
const auto isDigit = [](unsigned char c) { return std::isdigit(c); };
if (isDigit(buffer[offset - 1]) && offset + 1 < buffer_len && isDigit(buffer[offset + 1])) {
// One exception: u8'0' is a perfectly good character literal.
if (offset < 2 || buffer[offset - 1] != '8' || buffer[offset - 2] != 'u')
return offset;
}
}
if (israw) {

View File

@ -0,0 +1,8 @@
#include <QObject>
class AfterDigitSeparator : public QObject
{
Q_OBJECT
public:
AfterDigitSeparator() : QObject(nullptr) {}
};

View File

@ -1,5 +1,6 @@
DESTDIR = ./
HEADERS += object1.h object2.h object3.h object4.h \
object5.h object6.h object7.h object8.h object9.h
object5.h object6.h object7.h object8.h object9.h \
digitseparated.h
SOURCES += main.cpp

View File

@ -1,7 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <moc_object1.cpp>
#include <moc_object2.cpp>
#include <moc_object3.cpp>
@ -12,4 +11,9 @@
#include "object8.h"
#include <moc_object9.cpp>
int main() { return 0; }
int main() { return 0'000; }
/* Included *after* the use of a numeric literal with an *odd* number of digit
separator tick marks in it (and no subsequent apostrophe to end the
"single-quoted character literal" that the old parser though it was thus left
in). */
#include <moc_digitseparated.cpp>