diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h
index 05f79259d2fb9f8b5319fbb05b285ee0153fd9e2..71db0048617fcac0542aab0a69db488d1bc17fed 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 dfa55a091a7f32837b971249166d57b5ceaa1fce..4ef48b51cabf19ed967f8c3ee3381a2534042f69 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 5860d62ec29e302726aad57638c16e78889ae0c0..e9f0f203934e1396b74930368f0b57fb1370a346 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 69f4e9651291c9b15fdd9316da3903c731964f89..2eb762331e76bcd92e0d72c2528ab886b77a9e65 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 ef0f19367331a52699bddfd3773792d15eb428b2..0dbc3ce07be49bbe57e8770a99c56200eb2c1910 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 4f21d8e01790a2a9656e7c77b8cb5369552cfbad..8d5c3edf9a876015dc15c23c7d303283e731e22f 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 {