summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--qmake/library/proitems.h3
-rw-r--r--qmake/library/qmakeevaluator.cpp18
-rw-r--r--qmake/library/qmakeparser.cpp26
-rw-r--r--tests/auto/tools/qmakelib/evaltest.cpp25
-rw-r--r--tests/auto/tools/qmakelib/parsertest.cpp69
5 files changed, 140 insertions, 1 deletions
diff --git a/qmake/library/proitems.h b/qmake/library/proitems.h
index 05d9e8da28..c81e205699 100644
--- a/qmake/library/proitems.h
+++ b/qmake/library/proitems.h
@@ -329,6 +329,9 @@ enum ProToken {
// - function name: hash (2), length (1), chars (length)
// - body length (2)
// - body + TokTerminator (body length)
+ TokBypassNesting, // escape from function local variable scopes:
+ // - block length (2)
+ // - block + TokTerminator (block length)
TokMask = 0xff,
TokQuoted = 0x100, // The expression is quoted => join expanded stringlist
TokNewStr = 0x200 // Next stringlist element
diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp
index cc57aa7f2b..4ca87e8bc7 100644
--- a/qmake/library/qmakeevaluator.cpp
+++ b/qmake/library/qmakeevaluator.cpp
@@ -594,6 +594,24 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
tokPtr += blockLen;
okey = true, or_op = false; // force next evaluation
break;
+ case TokBypassNesting:
+ blockLen = getBlockLen(tokPtr);
+ if ((m_cumulative || okey != or_op) && blockLen) {
+ ProValueMapStack savedValuemapStack = m_valuemapStack;
+ m_valuemapStack.clear();
+ m_valuemapStack.append(savedValuemapStack.takeFirst());
+ traceMsg("visiting nesting-bypassing block");
+ ret = visitProBlock(tokPtr);
+ traceMsg("visited nesting-bypassing block");
+ savedValuemapStack.prepend(m_valuemapStack.first());
+ m_valuemapStack = savedValuemapStack;
+ } else {
+ traceMsg("skipped nesting-bypassing block");
+ ret = ReturnTrue;
+ }
+ tokPtr += blockLen;
+ okey = true, or_op = false; // force next evaluation
+ break;
case TokTestDef:
case TokReplaceDef:
if (m_cumulative || okey != or_op) {
diff --git a/qmake/library/qmakeparser.cpp b/qmake/library/qmakeparser.cpp
index 56b217dfbb..78350c76c4 100644
--- a/qmake/library/qmakeparser.cpp
+++ b/qmake/library/qmakeparser.cpp
@@ -118,6 +118,7 @@ static struct {
QString strfor;
QString strdefineTest;
QString strdefineReplace;
+ QString strbypassNesting;
QString stroption;
QString strreturn;
QString strnext;
@@ -141,6 +142,7 @@ void QMakeParser::initialize()
statics.strfor = QLatin1String("for");
statics.strdefineTest = QLatin1String("defineTest");
statics.strdefineReplace = QLatin1String("defineReplace");
+ statics.strbypassNesting = QLatin1String("bypassNesting");
statics.stroption = QLatin1String("option");
statics.strreturn = QLatin1String("return");
statics.strnext = QLatin1String("next");
@@ -1157,6 +1159,25 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
}
parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
return;
+ } else if (m_tmp == statics.strbypassNesting) {
+ if (*uce != TokFuncTerminator) {
+ bogusTest(tokPtr, fL1S("%1() requires zero arguments.").arg(m_tmp));
+ return;
+ }
+ if (!(m_blockstack.top().nest & NestFunction)) {
+ bogusTest(tokPtr, fL1S("Unexpected %1().").arg(m_tmp));
+ return;
+ }
+ if (m_invert) {
+ bogusTest(tokPtr, fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp));
+ return;
+ }
+ flushScopes(tokPtr);
+ putLineMarker(tokPtr);
+ putOperator(tokPtr);
+ putTok(tokPtr, TokBypassNesting);
+ enterScope(tokPtr, true, StCtrl);
+ return;
} else if (m_tmp == statics.strreturn) {
if (m_blockstack.top().nest & NestFunction) {
if (argc > 1) {
@@ -1425,7 +1446,7 @@ static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outS
"TokReturn", "TokBreak", "TokNext",
"TokNot", "TokAnd", "TokOr",
"TokBranch", "TokForLoop",
- "TokTestDef", "TokReplaceDef"
+ "TokTestDef", "TokReplaceDef", "TokBypassNesting"
};
while (offset != limit) {
@@ -1509,6 +1530,9 @@ static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outS
if (ok)
ok = getSubBlock(tokens, limit, offset, outStr, indent, "body");
break;
+ case TokBypassNesting:
+ ok = getSubBlock(tokens, limit, offset, outStr, indent, "block");
+ break;
default:
Q_ASSERT(!"unhandled token");
}
diff --git a/tests/auto/tools/qmakelib/evaltest.cpp b/tests/auto/tools/qmakelib/evaltest.cpp
index e3be294e5f..4e215b8570 100644
--- a/tests/auto/tools/qmakelib/evaltest.cpp
+++ b/tests/auto/tools/qmakelib/evaltest.cpp
@@ -633,6 +633,31 @@ void tst_qmakelib::addControlStructs()
<< ""
<< true;
+ QTest::newRow("bypassNesting()")
+ << "defineTest(func) {\n"
+ "LOCAL = 1\n"
+ "bypassNesting() {\n"
+ "OUT = 1\n"
+ "!isEmpty(GLOBAL): OUT1 = 1\n"
+ "!isEmpty(LOCAL): OUT2 = 1\n"
+ "}\n"
+ "}\n"
+ "GLOBAL = 1\n"
+ "func()"
+ << "GLOBAL = 1\nLOCAL = UNDEF\nOUT = 1\nOUT1 = 1\nOUT2 = UNDEF"
+ << ""
+ << true;
+
+ QTest::newRow("error() from bypassNesting()")
+ << "defineTest(func) {\n"
+ "bypassNesting() { error(error) }\n"
+ "}\n"
+ "func()\n"
+ "OKE = 1"
+ << "OKE = UNDEF"
+ << "Project ERROR: error"
+ << false;
+
QTest::newRow("top-level return()")
<< "VAR = good\nreturn()\nVAR = bad"
<< "VAR = good"
diff --git a/tests/auto/tools/qmakelib/parsertest.cpp b/tests/auto/tools/qmakelib/parsertest.cpp
index dc92f98f45..70f1be5fc3 100644
--- a/tests/auto/tools/qmakelib/parsertest.cpp
+++ b/tests/auto/tools/qmakelib/parsertest.cpp
@@ -1684,6 +1684,57 @@ void tst_qmakelib::addParseCustomFunctions()
/* 22 */ << H(TokTerminator))
<< ""
<< true;
+
+ QTest::newRow("bypassNesting()-{return}")
+ << "defineTest(test) { bypassNesting() { return(true) } }"
+ << TS(
+ /* 0 */ << H(TokLine) << H(1)
+ /* 2 */ << H(TokTestDef) << HS(L"test")
+ /* 10 */ /* body */ << I(16)
+ /* 12 */ << H(TokLine) << H(1)
+ /* 14 */ << H(TokBypassNesting)
+ /* 15 */ /* block */ << I(10)
+ /* 17 */ << H(TokLine) << H(1)
+ /* 19 */ << H(TokLiteral | TokNewStr) << S(L"true")
+ /* 25 */ << H(TokReturn)
+ /* 26 */ << H(TokTerminator)
+ /* 27 */ << H(TokTerminator))
+ << ""
+ << true;
+
+ QTest::newRow("test-AND-bypassNesting()-{}")
+ << "defineTest(test) { test: bypassNesting() {} }"
+ << TS(
+ /* 0 */ << H(TokLine) << H(1)
+ /* 2 */ << H(TokTestDef) << HS(L"test")
+ /* 10 */ /* body */ << I(17)
+ /* 12 */ << H(TokLine) << H(1)
+ /* 14 */ << H(TokHashLiteral) << HS(L"test")
+ /* 22 */ << H(TokCondition)
+ /* 23 */ << H(TokAnd)
+ /* 24 */ << H(TokBypassNesting)
+ /* 25 */ /* block */ << I(1)
+ /* 27 */ << H(TokTerminator)
+ /* 28 */ << H(TokTerminator))
+ << ""
+ << true;
+
+ QTest::newRow("test-OR-bypassNesting()-{}")
+ << "defineTest(test) { test| bypassNesting() {} }"
+ << TS(
+ /* 0 */ << H(TokLine) << H(1)
+ /* 2 */ << H(TokTestDef) << HS(L"test")
+ /* 10 */ /* body */ << I(17)
+ /* 12 */ << H(TokLine) << H(1)
+ /* 14 */ << H(TokHashLiteral) << HS(L"test")
+ /* 22 */ << H(TokCondition)
+ /* 23 */ << H(TokOr)
+ /* 24 */ << H(TokBypassNesting)
+ /* 25 */ /* block */ << I(1)
+ /* 27 */ << H(TokTerminator)
+ /* 28 */ << H(TokTerminator))
+ << ""
+ << true;
}
void tst_qmakelib::addParseAbuse()
@@ -1736,6 +1787,24 @@ void tst_qmakelib::addParseAbuse()
<< "in:1: Unexpected NOT operator in front of function definition."
<< false;
+ QTest::newRow("outer-bypassNesting()-{}")
+ << "bypassNesting() {}"
+ << TS()
+ << "in:1: Unexpected bypassNesting()."
+ << false;
+
+ QTest::newRow("bypassNesting(arg)-{}")
+ << "defineTest(test) { bypassNesting(arg) {} }"
+ << TS()
+ << "in:1: bypassNesting() requires zero arguments."
+ << false;
+
+ QTest::newRow("NOT-bypassNesting()-{}")
+ << "defineTest(test) { !bypassNesting() {} }"
+ << TS()
+ << "in:1: Unexpected NOT operator in front of bypassNesting()."
+ << false;
+
QTest::newRow("AND-test")
<< ":test"
<< TS(