summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeinz Wiesinger <pprkut@liwjatan.at>2017-05-18 07:07:15 (GMT)
committerHeinz Wiesinger <pprkut@liwjatan.at>2017-06-07 18:37:46 (GMT)
commit077b02757c301e95d2d2f1e0f74be6a2c63ba0ab (patch)
treeb1bfca5c64455011ddf746f8e1124b9a6c6391e7
parent64c99f807abbb2b766dd70c0c0330da25245dc98 (diff)
Implement syntax support for function argument unpacking.
Summary: https://wiki.php.net/rfc/argument_unpacking Reviewers: mwolff Reviewed By: mwolff Subscribers: mwolff, kdevelop-devel Differential Revision: https://phabricator.kde.org/D5908
-rw-r--r--duchain/builders/declarationbuilder.cpp12
-rw-r--r--duchain/builders/declarationbuilder.h2
-rw-r--r--duchain/tests/duchain.cpp19
-rw-r--r--duchain/tests/duchain.h1
-rw-r--r--duchain/tests/expressionparser.cpp13
-rw-r--r--duchain/tests/expressionparser.h1
-rw-r--r--parser/php.g2
7 files changed, 45 insertions, 5 deletions
diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp
index 1710bdf..e9995ac 100644
--- a/duchain/builders/declarationbuilder.cpp
+++ b/duchain/builders/declarationbuilder.cpp
@@ -1217,12 +1217,10 @@ void DeclarationBuilder::visitFunctionCall(FunctionCallAst* node)
void DeclarationBuilder::visitFunctionCallParameterList(FunctionCallParameterListAst* node)
{
- int oldPos = m_functionCallParameterPos;
- m_functionCallParameterPos = 0;
+ PushValue<FunctionCallParameterListElementAst*> push(m_functionCallPreviousArgument, 0);
+ PushValue<int> pos(m_functionCallParameterPos, 0);
DeclarationBuilderBase::visitFunctionCallParameterList(node);
-
- m_functionCallParameterPos = oldPos;
}
void DeclarationBuilder::visitFunctionCallParameterListElement(FunctionCallParameterListElementAst* node)
@@ -1245,6 +1243,12 @@ void DeclarationBuilder::visitFunctionCallParameterListElement(FunctionCallParam
}
}
+ 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/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h
index 418d768..5538308 100644
--- a/duchain/builders/declarationbuilder.h
+++ b/duchain/builders/declarationbuilder.h
@@ -145,6 +145,8 @@ private:
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 = nullptr;
unsigned int m_currentModifers;
QString m_lastTopStatementComment;
diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp
index b30185e..74eeb6b 100644
--- a/duchain/tests/duchain.cpp
+++ b/duchain/tests/duchain.cpp
@@ -3044,3 +3044,22 @@ void TestDUChain::staticFunctionClassPhp54()
ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
QCOMPARE(classDec->uses().count(),1);
}
+
+void TestDUChain::functionArgumentUnpacking()
+{
+ // 0 1 2 3 4 5 6 7
+ // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ QByteArray method("<?php\n"
+ "$a = [ 1,2 ];\n"
+ "$b = [ 3,4 ];\n"
+ "function aaa($c,$d,$e,$f) { }\n"
+ "aaa(...$a, ...$b);\n");
+
+ TopDUContext* top = parse(method);
+ QVERIFY(top);
+ DUChainReleaser releaseTop(top);
+ DUChainWriteLocker lock(DUChain::lock());
+
+ QVERIFY(top->problems().isEmpty());
+ QCOMPARE(top->localDeclarations().count(),3);
+}
diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h
index 4713d84..96f5d01 100644
--- a/duchain/tests/duchain.h
+++ b/duchain/tests/duchain.h
@@ -153,6 +153,7 @@ private slots:
void useThisAsArray();
void wrongUseOfThisAsArray();
void staticFunctionClassPhp54();
+ void functionArgumentUnpacking();
};
}
diff --git a/duchain/tests/expressionparser.cpp b/duchain/tests/expressionparser.cpp
index b9e0c4c..0a5fd96 100644
--- a/duchain/tests/expressionparser.cpp
+++ b/duchain/tests/expressionparser.cpp
@@ -681,5 +681,18 @@ void TestExpressionParser::invalidVariadicFunction()
QVERIFY(!top->problems().isEmpty());
}
+void TestExpressionParser::invalidArgumentUnpacking()
+{
+ // 0 1 2 3 4 5 6 7
+ // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ QByteArray method("<? function foo($a, $b, $c){}; $d = [ 1, 2 ]; foo(...$d, $e);");
+
+ TopDUContext* top = parse(method, DumpNone);
+ DUChainReleaser releaseTop(top);
+ DUChainWriteLocker lock;
+
+ QVERIFY(!top->problems().isEmpty());
+}
+
}
diff --git a/duchain/tests/expressionparser.h b/duchain/tests/expressionparser.h
index d5aac89..f50b7d6 100644
--- a/duchain/tests/expressionparser.h
+++ b/duchain/tests/expressionparser.h
@@ -64,6 +64,7 @@ private slots:
void classNameConstant();
void invalidVariadicFunction_data();
void invalidVariadicFunction();
+ void invalidArgumentUnpacking();
};
}
diff --git a/parser/php.g b/parser/php.g
index a15d1fd..4a15123 100644
--- a/parser/php.g
+++ b/parser/php.g
@@ -599,7 +599,7 @@ expression=booleanOrExpression
#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