Skip to content

Commit

Permalink
Teach qmake about digit-grouping apostrophes in numeric literals
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ediosyncratic committed Jun 16, 2022
1 parent 077eddb commit cfcbf38
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 5 deletions.
25 changes: 23 additions & 2 deletions qmake/generators/makefiledeps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions tests/auto/tools/qmake/testdata/findMocs/digitseparated.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <QObject>

class AfterDigitSeparator : public QObject
{
Q_OBJECT
public:
AfterDigitSeparator() : QObject(nullptr) {}
};
3 changes: 2 additions & 1 deletion tests/auto/tools/qmake/testdata/findMocs/findMocs.pro
Original file line number Diff line number Diff line change
@@ -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
8 changes: 6 additions & 2 deletions tests/auto/tools/qmake/testdata/findMocs/main.cpp
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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>

0 comments on commit cfcbf38

Please sign in to comment.