qmake: teach moc-detector to handle C++-11 raw strings

As for the #include-parser, the moc-detector's minimal C preprocessor
could be confused by a raw string into ignoring large chunks of code.

Change-Id: Id688e9a1f04628ce75a51a7d15269078c734288e
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
This commit is contained in:
Edward Welbourne 2015-12-08 14:50:45 +01:00
parent 209a26c6c4
commit 5e7492325a
2 changed files with 86 additions and 83 deletions

View File

@ -422,6 +422,87 @@ static bool matchWhileUnsplitting(const char *buffer, int buffer_len, int start,
return true; return true;
} }
/* 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)
{
// It might be a C++11 raw string.
bool israw = false;
if (buffer[offset] == '"' && offset > 0) {
int explore = offset - 1;
while (explore > 0 && buffer[explore] != 'R') {
if (buffer[explore] == '8' || buffer[explore] == 'u' || buffer[explore] == 'U') {
explore--;
} else if (explore > 1 && qmake_endOfLine(buffer[explore])
&& buffer[explore - 1] == '\\') {
explore -= 2;
} else if (explore > 2 && buffer[explore] == '\n'
&& buffer[explore - 1] == '\r'
&& buffer[explore - 2] == '\\') {
explore -= 3;
} else {
break;
}
}
israw = (buffer[explore] == 'R');
}
if (israw) {
#define SKIP_BSNL(pos) skipEscapedLineEnds(buffer, buffer_len, (pos), lines)
offset = SKIP_BSNL(offset + 1);
const char *const delim = buffer + offset;
int clean = offset;
while (offset < buffer_len && buffer[offset] != '(') {
if (clean < offset)
buffer[clean++] = buffer[offset];
else
clean++;
offset = SKIP_BSNL(offset + 1);
}
/*
Not checking correctness (trust real compiler to do that):
- no controls, spaces, '(', ')', '\\' or (presumably) '"' in delim;
- at most 16 bytes in delim
Raw strings are surely defined after phase 2, when BSNLs are resolved;
so the delimiter's exclusion of '\\' and space (including newlines)
applies too late to save us the need to cope with BSNLs in it.
*/
const int delimlen = buffer + clean - delim;
int matchlen = delimlen, extralines = 0;
while ((offset = SKIP_BSNL(offset + 1)) < buffer_len
&& (buffer[offset] != ')'
|| (delimlen > 0 &&
!matchWhileUnsplitting(buffer, buffer_len,
offset + 1, delim, delimlen,
&matchlen, &extralines))
|| buffer[offset + 1 + matchlen] != '"')) {
// skip, but keep track of lines
if (qmake_endOfLine(buffer[offset]))
++*lines;
extralines = 0;
}
*lines += extralines; // from the match
// buffer[offset] is ')'
offset += 1 + matchlen; // 1 for ')', then delim
// buffer[offset] is '"'
#undef SKIP_BSNL
} else { // Traditional string or char literal:
const char term = buffer[offset];
while (++offset < buffer_len && buffer[offset] != term) {
if (buffer[offset] == '\\')
++offset;
else if (qmake_endOfLine(buffer[offset]))
++*lines;
}
}
return offset;
}
bool QMakeSourceFileInfo::findDeps(SourceFile *file) bool QMakeSourceFileInfo::findDeps(SourceFile *file)
{ {
if(file->dep_checked || file->type == TYPE_UNKNOWN) if(file->dep_checked || file->type == TYPE_UNKNOWN)
@ -696,75 +777,7 @@ bool QMakeSourceFileInfo::findDeps(SourceFile *file)
case InCode: case InCode:
// matching quotes (string literals and character literals) // matching quotes (string literals and character literals)
if (buffer[x] == '\'' || buffer[x] == '"') { if (buffer[x] == '\'' || buffer[x] == '"') {
// It might be a C++11 raw string. x = scanPastString(buffer, buffer_len, x, &line_count);
bool israw = false;
if (buffer[x] == '"' && x > 0) {
int y = x - 1;
while (y > 0 && buffer[y] != 'R') {
if (buffer[y] == '8' || buffer[y] == 'u' || buffer[y] == 'U')
y--;
else if (y > 1 && qmake_endOfLine(buffer[y])
&& buffer[y - 1] == '\\')
y -= 2;
else if (y > 2 && buffer[y] == '\n'
&& buffer[y - 1] == '\r'
&& buffer[y - 2] == '\\')
y -= 3;
else
break;
}
israw = (buffer[y] == 'R');
}
if (israw) {
x = SKIP_BSNL(x + 1);
const char *const delim = buffer + x;
int clean = x;
while (x < buffer_len && buffer[x] != '(') {
if (clean < x)
buffer[clean++] = buffer[x];
else
clean++;
x = SKIP_BSNL(x + 1);
}
/*
Not checking correctness (trust real compiler to do that):
- no controls, spaces, '(', ')', '\\' or (presumably) '"' in delim;
- at most 16 bytes in delim
Raw strings are surely defined after phase 2, when
BSNLs are resolved; so the delimiter's exclusion
of '\\' and space (including newlines) applies too
late to save us the need to cope with BSNLs in it.
*/
const int delimlen = buffer + clean - delim;
int matchlen = delimlen, extralines = 0;
while ((x = SKIP_BSNL(x + 1)) < buffer_len
&& (buffer[x] != ')'
|| (delimlen > 0 &&
!matchWhileUnsplitting(buffer, buffer_len,
x + 1, delim, delimlen,
&matchlen, &extralines))
|| buffer[x + 1 + matchlen] != '"')) {
// skip, but keep track of lines
if (qmake_endOfLine(buffer[x]))
++line_count;
extralines = 0;
}
line_count += extralines; // from the match
// buffer[x] is ')'
x += 1 + matchlen; // 1 for ')', then delim
// buffer[x] is '"'
} else {
const char term = buffer[x];
while (++x < buffer_len && buffer[x] != term) {
if (buffer[x] == '\\')
++x;
else if (qmake_endOfLine(buffer[x]))
++line_count;
}
}
// for loop's ++x shall step over the closing quote. // for loop's ++x shall step over the closing quote.
} }
// else: buffer[x] is just some code; move on. // else: buffer[x] is just some code; move on.
@ -924,19 +937,10 @@ bool QMakeSourceFileInfo::findMocs(SourceFile *file)
} }
} }
} else if (buffer[x] == '\'' || buffer[x] == '"') { } else if (buffer[x] == '\'' || buffer[x] == '"') {
const char term = buffer[x++]; x = scanPastString(buffer, buffer_len, x, &line_count);
while(x < buffer_len) { // Leaves us on closing quote; for loop's x++ steps us past it.
if (buffer[x] == term)
break;
if (buffer[x] == '\\') {
x+=2;
} else {
if (qmake_endOfLine(buffer[x]))
++line_count;
++x;
}
}
} }
if (Option::debug_level && qmake_endOfLine(buffer[x])) if (Option::debug_level && qmake_endOfLine(buffer[x]))
++line_count; ++line_count;
if (buffer_len > x + 2 && buffer[x + 1] == 'Q' && if (buffer_len > x + 2 && buffer[x + 1] == 'Q' &&

View File

@ -31,11 +31,10 @@
** **
****************************************************************************/ ****************************************************************************/
#define rawstring R"blah(lorem " ipsum /*)blah";
#include <QObject> #include <QObject>
class Object1 : public QObject class Object1 : public QObject
{ {
Q_OBJECT Q_OBJECT
}; };