diff --git a/duchain/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h --- a/duchain/builders/declarationbuilder.h +++ b/duchain/builders/declarationbuilder.h @@ -145,6 +145,8 @@ KDevelop::FunctionType::Ptr m_currentFunctionType; /// The AstNode of the previous function declaration argument ParameterAst *m_functionDeclarationPreviousArgument; + /// The AstNode of the previous function call argument + FunctionCallParameterListElementAst *m_functionCallPreviousArgument; unsigned int m_currentModifers; QString m_lastTopStatementComment; diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -1217,12 +1217,10 @@ void DeclarationBuilder::visitFunctionCallParameterList(FunctionCallParameterListAst* node) { - int oldPos = m_functionCallParameterPos; - m_functionCallParameterPos = 0; + PushValue push(m_functionCallPreviousArgument, 0); + PushValue pos(m_functionCallParameterPos, 0); DeclarationBuilderBase::visitFunctionCallParameterList(node); - - m_functionCallParameterPos = oldPos; } void DeclarationBuilder::visitFunctionCallParameterListElement(FunctionCallParameterListElementAst* node) @@ -1245,6 +1243,12 @@ } } + if (m_functionCallPreviousArgument && m_functionCallPreviousArgument->isVariadic != -1 && node->isVariadic == -1) { + reportError(i18n("Cannot use positional argument after argument unpacking"), node); + } + + m_functionCallPreviousArgument = node; + ++m_functionCallParameterPos; } diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h --- a/duchain/tests/duchain.h +++ b/duchain/tests/duchain.h @@ -153,6 +153,7 @@ void useThisAsArray(); void wrongUseOfThisAsArray(); void staticFunctionClassPhp54(); + void functionArgumentUnpacking(); }; } diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp --- a/duchain/tests/duchain.cpp +++ b/duchain/tests/duchain.cpp @@ -3044,3 +3044,22 @@ ClassDeclaration* classDec = dynamic_cast(dec); QCOMPARE(classDec->uses().count(),1); } + +void TestDUChain::functionArgumentUnpacking() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("problems().isEmpty()); + QCOMPARE(top->localDeclarations().count(),3); +} diff --git a/duchain/tests/expressionparser.h b/duchain/tests/expressionparser.h --- a/duchain/tests/expressionparser.h +++ b/duchain/tests/expressionparser.h @@ -64,6 +64,7 @@ void classNameConstant(); void invalidVariadicFunction_data(); void invalidVariadicFunction(); + void invalidArgumentUnpacking(); }; } diff --git a/duchain/tests/expressionparser.cpp b/duchain/tests/expressionparser.cpp --- a/duchain/tests/expressionparser.cpp +++ b/duchain/tests/expressionparser.cpp @@ -681,5 +681,18 @@ QVERIFY(!top->problems().isEmpty()); } +void TestExpressionParser::invalidArgumentUnpacking() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("problems().isEmpty()); +} + } diff --git a/parser/php.g b/parser/php.g --- a/parser/php.g +++ b/parser/php.g @@ -599,7 +599,7 @@ #parameters=functionCallParameterListElement @ COMMA | 0 -> functionCallParameterList ;; - (BIT_AND variable=variable) | expr=expr + (BIT_AND variable=variable) | (isVariadic=ELLIPSIS variable=variable) | expr=expr -> functionCallParameterListElement ;; #element=assignmentListElement @COMMA