Adapt qmake's raw-string parser to avoid confusion by macros
A macro name ending in R might expand to a string; if this precedes a string constant, we're juxtaposing the strings. My first parser for raw strings would mistake it for a raw string instead, ignoring the part of the identifier before R. Re-worked the exploration of what came before the string to catch these cases, too. The backwards parsing would also allow any messy jumble of [RLUu8]* as prefix for the string; but in fact R must (if present) be last in the prefix and *it* can have at most one prefix, [LUu] or u8. Anything else is an identifier that happens to precede the string. Reworked the parsing to allow only one prefix and not treat R specially unless it's immediately (modulo BSNL) before the string's open-quotes. Add link to the cppreference page about string literals, on which the grammar now parsed is based. Added a test for the issue this addresses. Verified that this fails on 5.6, dev and 5.9 without the fix. Expanded the existing test to cover R-with-prefix cases. Task-number: QTBUG-55633 Change-Id: I541486c2ec909cfb42050907c84bee83ead4a2f4 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
This commit is contained in:
parent
797530c3f8
commit
515b905150
@ -422,25 +422,53 @@ static bool matchWhileUnsplitting(const char *buffer, int buffer_len, int start,
|
|||||||
/* 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. */
|
||||||
static int scanPastString(char *buffer, int buffer_len, int offset, int *lines)
|
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.
|
// It might be a C++11 raw string.
|
||||||
bool israw = false;
|
bool israw = false;
|
||||||
if (buffer[offset] == '"' && offset > 0) {
|
if (buffer[offset] == '"' && offset > 0) {
|
||||||
int explore = offset - 1;
|
int explore = offset - 1;
|
||||||
while (explore > 0 && buffer[explore] != 'R') {
|
bool prefix = false; // One of L, U, u or u8 may appear before R
|
||||||
if (buffer[explore] == '8' || buffer[explore] == 'u' || buffer[explore] == 'U') {
|
bool saw8 = false; // Partial scan of u8
|
||||||
explore--;
|
while (explore >= 0) {
|
||||||
} else if (explore > 1 && qmake_endOfLine(buffer[explore])
|
// Cope with backslash-newline interruptions of the prefix:
|
||||||
&& buffer[explore - 1] == '\\') {
|
if (explore > 0
|
||||||
|
&& qmake_endOfLine(buffer[explore])
|
||||||
|
&& buffer[explore - 1] == '\\') {
|
||||||
explore -= 2;
|
explore -= 2;
|
||||||
} else if (explore > 2 && buffer[explore] == '\n'
|
} else if (explore > 1
|
||||||
|
&& buffer[explore] == '\n'
|
||||||
&& buffer[explore - 1] == '\r'
|
&& buffer[explore - 1] == '\r'
|
||||||
&& buffer[explore - 2] == '\\') {
|
&& buffer[explore - 2] == '\\') {
|
||||||
explore -= 3;
|
explore -= 3;
|
||||||
|
// Remaining cases can only decrement explore by one at a time:
|
||||||
|
} else if (saw8 && buffer[explore] == 'u') {
|
||||||
|
explore--;
|
||||||
|
saw8 = false;
|
||||||
|
prefix = true;
|
||||||
|
} else if (saw8 || prefix) {
|
||||||
|
break;
|
||||||
|
} else if (explore > 1 && buffer[explore] == '8') {
|
||||||
|
explore--;
|
||||||
|
saw8 = true;
|
||||||
|
} else if (buffer[explore] == 'L'
|
||||||
|
|| buffer[explore] == 'U'
|
||||||
|
|| buffer[explore] == 'u') {
|
||||||
|
explore--;
|
||||||
|
prefix = true;
|
||||||
|
} else if (buffer[explore] == 'R') {
|
||||||
|
if (israw)
|
||||||
|
break;
|
||||||
|
explore--;
|
||||||
|
israw = true;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
israw = (buffer[explore] == 'R');
|
// Check the R (with possible prefix) isn't just part of an identifier:
|
||||||
|
if (israw && explore >= 0
|
||||||
|
&& (isalnum(buffer[explore]) || buffer[explore] == '_')) {
|
||||||
|
israw = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (israw) {
|
if (israw) {
|
||||||
|
101
tests/auto/tools/qmake/testdata/rawString/main.cpp
vendored
101
tests/auto/tools/qmake/testdata/rawString/main.cpp
vendored
@ -26,8 +26,107 @@
|
|||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
// macro names that *aren't* string-literal-prefixes:
|
||||||
|
#define Ru8 "rue-it"
|
||||||
|
#define RL "real life"
|
||||||
|
#define Ru "are you ?"
|
||||||
|
#define RU "Are You ?"
|
||||||
|
#define LLR "double-hockey-sticks"
|
||||||
|
#define LUR "Tricky"
|
||||||
|
#define LuR "tricky"
|
||||||
|
#define Lu8R "l'uber"
|
||||||
|
#define UUR "Double-Yew"
|
||||||
|
#define ULR "Eweler"
|
||||||
|
#define UuR "You ... you-are"
|
||||||
|
#define Uu8R "You ... you *ate* our ..."
|
||||||
|
#define uuR "water"
|
||||||
|
#define uLR "eweler"
|
||||||
|
#define uUR "double-Your"
|
||||||
|
#define uu8R "totally uber"
|
||||||
|
#define u8u8R "rubber-you"
|
||||||
|
#define u8LR "Uber left-to-right"
|
||||||
|
#define u8UR "Uber Upper-Right"
|
||||||
|
#define u8uR "Uber upper-right"
|
||||||
|
#define Ru8R "bouncy"
|
||||||
|
#define RLR "Marching"
|
||||||
|
#define RuR "Rossum's general-purpose workers"
|
||||||
|
#define RUR "Rossum's Universal Robots"
|
||||||
|
|
||||||
|
static const char monstrosity[] =
|
||||||
|
Ru8"Ru8("
|
||||||
|
RL"RL("
|
||||||
|
Ru"Ru("
|
||||||
|
RU"RU("
|
||||||
|
LLR"LLR("
|
||||||
|
LUR"LUR("
|
||||||
|
LuR"LuR("
|
||||||
|
Lu8R"Lu8R("
|
||||||
|
UUR"UUR("
|
||||||
|
ULR"ULR("
|
||||||
|
UuR"UuR("
|
||||||
|
Uu8R"Uu8R("
|
||||||
|
uuR"uuR("
|
||||||
|
uLR"uLR("
|
||||||
|
uUR"uUR("
|
||||||
|
uu8R"uu8R("
|
||||||
|
u8u8R"u8u8R("
|
||||||
|
u8LR"u8LR("
|
||||||
|
u8UR"u8UR("
|
||||||
|
u8uR"u8uR("
|
||||||
|
Ru8R"Ru8R("
|
||||||
|
RLR"RLR("
|
||||||
|
RuR"RuR("
|
||||||
|
RUR"RUR("
|
||||||
|
"Finally, some content";
|
||||||
|
|
||||||
|
#include <moc_object2.cpp>
|
||||||
|
|
||||||
|
static const char closure[] =
|
||||||
|
")RUR"
|
||||||
|
")RuR"
|
||||||
|
")RLR"
|
||||||
|
")Ru8R"
|
||||||
|
")u8uR"
|
||||||
|
")u8UR"
|
||||||
|
")u8LR"
|
||||||
|
")u8u8R"
|
||||||
|
")uu8R"
|
||||||
|
")uUR"
|
||||||
|
")uLR"
|
||||||
|
")uuR"
|
||||||
|
")Uu8R"
|
||||||
|
")UuR"
|
||||||
|
")ULR"
|
||||||
|
")UUR"
|
||||||
|
")Lu8R"
|
||||||
|
")LuR"
|
||||||
|
")LUR"
|
||||||
|
")LLR"
|
||||||
|
")RU"
|
||||||
|
")Ru"
|
||||||
|
")RL"
|
||||||
|
")Ru8";
|
||||||
|
// If moc got confused, the confusion should now be over
|
||||||
|
|
||||||
|
// Real raw strings, not actually leaving us inside any comments:
|
||||||
static const char raw[] = R"blah(lorem " ipsum /*)blah"\
|
static const char raw[] = R"blah(lorem " ipsum /*)blah"\
|
||||||
;
|
;
|
||||||
|
static const wchar_t wider[] = LR"blah(lorem " ipsum /*)blah"\
|
||||||
|
;
|
||||||
|
static const char32_t UCS4[] = UR"blah(lorem " ipsum /*)blah"\
|
||||||
|
;
|
||||||
|
static const char16_t UCS2[] = uR"blah(lorem " ipsum /*)blah"\
|
||||||
|
;
|
||||||
|
static const char utf8[] = u8R"blah(lorem " ipsum /*)blah"\
|
||||||
|
;
|
||||||
#include <moc_object1.cpp>
|
#include <moc_object1.cpp>
|
||||||
|
|
||||||
int main () { return 0; }
|
/* Avoid unused variable warnings by silly uses of arrays: */
|
||||||
|
#define final(x) x[sizeof(x) - 1] // 0, of course
|
||||||
|
int main () {
|
||||||
|
return final(raw)
|
||||||
|
* (final(wider) - final(UCS4))
|
||||||
|
* (final(UCS2) - final(utf8))
|
||||||
|
* (final(monstrosity) - final(closure));
|
||||||
|
}
|
||||||
|
#undef final
|
||||||
|
54
tests/auto/tools/qmake/testdata/rawString/object2.h
vendored
Normal file
54
tests/auto/tools/qmake/testdata/rawString/object2.h
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the test suite of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** As a special exception, The Qt Company gives you certain additional
|
||||||
|
** rights. These rights are described in The Qt Company LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef TEST_QMAKE_RAWSTRING_OBJECT2_H
|
||||||
|
#define TEST_QMAKE_RAWSTRING_OBJECT2_H
|
||||||
|
|
||||||
|
#define Lu8UR "land"
|
||||||
|
inline char opener(int i) {
|
||||||
|
const char text[] = Lu8UR"blah( not a raw string; just juxtaposed";
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class Object2 : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
inline char closer(int i) {
|
||||||
|
const char text[] = "pretend to close it, all the same )blah";
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TEST_QMAKE_RAWSTRING_OBJECT2_H
|
@ -1,4 +1,4 @@
|
|||||||
DESTDIR = ./
|
DESTDIR = ./
|
||||||
|
|
||||||
HEADERS += object1.h
|
HEADERS += object1.h object2.h
|
||||||
SOURCES += main.cpp
|
SOURCES += main.cpp
|
||||||
|
Loading…
x
Reference in New Issue
Block a user