summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2012-09-11 19:30:29 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-05-21 14:43:04 +0200
commit8591d77fa6d3383c07c373970e59362ff16b143f (patch)
treeaf4036b67749f7fffd26892824137ffff8e5b9c7
parentf0e0cd9baf295f6fe8d7575762aedea7d4ee035e (diff)
downloadqttools-8591d77fa6d3383c07c373970e59362ff16b143f.tar.gz
don't pretend that break()/next()/return() are functions
it's a pretty braindead thing to implement control flow statements as (built-in) functions. as a "side effect", this fixes return() value handling for lists. (cherry picked from qtcreator/f53ed6c4b3feca59a94d4f0de8b1a7411122e30e) (cherry picked from qtcreator/f529e22ec38fb9a656d74394e484d2453cf42c69) Change-Id: I59c8efa0e4d65329327115f7f8ed20719e7f7546 Reviewed-by: Qt Doc Bot <qt_docbot@qt-project.org> Reviewed-by: Joerg Bornemann <joerg.bornemann@nokia.com> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com> (cherry picked from qtbase/8400896cfe3fbef7666329a2920bd0dbdd5890af) Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
-rw-r--r--src/linguist/shared/proitems.h3
-rw-r--r--src/linguist/shared/qmakebuiltins.cpp30
-rw-r--r--src/linguist/shared/qmakeevaluator.cpp24
-rw-r--r--src/linguist/shared/qmakeevaluator.h1
-rw-r--r--src/linguist/shared/qmakeparser.cpp47
-rw-r--r--src/linguist/shared/qmakeparser.h9
6 files changed, 77 insertions, 37 deletions
diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h
index 05f79259d..71db00486 100644
--- a/src/linguist/shared/proitems.h
+++ b/src/linguist/shared/proitems.h
@@ -307,6 +307,9 @@ enum ProToken {
TokTestCall, // previous literal/expansion is a test function call
// - ((nested expansion + TokArgSeparator)* + nested expansion)?
// - TokFuncTerminator
+ TokReturn, // previous literal/expansion is a return value
+ TokBreak, // break loop
+ TokNext, // shortcut to next loop iteration
TokNot, // '!' operator
TokAnd, // ':' operator
TokOr, // '|' operator
diff --git a/src/linguist/shared/qmakebuiltins.cpp b/src/linguist/shared/qmakebuiltins.cpp
index dfa55a091..4ef48b51c 100644
--- a/src/linguist/shared/qmakebuiltins.cpp
+++ b/src/linguist/shared/qmakebuiltins.cpp
@@ -96,7 +96,7 @@ enum ExpandFunc {
enum TestFunc {
T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
- T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE,
+ T_DEFINED, T_CONTAINS, T_INFILE,
T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
};
@@ -168,9 +168,6 @@ void QMakeEvaluator::initFunctionStatics()
{ "if", T_IF },
{ "isActiveConfig", T_CONFIG },
{ "system", T_SYSTEM },
- { "return", T_RETURN },
- { "break", T_BREAK },
- { "next", T_NEXT },
{ "defined", T_DEFINED },
{ "contains", T_CONTAINS },
{ "infile", T_INFILE },
@@ -1071,17 +1068,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
return returnBool(m_functionDefs.replaceFunctions.contains(var)
|| m_functionDefs.testFunctions.contains(var));
}
- case T_RETURN:
- m_returnValue = args;
- // It is "safe" to ignore returns - due to qmake brokeness
- // they cannot be used to terminate loops anyway.
- if (m_cumulative)
- return ReturnTrue;
- if (m_valuemapStack.size() == 1) {
- evalError(fL1S("unexpected return()."));
- return ReturnFalse;
- }
- return ReturnReturn;
case T_EXPORT: {
if (args.count() != 1) {
evalError(fL1S("export(variable) requires one argument."));
@@ -1152,20 +1138,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
}
return ret;
}
- case T_BREAK:
- if (m_skipLevel)
- return ReturnFalse;
- if (m_loopLevel)
- return ReturnBreak;
- evalError(fL1S("Unexpected break()."));
- return ReturnFalse;
- case T_NEXT:
- if (m_skipLevel)
- return ReturnFalse;
- if (m_loopLevel)
- return ReturnNext;
- evalError(fL1S("Unexpected next()."));
- return ReturnFalse;
case T_IF: {
if (args.count() != 1) {
evalError(fL1S("if(condition) requires one argument."));
diff --git a/src/linguist/shared/qmakeevaluator.cpp b/src/linguist/shared/qmakeevaluator.cpp
index 5860d62ec..e9f0f2039 100644
--- a/src/linguist/shared/qmakeevaluator.cpp
+++ b/src/linguist/shared/qmakeevaluator.cpp
@@ -195,7 +195,6 @@ QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
#ifdef PROEVALUATOR_CUMULATIVE
m_skipLevel = 0;
#endif
- m_loopLevel = 0;
m_listCount = 0;
m_valuemapStack.push(ProValueMap());
m_valuemapInited = false;
@@ -683,6 +682,24 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
invert = false;
curr.clear();
continue;
+ case TokReturn:
+ m_returnValue = curr;
+ curr.clear();
+ ret = ReturnReturn;
+ goto ctrlstm;
+ case TokBreak:
+ ret = ReturnBreak;
+ goto ctrlstm;
+ case TokNext:
+ ret = ReturnNext;
+ ctrlstm:
+ if (!m_skipLevel && okey != or_op) {
+ traceMsg("flow control statement '%s', aborting block", dbgReturn(ret));
+ return ret;
+ }
+ traceMsg("skipped flow control statement '%s'", dbgReturn(ret));
+ okey = false, or_op = true; // force next evaluation
+ continue;
default: {
const ushort *oTokPtr = --tokPtr;
evaluateExpression(tokPtr, &curr, false);
@@ -762,7 +779,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
else
traceMsg("entering loop for %s over %s", dbgKey(variable), dbgStrList(list));
- m_loopLevel++;
forever {
if (infinite) {
if (!variable.isEmpty())
@@ -799,7 +815,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
}
}
do_break:
- m_loopLevel--;
traceMsg("done looping");
@@ -1589,8 +1604,6 @@ ProStringList QMakeEvaluator::evaluateFunction(
} else {
m_valuemapStack.push(ProValueMap());
m_locationStack.push(m_current);
- int loopLevel = m_loopLevel;
- m_loopLevel = 0;
ProStringList args;
for (int i = 0; i < argumentsList.count(); ++i) {
@@ -1603,7 +1616,6 @@ ProStringList QMakeEvaluator::evaluateFunction(
ret = m_returnValue;
m_returnValue.clear();
- m_loopLevel = loopLevel;
m_current = m_locationStack.pop();
m_valuemapStack.pop();
}
diff --git a/src/linguist/shared/qmakeevaluator.h b/src/linguist/shared/qmakeevaluator.h
index 69f4e9651..2eb762331 100644
--- a/src/linguist/shared/qmakeevaluator.h
+++ b/src/linguist/shared/qmakeevaluator.h
@@ -233,7 +233,6 @@ public:
static void removeEach(ProStringList *varlist, const ProStringList &value);
QMakeEvaluator *m_caller;
- int m_loopLevel; // To report unexpected break() and next()s
#ifdef PROEVALUATOR_CUMULATIVE
bool m_cumulative;
int m_skipLevel;
diff --git a/src/linguist/shared/qmakeparser.cpp b/src/linguist/shared/qmakeparser.cpp
index ef0f19367..0dbc3ce07 100644
--- a/src/linguist/shared/qmakeparser.cpp
+++ b/src/linguist/shared/qmakeparser.cpp
@@ -108,6 +108,9 @@ static struct {
QString strdefineTest;
QString strdefineReplace;
QString stroption;
+ QString strreturn;
+ QString strnext;
+ QString strbreak;
QString strhost_build;
QString strLINE;
QString strFILE;
@@ -128,6 +131,9 @@ void QMakeParser::initialize()
statics.strdefineTest = QLatin1String("defineTest");
statics.strdefineReplace = QLatin1String("defineReplace");
statics.stroption = QLatin1String("option");
+ statics.strreturn = QLatin1String("return");
+ statics.strnext = QLatin1String("next");
+ statics.strbreak = QLatin1String("break");
statics.strhost_build = QLatin1String("host_build");
statics.strLINE = QLatin1String("_LINE_");
statics.strFILE = QLatin1String("_FILE_");
@@ -874,9 +880,11 @@ void QMakeParser::putLineMarker(ushort *&tokPtr)
void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
{
+ uchar nest = m_blockstack.top().nest;
m_blockstack.resize(m_blockstack.size() + 1);
m_blockstack.top().special = special;
m_blockstack.top().start = tokPtr;
+ m_blockstack.top().nest = nest;
tokPtr += 2;
m_state = state;
m_canElse = false;
@@ -1016,6 +1024,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
m_tmp.setRawData((QChar *)uc + 4, nlen);
const QString *defName;
ushort defType;
+ uchar nest;
if (m_tmp == statics.strfor) {
if (m_invert || m_operator == OrOperator) {
// '|' could actually work reasonably, but qmake does nonsense here.
@@ -1038,6 +1047,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
didFor:
putTok(tokPtr, TokValueTerminator);
enterScope(tokPtr, true, StCtrl);
+ m_blockstack.top().nest |= NestLoop;
return;
} else if (*uc == TokArgSeparator && argc == 2) {
// for(var, something)
@@ -1084,11 +1094,48 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
putTok(tokPtr, defType);
putHashStr(tokPtr, uce + 2, nlen);
enterScope(tokPtr, true, StCtrl);
+ m_blockstack.top().nest = NestFunction;
return;
}
}
parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
return;
+ } else if (m_tmp == statics.strreturn) {
+ if (argc > 1) {
+ parseError(fL1S("return() requires zero or one argument."));
+ bogusTest(tokPtr);
+ return;
+ }
+ defType = TokReturn;
+ nest = NestFunction;
+ goto ctrlstm2;
+ } else if (m_tmp == statics.strnext) {
+ defType = TokNext;
+ goto ctrlstm;
+ } else if (m_tmp == statics.strbreak) {
+ defType = TokBreak;
+ ctrlstm:
+ if (*uce != TokFuncTerminator) {
+ parseError(fL1S("%1() requires zero arguments.").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ nest = NestLoop;
+ ctrlstm2:
+ if (m_invert) {
+ parseError(fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ if (!(m_blockstack.top().nest & nest)) {
+ parseError(fL1S("Unexpected %1().").arg(m_tmp));
+ bogusTest(tokPtr);
+ return;
+ }
+ finalizeTest(tokPtr);
+ putBlock(tokPtr, uce, ptr - uce - 1); // Only for TokReturn
+ putTok(tokPtr, defType);
+ return;
} else if (m_tmp == statics.stroption) {
if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1
|| m_invert || m_operator != NoOperator) {
diff --git a/src/linguist/shared/qmakeparser.h b/src/linguist/shared/qmakeparser.h
index 4f21d8e01..8d5c3edf9 100644
--- a/src/linguist/shared/qmakeparser.h
+++ b/src/linguist/shared/qmakeparser.h
@@ -97,13 +97,20 @@ public:
void discardFileFromCache(const QString &fileName);
private:
+ enum ScopeNesting {
+ NestNone = 0,
+ NestLoop = 1,
+ NestFunction = 2
+ };
+
struct BlockScope {
- BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {}
+ BlockScope() : start(0), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
BlockScope(const BlockScope &other) { *this = other; }
ushort *start; // Where this block started; store length here
int braceLevel; // Nesting of braces in scope
bool special; // Single-line conditionals inside loops, etc. cannot have else branches
bool inBranch; // The 'else' branch of the previous TokBranch is still open
+ uchar nest; // Into what control structures we are nested
};
enum ScopeState {