add autotest for qmake parser
Change-Id: Ib3dcb6c1aaac20ca6a3bc0dc564e16ec7bd152db Reviewed-by: Daniel Teske <daniel.teske@theqtcompany.com>
This commit is contained in:
parent
9113e5677c
commit
9d4641f56e
@ -138,6 +138,7 @@ public:
|
||||
int toInt(bool *ok = 0, int base = 10) const { return toQString().toInt(ok, base); } // XXX optimize
|
||||
short toShort(bool *ok = 0, int base = 10) const { return toQString().toShort(ok, base); } // XXX optimize
|
||||
|
||||
uint hash() const { return m_hash; }
|
||||
static uint hash(const QChar *p, int n);
|
||||
|
||||
ALWAYS_INLINE QStringRef toQStringRef() const { return QStringRef(&m_string, m_offset, m_length); }
|
||||
|
@ -1250,4 +1250,258 @@ void QMakeParser::message(int type, const QString &msg) const
|
||||
m_handler->message(type, msg, m_proFile->fileName(), m_lineNo);
|
||||
}
|
||||
|
||||
#ifdef PROPARSER_DEBUG
|
||||
|
||||
#define BOUNDS_CHECK(need) \
|
||||
do { \
|
||||
int have = limit - offset; \
|
||||
if (have < (int)need) { \
|
||||
*outStr += fL1S("<out of bounds (need %1, got %2)>").arg(need).arg(have); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool getRawUshort(const ushort *tokens, int limit, int &offset, ushort *outVal, QString *outStr)
|
||||
{
|
||||
BOUNDS_CHECK(1);
|
||||
uint val = tokens[offset++];
|
||||
*outVal = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getUshort(const ushort *tokens, int limit, int &offset, ushort *outVal, QString *outStr)
|
||||
{
|
||||
*outStr += fL1S(" << H(");
|
||||
if (!getRawUshort(tokens, limit, offset, outVal, outStr))
|
||||
return false;
|
||||
*outStr += QString::number(*outVal) + QLatin1Char(')');
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getRawUint(const ushort *tokens, int limit, int &offset, uint *outVal, QString *outStr)
|
||||
{
|
||||
BOUNDS_CHECK(2);
|
||||
uint val = tokens[offset++];
|
||||
val |= (uint)tokens[offset++] << 16;
|
||||
*outVal = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getUint(const ushort *tokens, int limit, int &offset, uint *outVal, QString *outStr)
|
||||
{
|
||||
*outStr += fL1S(" << I(");
|
||||
if (!getRawUint(tokens, limit, offset, outVal, outStr))
|
||||
return false;
|
||||
*outStr += QString::number(*outVal) + QLatin1Char(')');
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getRawStr(const ushort *tokens, int limit, int &offset, int strLen, QString *outStr)
|
||||
{
|
||||
BOUNDS_CHECK(strLen);
|
||||
*outStr += fL1S("L\"");
|
||||
bool attn = false;
|
||||
for (int i = 0; i < strLen; i++) {
|
||||
ushort val = tokens[offset++];
|
||||
switch (val) {
|
||||
case '"': *outStr += fL1S("\\\""); break;
|
||||
case '\n': *outStr += fL1S("\\n"); break;
|
||||
case '\r': *outStr += fL1S("\\r"); break;
|
||||
case '\t': *outStr += fL1S("\\t"); break;
|
||||
case '\\': *outStr += fL1S("\\\\"); break;
|
||||
default:
|
||||
if (val < 32 || val > 126) {
|
||||
*outStr += (val > 255 ? fL1S("\\u") : fL1S("\\x")) + QString::number(val, 16);
|
||||
attn = true;
|
||||
continue;
|
||||
}
|
||||
if (attn && isxdigit(val))
|
||||
*outStr += fL1S("\"\"");
|
||||
*outStr += QChar(val);
|
||||
break;
|
||||
}
|
||||
attn = false;
|
||||
}
|
||||
*outStr += QLatin1Char('"');
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getStr(const ushort *tokens, int limit, int &offset, QString *outStr)
|
||||
{
|
||||
*outStr += fL1S(" << S(");
|
||||
ushort len;
|
||||
if (!getRawUshort(tokens, limit, offset, &len, outStr))
|
||||
return false;
|
||||
if (!getRawStr(tokens, limit, offset, len, outStr))
|
||||
return false;
|
||||
*outStr += QLatin1Char(')');
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getHashStr(const ushort *tokens, int limit, int &offset, QString *outStr)
|
||||
{
|
||||
*outStr += fL1S(" << HS(");
|
||||
uint hash;
|
||||
if (!getRawUint(tokens, limit, offset, &hash, outStr))
|
||||
return false;
|
||||
ushort len;
|
||||
if (!getRawUshort(tokens, limit, offset, &len, outStr))
|
||||
return false;
|
||||
const QChar *chars = (const QChar *)tokens + offset;
|
||||
if (!getRawStr(tokens, limit, offset, len, outStr))
|
||||
return false;
|
||||
uint realhash = ProString::hash(chars, len);
|
||||
if (realhash != hash)
|
||||
*outStr += fL1S(" /* Bad hash ") + QString::number(hash) + fL1S(" */");
|
||||
*outStr += QLatin1Char(')');
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outStr, int indent);
|
||||
|
||||
static bool getSubBlock(const ushort *tokens, int limit, int &offset, QString *outStr, int indent,
|
||||
const char *scope)
|
||||
{
|
||||
*outStr += fL1S("\n /* %1 */ ").arg(offset, 5)
|
||||
+ QString(indent * 4, QLatin1Char(' '))
|
||||
+ fL1S("/* ") + fL1S(scope) + fL1S(" */");
|
||||
uint len;
|
||||
if (!getUint(tokens, limit, offset, &len, outStr))
|
||||
return false;
|
||||
if (len) {
|
||||
BOUNDS_CHECK(len);
|
||||
int tmpOff = offset;
|
||||
offset += len;
|
||||
forever {
|
||||
if (!getBlock(tokens, offset, tmpOff, outStr, indent + 1))
|
||||
break; // Error was already reported, try to continue
|
||||
if (tmpOff == offset)
|
||||
break;
|
||||
*outStr += QLatin1Char('\n') + QString(20 + indent * 4, QLatin1Char(' '))
|
||||
+ fL1S("/* Warning: Excess tokens follow. */");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outStr, int indent)
|
||||
{
|
||||
static const char * const tokNames[] = {
|
||||
"TokTerminator",
|
||||
"TokLine",
|
||||
"TokAssign", "TokAppend", "TokAppendUnique", "TokRemove", "TokReplace",
|
||||
"TokValueTerminator",
|
||||
"TokLiteral", "TokHashLiteral", "TokVariable", "TokProperty", "TokEnvVar",
|
||||
"TokFuncName", "TokArgSeparator", "TokFuncTerminator",
|
||||
"TokCondition", "TokTestCall",
|
||||
"TokReturn", "TokBreak", "TokNext",
|
||||
"TokNot", "TokAnd", "TokOr",
|
||||
"TokBranch", "TokForLoop",
|
||||
"TokTestDef", "TokReplaceDef"
|
||||
};
|
||||
|
||||
while (offset != limit) {
|
||||
*outStr += fL1S("\n /* %1 */").arg(offset, 5)
|
||||
+ QString(indent * 4, QLatin1Char(' '));
|
||||
BOUNDS_CHECK(1);
|
||||
ushort tok = tokens[offset++];
|
||||
ushort maskedTok = tok & TokMask;
|
||||
if (maskedTok >= sizeof(tokNames)/sizeof(tokNames[0])
|
||||
|| (tok & ~(TokNewStr | TokQuoted | TokMask))) {
|
||||
*outStr += fL1S(" << {invalid token %1}").arg(tok);
|
||||
return false;
|
||||
}
|
||||
*outStr += fL1S(" << H(") + fL1S(tokNames[maskedTok]);
|
||||
if (tok & TokNewStr)
|
||||
*outStr += fL1S(" | TokNewStr");
|
||||
if (tok & TokQuoted)
|
||||
*outStr += fL1S(" | TokQuoted");
|
||||
*outStr += QLatin1Char(')');
|
||||
bool ok;
|
||||
switch (maskedTok) {
|
||||
case TokFuncTerminator: // Recursion, but not a sub-block
|
||||
return true;
|
||||
case TokArgSeparator:
|
||||
case TokValueTerminator: // Not recursion
|
||||
case TokTerminator: // Recursion, and limited by (sub-)block length
|
||||
case TokCondition:
|
||||
case TokReturn:
|
||||
case TokBreak:
|
||||
case TokNext:
|
||||
case TokNot:
|
||||
case TokAnd:
|
||||
case TokOr:
|
||||
ok = true;
|
||||
break;
|
||||
case TokTestCall:
|
||||
ok = getBlock(tokens, limit, offset, outStr, indent + 1);
|
||||
break;
|
||||
case TokBranch:
|
||||
ok = getSubBlock(tokens, limit, offset, outStr, indent, "then branch");
|
||||
if (ok)
|
||||
ok = getSubBlock(tokens, limit, offset, outStr, indent, "else branch");
|
||||
break;
|
||||
default:
|
||||
switch (maskedTok) {
|
||||
case TokAssign:
|
||||
case TokAppend:
|
||||
case TokAppendUnique:
|
||||
case TokRemove:
|
||||
case TokReplace:
|
||||
// The parameter is the sizehint for the output.
|
||||
// fallthrough
|
||||
case TokLine: {
|
||||
ushort dummy;
|
||||
ok = getUshort(tokens, limit, offset, &dummy, outStr);
|
||||
break; }
|
||||
case TokLiteral:
|
||||
case TokEnvVar:
|
||||
ok = getStr(tokens, limit, offset, outStr);
|
||||
break;
|
||||
case TokHashLiteral:
|
||||
case TokVariable:
|
||||
case TokProperty:
|
||||
ok = getHashStr(tokens, limit, offset, outStr);
|
||||
break;
|
||||
case TokFuncName:
|
||||
ok = getHashStr(tokens, limit, offset, outStr);
|
||||
if (ok)
|
||||
ok = getBlock(tokens, limit, offset, outStr, indent + 1);
|
||||
break;
|
||||
case TokForLoop:
|
||||
ok = getHashStr(tokens, limit, offset, outStr);
|
||||
if (ok)
|
||||
ok = getSubBlock(tokens, limit, offset, outStr, indent, "iterator");
|
||||
if (ok)
|
||||
ok = getSubBlock(tokens, limit, offset, outStr, indent, "body");
|
||||
break;
|
||||
case TokTestDef:
|
||||
case TokReplaceDef:
|
||||
ok = getHashStr(tokens, limit, offset, outStr);
|
||||
if (ok)
|
||||
ok = getSubBlock(tokens, limit, offset, outStr, indent, "body");
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(!"unhandled token");
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QMakeParser::formatProBlock(const QString &block)
|
||||
{
|
||||
QString outStr;
|
||||
outStr += fL1S("\n << TS(");
|
||||
int offset = 0;
|
||||
getBlock(reinterpret_cast<const ushort *>(block.constData()), block.length(),
|
||||
offset, &outStr, 0);
|
||||
outStr += QLatin1Char(')');
|
||||
return outStr;
|
||||
}
|
||||
|
||||
#endif // PROPARSER_DEBUG
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -96,6 +96,10 @@ public:
|
||||
|
||||
void discardFileFromCache(const QString &fileName);
|
||||
|
||||
#ifdef PROPARSER_DEBUG
|
||||
static QString formatProBlock(const QString &block);
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum ScopeNesting {
|
||||
NestNone = 0,
|
||||
|
@ -9,4 +9,8 @@ VPATH += ../../../../qmake/library
|
||||
SOURCES += \
|
||||
tst_qmakelib.cpp \
|
||||
ioutils.cpp \
|
||||
proitems.cpp
|
||||
proitems.cpp \
|
||||
qmakevfs.cpp \
|
||||
qmakeparser.cpp
|
||||
|
||||
DEFINES += PROPARSER_DEBUG
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user