summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeinz Wiesinger <pprkut@liwjatan.at>2017-05-03 19:04:32 (GMT)
committerHeinz Wiesinger <pprkut@liwjatan.at>2017-05-11 19:01:31 (GMT)
commit17202e149e5495766334bb7fb4ce821165d4bf79 (patch)
treea0e959972a2c2d8bb0f3cf5c57165df2f42e5c46
parentf1491b8df01cf2460001a06a40f97b6c3f4d47b1 (diff)
Add support for variadic functions.
Summary: http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list Subscribers: kdevelop-devel Differential Revision: https://phabricator.kde.org/D5703
-rw-r--r--completion/context.cpp1
-rw-r--r--duchain/builders/declarationbuilder.cpp19
-rw-r--r--duchain/builders/declarationbuilder.h3
-rw-r--r--duchain/builders/typebuilder.cpp4
-rw-r--r--duchain/tests/duchain.cpp22
-rw-r--r--duchain/tests/duchain.h1
-rw-r--r--duchain/tests/expressionparser.cpp20
-rw-r--r--duchain/tests/expressionparser.h2
-rw-r--r--parser/php.g4
-rw-r--r--parser/phplexer.cpp3
-rw-r--r--parser/test/lexertest.cpp18
-rw-r--r--parser/test/lexertest.h1
12 files changed, 94 insertions, 4 deletions
diff --git a/completion/context.cpp b/completion/context.cpp
index ba30b1f..76d1503 100644
--- a/completion/context.cpp
+++ b/completion/context.cpp
@@ -504,6 +504,7 @@ CodeCompletionContext::CodeCompletionContext(KDevelop::DUContextPointer context,
case Parser::Token_DOUBLE_CAST:
case Parser::Token_DOUBLE_QUOTE:
case Parser::Token_ECHO:
+ case Parser::Token_ELLIPSIS:
case Parser::Token_ENCAPSED_AND_WHITESPACE:
case Parser::Token_EXIT:
case Parser::Token_INC:
diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp
index 7ae53e1..c0a8cff 100644
--- a/duchain/builders/declarationbuilder.cpp
+++ b/duchain/builders/declarationbuilder.cpp
@@ -762,14 +762,24 @@ void DeclarationBuilder::createTraitAliasDeclarations(TraitAliasStatementAst *no
}
}
+void DeclarationBuilder::visitParameterList(ParameterListAst* node)
+{
+ PushValue<ParameterAst*> push(m_functionDeclarationPreviousArgument, 0);
+
+ DeclarationBuilderBase::visitParameterList(node);
+}
+
void DeclarationBuilder::visitParameter(ParameterAst *node)
{
AbstractFunctionDeclaration* funDec = dynamic_cast<AbstractFunctionDeclaration*>(currentDeclaration());
Q_ASSERT(funDec);
+
if (node->defaultValue) {
QString symbol = m_editor->parseSession()->symbol(node->defaultValue);
funDec->addDefaultParameter(IndexedString(symbol));
- if ( node->parameterType && symbol.compare(QLatin1String("null"), Qt::CaseInsensitive) != 0 ) {
+ if (node->isVariadic != -1) {
+ reportError(i18n("Variadic parameter cannot have a default value"), node->defaultValue);
+ } else if ( node->parameterType && symbol.compare(QLatin1String("null"), Qt::CaseInsensitive) != 0 ) {
reportError(i18n("Default value for parameters with a class type hint can only be NULL."), node->defaultValue);
}
} else if ( !node->defaultValue && funDec->defaultParametersSize() ) {
@@ -784,7 +794,14 @@ void DeclarationBuilder::visitParameter(ParameterAst *node)
}
DeclarationBuilderBase::visitParameter(node);
+
+ if (m_functionDeclarationPreviousArgument && m_functionDeclarationPreviousArgument->isVariadic != -1) {
+ reportError(i18n("Only the last parameter can be variadic."), m_functionDeclarationPreviousArgument);
+ }
+
closeDeclaration();
+
+ m_functionDeclarationPreviousArgument = node;
}
void DeclarationBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node)
diff --git a/duchain/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h
index 39a7d41..418d768 100644
--- a/duchain/builders/declarationbuilder.h
+++ b/duchain/builders/declarationbuilder.h
@@ -70,6 +70,7 @@ protected:
virtual void importTraitMethods(ClassStatementAst *node);
void visitClassExtends(ClassExtendsAst *node) override;
void visitClassImplements(ClassImplementsAst *node) override;
+ void visitParameterList(ParameterListAst *node) override;
void visitParameter(ParameterAst *node) override;
void visitFunctionDeclarationStatement(FunctionDeclarationStatementAst *node) override;
void visitClassVariable(ClassVariableAst *node) override;
@@ -142,6 +143,8 @@ private:
int m_functionCallParameterPos;
/// Type of the current function, will only be set inside function calls.
KDevelop::FunctionType::Ptr m_currentFunctionType;
+ /// The AstNode of the previous function declaration argument
+ ParameterAst *m_functionDeclarationPreviousArgument;
unsigned int m_currentModifers;
QString m_lastTopStatementComment;
diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp
index 25b9c15..9730520 100644
--- a/duchain/builders/typebuilder.cpp
+++ b/duchain/builders/typebuilder.cpp
@@ -357,7 +357,9 @@ void TypeBuilder::visitConstantDeclaration(ConstantDeclarationAst* node)
void TypeBuilder::visitParameter(ParameterAst *node)
{
AbstractType::Ptr type;
- if (node->parameterType) {
+ if (node->isVariadic != -1) {
+ type = AbstractType::Ptr(new IntegralType(IntegralType::TypeArray));
+ } else if (node->parameterType) {
//don't use openTypeFromName as it uses cursor for findDeclarations
DeclarationPointer decl = findDeclarationImport(ClassDeclarationType,
identifierForNamespace(node->parameterType, editor()));
diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp
index d2e8fe6..b30185e 100644
--- a/duchain/tests/duchain.cpp
+++ b/duchain/tests/duchain.cpp
@@ -608,6 +608,28 @@ void TestDUChain::declareTypehintFunction()
QCOMPARE(StructureType::Ptr::dynamicCast(fType->returnType())->qualifiedIdentifier(), QualifiedIdentifier("a"));
}
+void TestDUChain::declareVariadicFunction()
+{
+ // 0 1 2 3 4 5 6 7
+ // 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ QByteArray method("<? function foo(...$i) { } ");
+
+ TopDUContext* top = parse(method, DumpAll);
+ DUChainReleaser releaseTop(top);
+
+ DUChainWriteLocker lock(DUChain::lock());
+
+ FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
+ QVERIFY(fun);
+ QCOMPARE(fun->arguments().count(), 1);
+ QVERIFY(IntegralType::Ptr::dynamicCast(fun->arguments().first()));
+ QVERIFY(IntegralType::Ptr::dynamicCast(fun->arguments().first())->dataType() == IntegralType::TypeArray);
+
+ IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
+ QVERIFY(type);
+ QVERIFY(type->dataType() == IntegralType::TypeArray);
+}
+
void TestDUChain::declareTypehintArrayFunction()
{
// 0 1 2 3 4 5 6 7
diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h
index 8edc192..4713d84 100644
--- a/duchain/tests/duchain.h
+++ b/duchain/tests/duchain.h
@@ -37,6 +37,7 @@ private slots:
void declareClass();
void classMemberVar();
void declareTypehintFunction();
+ void declareVariadicFunction();
void declareTypehintArrayFunction();
void declareTypehintCallableFunction();
void returnTypeClass();
diff --git a/duchain/tests/expressionparser.cpp b/duchain/tests/expressionparser.cpp
index 071804c..b9e0c4c 100644
--- a/duchain/tests/expressionparser.cpp
+++ b/duchain/tests/expressionparser.cpp
@@ -661,5 +661,25 @@ void TestExpressionParser::classNameConstant()
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeString));
}
+void TestExpressionParser::invalidVariadicFunction_data()
+{
+ QTest::addColumn<QString>("code");
+
+ QTest::newRow("defaultValue") << "<? function foo(...$i=NULL) { } \n";
+
+ QTest::newRow("multipleVariadics") << "<? function foo(...$i, ...$j) { } \n";
+}
+
+void TestExpressionParser::invalidVariadicFunction()
+{
+ QFETCH(QString, code);
+
+ TopDUContext* top = parse(code.toUtf8(), DumpNone);
+ DUChainReleaser releaseTop(top);
+ DUChainWriteLocker lock;
+
+ QVERIFY(!top->problems().isEmpty());
+}
+
}
diff --git a/duchain/tests/expressionparser.h b/duchain/tests/expressionparser.h
index 44dba03..d5aac89 100644
--- a/duchain/tests/expressionparser.h
+++ b/duchain/tests/expressionparser.h
@@ -62,6 +62,8 @@ private slots:
void classMemberOnInstantiation();
void classNameConstant_data();
void classNameConstant();
+ void invalidVariadicFunction_data();
+ void invalidVariadicFunction();
};
}
diff --git a/parser/php.g b/parser/php.g
index 33f2af1..a15d1fd 100644
--- a/parser/php.g
+++ b/parser/php.g
@@ -266,7 +266,7 @@ namespace KDevelop
INC ("++"), DEC ("--"), BANG ("!"), QUESTION ("?"), COLON (":"),
BIT_AND ("&"), BIT_OR("|"), BIT_XOR ("^"),
SL ("<<"), SR (">>"), MUL("*"), DIV("/"), MOD ("%"),
- TILDE ("~"), DOLLAR ("$"), EXP ("**"),
+ TILDE ("~"), DOLLAR ("$"), EXP ("**"), ELLIPSIS ("..."),
LOGICAL_OR ("logical or"), LOGICAL_AND ("logical and"), LOGICAL_XOR ("logical xor") ;;
-- literals and identifiers:
@@ -880,7 +880,7 @@ arrayIndex=arrayIndexSpecifier | LBRACE expr=expr RBRACE
-> parameterList ;;
(parameterType=namespacedIdentifier | arrayType=ARRAY | callableType=CALLABLE | 0) (isRef=BIT_AND | 0)
- variable=variableIdentifier (ASSIGN defaultValue=staticScalar | 0)
+ (isVariadic=ELLIPSIS | 0) variable=variableIdentifier (ASSIGN defaultValue=staticScalar | 0)
-> parameter ;;
value=commonScalar
diff --git a/parser/phplexer.cpp b/parser/phplexer.cpp
index f1b92b4..33d434f 100644
--- a/parser/phplexer.cpp
+++ b/parser/phplexer.cpp
@@ -544,6 +544,9 @@ int Lexer::nextTokenKind()
if ((it + 1)->unicode() == '=') {
m_curpos++;
token = Parser::Token_CONCAT_ASSIGN;
+ } else if ((it + 1)->unicode() == '.' && (it + 2)->unicode() == '.') {
+ m_curpos = m_curpos + 2;
+ token = Parser::Token_ELLIPSIS;
} else {
token = Parser::Token_CONCAT;
}
diff --git a/parser/test/lexertest.cpp b/parser/test/lexertest.cpp
index c21dc13..7d38df8 100644
--- a/parser/test/lexertest.cpp
+++ b/parser/test/lexertest.cpp
@@ -510,6 +510,24 @@ void LexerTest::testExceptionFinally()
COMPARE_TOKEN(ts, 18, Parser::Token_RBRACE, 1, 26, 1, 26);
}
+void LexerTest::testEllipsis()
+{
+ QScopedPointer<TokenStream> ts(tokenize(QStringLiteral("<?php\nfunction foo(...$args) {}"), true));
+ QCOMPARE((int)ts->size(), 11);
+
+ COMPARE_TOKEN(ts, 0, Parser::Token_OPEN_TAG, 0, 0, 0, 5);
+ COMPARE_TOKEN(ts, 1, Parser::Token_FUNCTION, 1, 0, 1, 7);
+ COMPARE_TOKEN(ts, 2, Parser::Token_WHITESPACE, 1, 8, 1, 8);
+ COMPARE_TOKEN(ts, 3, Parser::Token_STRING, 1, 9, 1, 11);
+ COMPARE_TOKEN(ts, 4, Parser::Token_LPAREN, 1, 12, 1, 12);
+ COMPARE_TOKEN(ts, 5, Parser::Token_ELLIPSIS, 1, 13, 1, 15);
+ COMPARE_TOKEN(ts, 6, Parser::Token_VARIABLE, 1, 16, 1, 20);
+ COMPARE_TOKEN(ts, 7, Parser::Token_RPAREN, 1, 21, 1, 21);
+ COMPARE_TOKEN(ts, 8, Parser::Token_WHITESPACE, 1, 22, 1, 22);
+ COMPARE_TOKEN(ts, 9, Parser::Token_LBRACE, 1, 23, 1, 23);
+ COMPARE_TOKEN(ts, 10, Parser::Token_RBRACE, 1, 24, 1, 24);
+}
+
TokenStream* LexerTest::tokenize(const QString& unit, bool debug, int initialState)
{
TokenStream* tokenStream = new TokenStream;
diff --git a/parser/test/lexertest.h b/parser/test/lexertest.h
index 4489fc1..db73d22 100644
--- a/parser/test/lexertest.h
+++ b/parser/test/lexertest.h
@@ -68,6 +68,7 @@ private slots:
void testTypeHintsOnFunction();
void testExponentiation();
void testExceptionFinally();
+ void testEllipsis();
protected:
TokenStream* tokenize(const QString& unit, bool debug = false, int initialState = Lexer::HtmlState);