summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthijs Tijink <matthijstijink@gmail.com>2017-08-01 18:16:31 (GMT)
committerAlexander Zhigalin <alexander@zhigalin.tk>2017-08-02 09:12:10 (GMT)
commitbfc97e1e0f5feebfb6f2a7bb3dc59013a4b11de5 (patch)
tree8de29535f33163fad0e7ed29bd04b933c1b4e070
parent44a9b2dfd678297c4d6accc730abe0d6c282f680 (diff)
Add callable type to PHP
Summary: This diff adds a new primitive `callable` type to the PHP language support. This represents anything which can be called as a function in PHP (e.g. a function name as string, a closure, an array with two entries for static class methods, etc.). Additionally, for return types, when two return types are detected with one being a callable and one an actual function, the return type is set to just a callable. Reviewers: #kdevelop Subscribers: kdevelop-devel Differential Revision: https://phabricator.kde.org/D7034
-rw-r--r--duchain/builders/typebuilder.cpp7
-rw-r--r--duchain/helper.cpp2
-rw-r--r--duchain/tests/duchain.cpp32
-rw-r--r--duchain/tests/duchain.h3
-rw-r--r--duchain/types/integraltypeextended.cpp2
-rw-r--r--duchain/types/integraltypeextended.h3
6 files changed, 41 insertions, 8 deletions
diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp
index f2ffe9c..7310fa0 100644
--- a/duchain/builders/typebuilder.cpp
+++ b/duchain/builders/typebuilder.cpp
@@ -445,11 +445,18 @@ void TypeBuilder::visitStatement(StatementAst* node)
type = rType->baseType();
}
if (ft->returnType() && !ft->returnType()->equals(type.data())) {
+ bool existingTypeIsCallable = ft->returnType().cast<IntegralTypeExtended>() &&
+ ft->returnType().cast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable;
+ bool newTypeIsCallable = type.cast<IntegralTypeExtended>() &&
+ type.cast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable;
if (ft->returnType().cast<IntegralType>()
&& ft->returnType().cast<IntegralType>()->dataType() == IntegralType::TypeMixed)
{
//don't add TypeMixed to the list, just ignore
ft->setReturnType(type);
+ } else if ((existingTypeIsCallable && type.cast<FunctionType>()) || (newTypeIsCallable && ft->returnType().cast<FunctionType>())) {
+ //If one type is "callable" and the other a real function, the result is just a "callable".
+ ft->setReturnType(AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeCallable)));
} else {
UnsureType::Ptr retT;
if (ft->returnType().cast<UnsureType>()) {
diff --git a/duchain/helper.cpp b/duchain/helper.cpp
index 467fc43..e755240 100644
--- a/duchain/helper.cpp
+++ b/duchain/helper.cpp
@@ -447,6 +447,8 @@ AbstractType::Ptr determineTypehint(const T* parameterType, EditorIntegrator *ed
type = AbstractType::Ptr(new IntegralType(IntegralType::TypeInt));
} else if (parameterType->stringType != -1) {
type = AbstractType::Ptr(new IntegralType(IntegralType::TypeString));
+ } else if (parameterType->callableType != -1) {
+ type = AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeCallable));
} else if (parameterType->iterableType != -1) {
DeclarationPointer traversableDecl = findDeclarationImportHelper(currentContext, QualifiedIdentifier("traversable"), ClassDeclarationType);
diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp
index 9e66488..e5da66d 100644
--- a/duchain/tests/duchain.cpp
+++ b/duchain/tests/duchain.cpp
@@ -738,12 +738,32 @@ void TestDUChain::declareTypehintCallableFunction()
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::TypeMixed);
+ QVERIFY(IntegralTypeExtended::Ptr::dynamicCast(fun->arguments().first()));
+ QVERIFY(IntegralTypeExtended::Ptr::dynamicCast(fun->arguments().first())->dataType() == IntegralTypeExtended::TypeCallable);
- IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
+ IntegralTypeExtended::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralTypeExtended>();
QVERIFY(type);
- QVERIFY(type->dataType() == IntegralType::TypeMixed);
+ QVERIFY(type->dataType() == IntegralTypeExtended::TypeCallable);
+}
+
+void Php::TestDUChain::functionWithCallableAndFunctionReturn()
+{
+ QByteArray method("<? function foo(callable $i) { return $i; return function () {}; } ");
+
+ 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(IntegralTypeExtended::Ptr::dynamicCast(fun->arguments().first()));
+ QVERIFY(IntegralTypeExtended::Ptr::dynamicCast(fun->arguments().first())->dataType() == IntegralTypeExtended::TypeCallable);
+
+ IntegralTypeExtended::Ptr retType = IntegralTypeExtended::Ptr::dynamicCast(fun->returnType());
+ QVERIFY(retType);
+ QVERIFY(retType->dataType() == IntegralTypeExtended::TypeCallable);
}
void TestDUChain::declareTypehintIterableFunction()
@@ -914,11 +934,11 @@ void TestDUChain::declareTypehintWithPhpdocFunction()
QVERIFY(type->dataType() == IntegralType::TypeInt);
}
-void TestDUChain::declareNullableTypehintCallableFunction()
+void TestDUChain::declareNullableTypehintMixedFunction()
{
// 0 1 2 3
// 0123456789012345678901234567890123
- QByteArray method("<? function foo(?callable $i) { } ");
+ QByteArray method("<? function foo(?UnknownClass $i) { } ");
TopDUContext* top = parse(method, DumpAll);
DUChainReleaser releaseTop(top);
diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h
index f7d137f..a61e27a 100644
--- a/duchain/tests/duchain.h
+++ b/duchain/tests/duchain.h
@@ -41,13 +41,14 @@ private slots:
void declareTypehintVariadicFunction();
void declareTypehintArrayFunction();
void declareTypehintCallableFunction();
+ void functionWithCallableAndFunctionReturn();
void declareTypehintIterableFunction();
void declareTypehintBoolFunction();
void declareTypehintFloatFunction();
void declareTypehintIntFunction();
void declareTypehintStringFunction();
void declareNullableTypehintArrayFunction();
- void declareNullableTypehintCallableFunction();
+ void declareNullableTypehintMixedFunction();
void declareTypehintNullableIterableFunction();
void declareTypehintWithPhpdocFunction();
void returnTypeClass();
diff --git a/duchain/types/integraltypeextended.cpp b/duchain/types/integraltypeextended.cpp
index f4c4be2..0fb9003 100644
--- a/duchain/types/integraltypeextended.cpp
+++ b/duchain/types/integraltypeextended.cpp
@@ -51,6 +51,8 @@ QString IntegralTypeExtended::toString() const
{
if ( d_func()->m_dataType == TypeResource ) {
return QStringLiteral("resource");
+ } else if (d_func()->m_dataType == TypeCallable) {
+ return QStringLiteral("callable");
}
return KDevelop::IntegralType::toString();
}
diff --git a/duchain/types/integraltypeextended.h b/duchain/types/integraltypeextended.h
index 06b9725..71d4036 100644
--- a/duchain/types/integraltypeextended.h
+++ b/duchain/types/integraltypeextended.h
@@ -40,7 +40,8 @@ public:
typedef KDevelop::TypePtr<IntegralTypeExtended> Ptr;
enum PHPIntegralTypes {
- TypeResource = KDevelop::IntegralType::TypeLanguageSpecific
+ TypeResource = KDevelop::IntegralType::TypeLanguageSpecific,
+ TypeCallable
};
/// Default constructor