diff options
-rw-r--r-- | src/libs/qmljs/qmljscheck.cpp | 49 | ||||
-rw-r--r-- | src/libs/qmljs/qmljscheck.h | 5 | ||||
-rw-r--r-- | tests/auto/qml/codemodel/check/case-fallthrough.qml | 42 |
3 files changed, 84 insertions, 12 deletions
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 0b5eb1943c..3b1bde0438 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1086,15 +1086,24 @@ bool Check::visit(DoWhileStatement *ast) return true; } -bool Check::visit(CaseClause *ast) +bool Check::visit(CaseBlock *ast) { - checkEndsWithControlFlow(ast->statements, ast->caseToken); - return true; -} - -bool Check::visit(DefaultClause *ast) -{ - checkEndsWithControlFlow(ast->statements, ast->defaultToken); + QList< QPair<SourceLocation, StatementList *> > clauses; + for (CaseClauses *it = ast->clauses; it; it = it->next) + clauses += qMakePair(it->clause->caseToken, it->clause->statements); + if (ast->defaultClause) + clauses += qMakePair(ast->defaultClause->defaultToken, ast->defaultClause->statements); + for (CaseClauses *it = ast->moreClauses; it; it = it->next) + clauses += qMakePair(it->clause->caseToken, it->clause->statements); + + for (int i = 0; i < clauses.size(); ++i) { + SourceLocation nextToken; + if (i + 1 < clauses.size()) + nextToken = clauses[i + 1].first; + else + nextToken = ast->rbraceToken; + checkCaseFallthrough(clauses[i].second, clauses[i].first, nextToken); + } return true; } @@ -1435,13 +1444,35 @@ void Check::checkAssignInCondition(AST::ExpressionNode *condition) } } -void Check::checkEndsWithControlFlow(StatementList *statements, SourceLocation errorLoc) +void Check::checkCaseFallthrough(StatementList *statements, SourceLocation errorLoc, SourceLocation nextLoc) { if (!statements) return; ReachesEndCheck check; if (check(statements)) { + // check for fallthrough comments + if (nextLoc.isValid()) { + quint32 afterLastStatement = 0; + for (StatementList *it = statements; it; it = it->next) { + if (!it->next) + afterLastStatement = it->statement->lastSourceLocation().end(); + } + + foreach (const SourceLocation &comment, _doc->engine()->comments()) { + if (comment.begin() < afterLastStatement + || comment.end() > nextLoc.begin()) + continue; + + const QString &commentText = _doc->source().mid(comment.begin(), comment.length); + if (commentText.contains(QLatin1String("fall through")) + || commentText.contains(QLatin1String("fall-through")) + || commentText.contains(QLatin1String("fallthrough"))) { + return; + } + } + } + addMessage(WarnCaseWithoutFlowControl, errorLoc); } } diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 6c8f88396d..39ed5493c5 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -90,8 +90,7 @@ protected: virtual bool visit(AST::LocalForStatement *ast); virtual bool visit(AST::WhileStatement *ast); virtual bool visit(AST::DoWhileStatement *ast); - virtual bool visit(AST::CaseClause *ast); - virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::CaseBlock *ast); virtual bool visit(AST::NewExpression *ast); virtual bool visit(AST::NewMemberExpression *ast); virtual bool visit(AST::CallExpression *ast); @@ -108,7 +107,7 @@ private: AST::UiObjectInitializer *initializer); const Value *checkScopeObjectMember(const AST::UiQualifiedId *id); void checkAssignInCondition(AST::ExpressionNode *condition); - void checkEndsWithControlFlow(AST::StatementList *statements, AST::SourceLocation errorLoc); + void checkCaseFallthrough(AST::StatementList *statements, AST::SourceLocation errorLoc, AST::SourceLocation nextLoc); void checkProperty(QmlJS::AST::UiQualifiedId *); void checkNewExpression(AST::ExpressionNode *node); void checkBindingRhs(AST::Statement *statement); diff --git a/tests/auto/qml/codemodel/check/case-fallthrough.qml b/tests/auto/qml/codemodel/check/case-fallthrough.qml new file mode 100644 index 0000000000..f98bcf2b0e --- /dev/null +++ b/tests/auto/qml/codemodel/check/case-fallthrough.qml @@ -0,0 +1,42 @@ +import QtQuick 2.0 + +Item { + x: { + switch (a) { + case 1: + case 2: + x = 1 + // fallthrough + case 3: + break + case 4: + continue + case 5: + case 6: + default: + case 8: + return + case 10: + x = 1 + break + case 9: + } + switch (a) { + case 1: + case 2: // 20 9 12 + x = 1 + case 3: + break + case 4: // 20 9 12 + function a() { + + } + case 5: + case 6: + default: + case 8: // 20 9 12 + x = 1 + case 9: + } + } +} |