Warning, file /kdevelop/kdev-php/duchain/tests/duchain.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "duchain.h"
0008 
0009 #include <QtTest>
0010 
0011 #include <language/duchain/parsingenvironment.h>
0012 #include <language/duchain/problem.h>
0013 #include <language/duchain/duchain.h>
0014 #include <language/duchain/duchainlock.h>
0015 #include <language/duchain/topducontext.h>
0016 #include <language/duchain/types/functiontype.h>
0017 #include <language/duchain/types/integraltype.h>
0018 #include <language/duchain/types/unsuretype.h>
0019 #include <language/duchain/types/arraytype.h>
0020 #include <language/duchain/namespacealiasdeclaration.h>
0021 #include <language/editor/documentrange.h>
0022 
0023 #include <interfaces/icore.h>
0024 #include <interfaces/ilanguagecontroller.h>
0025 #include <interfaces/icompletionsettings.h>
0026 
0027 #include "helper.h"
0028 
0029 #include "../declarations/classdeclaration.h"
0030 #include "../declarations/classmethoddeclaration.h"
0031 #include "../declarations/functiondeclaration.h"
0032 #include "../declarations/variabledeclaration.h"
0033 
0034 #include "../types/structuretype.h"
0035 #include "../types/integraltypeextended.h"
0036 
0037 #include <QStandardPaths>
0038 
0039 using namespace KDevelop;
0040 using namespace Php;
0041 
0042 QTEST_MAIN(Php::TestDUChain)
0043 
0044 TestDUChain::TestDUChain()
0045 {
0046 }
0047 
0048 void TestDUChain::declareFunction()
0049 {
0050     //                 0         1         2         3         4         5         6         7
0051     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0052     QByteArray method("<? function foo() {}");
0053 
0054     TopDUContext* top = parse(method, DumpNone);
0055     DUChainReleaser releaseTop(top);
0056     DUChainWriteLocker lock(DUChain::lock());
0057 
0058     QCOMPARE(top->childContexts().count(), 2);
0059     QCOMPARE(top->localDeclarations().count(), 1);
0060 
0061     Declaration* dec = top->localDeclarations().at(0);
0062     QVERIFY(dec);
0063     QCOMPARE(dec->context(), top);
0064     QCOMPARE(dec->internalContext(), top->childContexts().at(1));
0065 
0066     // no return means void as return type
0067     auto ftype = dec->abstractType().dynamicCast<FunctionType>();
0068     QVERIFY(ftype);
0069     auto itype = ftype->returnType().dynamicCast<IntegralType>();
0070     QVERIFY(itype->dataType() == IntegralType::TypeVoid);
0071 
0072 
0073     QCOMPARE(top->childContexts().at(0)->type(), DUContext::Function);
0074     QCOMPARE(top->childContexts().at(1)->type(), DUContext::Other);
0075 }
0076 
0077 void TestDUChain::declareBaseTypeFunction()
0078 {
0079     //                 0         1         2         3         4         5         6         7
0080     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0081     QByteArray method("<? function string() {}");
0082 
0083     TopDUContext* top = parse(method, DumpNone);
0084     DUChainReleaser releaseTop(top);
0085     DUChainWriteLocker lock(DUChain::lock());
0086 
0087     QCOMPARE(top->childContexts().count(), 2);
0088     QCOMPARE(top->localDeclarations().count(), 1);
0089 
0090     Declaration* dec = top->localDeclarations().at(0);
0091     QVERIFY(dec);
0092     QCOMPARE(dec->context(), top);
0093     QCOMPARE(dec->internalContext(), top->childContexts().at(1));
0094 
0095     // no return means void as return type
0096     auto ftype = dec->abstractType().dynamicCast<FunctionType>();
0097     QVERIFY(ftype);
0098     auto itype = ftype->returnType().dynamicCast<IntegralType>();
0099     QVERIFY(itype->dataType() == IntegralType::TypeVoid);
0100 
0101 
0102     QCOMPARE(top->childContexts().at(0)->type(), DUContext::Function);
0103     QCOMPARE(top->childContexts().at(1)->type(), DUContext::Other);
0104 }
0105 
0106 void TestDUChain::declareSemiReservedFunction()
0107 {
0108     //                 0         1         2         3         4         5         6         7
0109     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0110     QByteArray method("<? function switch() {}");
0111 
0112     TopDUContext* top = parse(method, DumpNone);
0113     QVERIFY(!top);
0114 }
0115 
0116 void TestDUChain::declareVar()
0117 {
0118     //                 0         1         2         3         4         5         6         7
0119     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0120     QByteArray method("<? class A {} class B {} $i = new A(); $j = new B(); $i = new B(); $i = 'foo';"
0121                         " $a = substr($i, 0, 1);");
0122 
0123     TopDUContext* top = parse(method, DumpNone);
0124     DUChainReleaser releaseTop(top);
0125     DUChainWriteLocker lock(DUChain::lock());
0126 
0127     QVERIFY(!top->parentContext());
0128     QCOMPARE(top->childContexts().count(), 2);
0129     QCOMPARE(top->localDeclarations().count(), 5);
0130 
0131     //class A
0132     Declaration* dec = top->localDeclarations().at(0);
0133     QCOMPARE(dec->uses().count(), 1);
0134     QCOMPARE(dec->uses().begin()->count(), 1);
0135 
0136     //$i
0137     Declaration* decVar = top->localDeclarations().at(2);
0138     QCOMPARE(decVar->identifier(), Identifier(u"i"));
0139     qDebug() << decVar->abstractType()->toString();
0140     UnsureType::Ptr unsureType = decVar->type<UnsureType>();
0141     QVERIFY(unsureType);
0142     QCOMPARE(unsureType->typesSize(), 3u);
0143     // = new A();
0144     QCOMPARE(unsureType->types()[0].abstractType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0145     QVERIFY(unsureType->types()[0].abstractType()->equals(dec->abstractType().data()));
0146     // = new B();
0147     //class B
0148     dec = top->localDeclarations().at(1);
0149     QCOMPARE(dec->uses().count(), 1);
0150     QCOMPARE(dec->uses().begin()->count(), 2);
0151     QCOMPARE(unsureType->types()[1].abstractType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
0152     QVERIFY(unsureType->types()[1].abstractType()->equals(dec->abstractType().data()));
0153     // = 'foo';
0154     QVERIFY(unsureType->types()[2].abstractType().dynamicCast<IntegralType>());
0155     QVERIFY(unsureType->types()[2].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
0156 
0157     //$j
0158     decVar = top->localDeclarations().at(3);
0159     QCOMPARE(decVar->identifier(), Identifier(u"j"));
0160     StructureType::Ptr classType = decVar->type<StructureType>();
0161     QVERIFY(classType);
0162     QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
0163     QVERIFY(classType->equals(dec->abstractType().data()));
0164 
0165     // $a
0166     decVar = top->localDeclarations().at(4);
0167     QCOMPARE(decVar->identifier(), Identifier(u"a"));
0168     QVERIFY(decVar->type<IntegralType>());
0169 }
0170 
0171 void TestDUChain::varTypehint()
0172 {
0173     //                 0         1         2         3         4         5         6         7
0174     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0175     QByteArray method("<? class A {} /** @var A **/ $i = foo();");
0176 
0177     TopDUContext* top = parse(method, DumpAll);
0178     DUChainReleaser releaseTop(top);
0179     DUChainWriteLocker lock(DUChain::lock());
0180 
0181     //class A
0182     Declaration* dec = top->localDeclarations().at(0);
0183 
0184     //$i
0185     Declaration* decVar = top->localDeclarations().at(1);
0186     QCOMPARE(decVar->identifier(), Identifier(u"i"));
0187     StructureType::Ptr classType = decVar->type<StructureType>();
0188     QVERIFY(classType);
0189     QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0190     QVERIFY(classType->equals(dec->abstractType().data()));
0191 }
0192 
0193 void TestDUChain::declareClass()
0194 {
0195     //                 0         1         2         3         4         5         6         7
0196     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0197     QByteArray method("<? class A { public function foo($i) {} protected static function bar() {} private function baz() {} function boo() {} }");
0198 
0199     TopDUContext* top = parse(method, DumpNone);
0200     DUChainReleaser releaseTop(top);
0201     DUChainWriteLocker lock(DUChain::lock());
0202 
0203     QVERIFY(!top->parentContext());
0204     QCOMPARE(top->childContexts().count(), 1);
0205 
0206     DUContext* contextClassA = top->childContexts().first();
0207 
0208 
0209     QCOMPARE(top->localDeclarations().count(), 1);
0210     Declaration* dec = top->localDeclarations().first();
0211     QCOMPARE(dec->kind(), Declaration::Type);
0212     QCOMPARE(dec->toString(), QString("class A"));
0213     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0214     QCOMPARE(dec->isDefinition(), true);
0215     QCOMPARE(dec->logicalInternalContext(top), contextClassA);
0216 
0217     qDebug() << contextClassA->localScopeIdentifier().toString();
0218     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0219     QCOMPARE(contextClassA->childContexts().count(), 8);
0220     QCOMPARE(contextClassA->childContexts().first()->localScopeIdentifier(), QualifiedIdentifier(u"foo"));
0221 
0222     DUContext* contextMethodBodyFoo = contextClassA->childContexts().at(1);
0223     QCOMPARE(contextMethodBodyFoo->localScopeIdentifier(), QualifiedIdentifier(u"foo"));
0224     QCOMPARE(contextMethodBodyFoo->importedParentContexts().count(), 1);
0225     QCOMPARE(contextMethodBodyFoo->childContexts().count(), 0);
0226     QVERIFY(contextMethodBodyFoo->importedParentContexts().first().context(top) ==
0227             contextClassA->childContexts().first());
0228 
0229     //foo()
0230     dec = contextClassA->localDeclarations().at(0);
0231     ClassFunctionDeclaration* funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0232     QVERIFY(funDec);
0233     QCOMPARE(funDec->kind(), Declaration::Type);
0234     QCOMPARE(funDec->identifier(), Identifier(u"foo"));
0235     QCOMPARE(funDec->accessPolicy(), Declaration::Public);
0236     QCOMPARE(funDec->isStatic(), false);
0237 
0238     {
0239         // no return means void as return type
0240         auto ftype = dec->abstractType().dynamicCast<FunctionType>();
0241         QVERIFY(ftype);
0242         auto itype = ftype->returnType().dynamicCast<IntegralType>();
0243         QVERIFY(itype->dataType() == IntegralType::TypeVoid);
0244     }
0245 
0246     //bar()
0247     dec = contextClassA->localDeclarations().at(1);
0248     funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0249     QVERIFY(funDec);
0250     QCOMPARE(funDec->identifier(), Identifier(u"bar"));
0251     QCOMPARE(funDec->accessPolicy(), Declaration::Protected);
0252     QCOMPARE(funDec->isStatic(), true);
0253 
0254     //baz()
0255     dec = contextClassA->localDeclarations().at(2);
0256     funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0257     QVERIFY(funDec);
0258     QCOMPARE(funDec->identifier(), Identifier(u"baz"));
0259     QCOMPARE(funDec->accessPolicy(), Declaration::Private);
0260     QCOMPARE(funDec->isStatic(), false);
0261 
0262     //boo()
0263     dec = contextClassA->localDeclarations().at(3);
0264     funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0265     QVERIFY(funDec);
0266     QCOMPARE(funDec->identifier(), Identifier(u"boo"));
0267     QCOMPARE(funDec->accessPolicy(), Declaration::Public);
0268 }
0269 
0270 void TestDUChain::declareBaseTypeClass()
0271 {
0272     //                 0         1         2         3         4         5         6         7
0273     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0274     QByteArray method("<? class String {}");
0275 
0276     TopDUContext* top = parse(method, DumpNone);
0277     QVERIFY(top);
0278     DUChainReleaser releaseTop(top);
0279     DUChainWriteLocker lock(DUChain::lock());
0280 
0281     QCOMPARE(top->problems().count(), 1);
0282 }
0283 
0284 void TestDUChain::declareClassWithSemiReservedMethod()
0285 {
0286     //                 0         1         2         3         4         5         6         7
0287     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0288     QByteArray method("<? class A { public function switch($i) {} protected static function public() {} }");
0289 
0290     TopDUContext* top = parse(method, DumpNone);
0291     DUChainReleaser releaseTop(top);
0292     DUChainWriteLocker lock(DUChain::lock());
0293 
0294     QVERIFY(!top->parentContext());
0295     QCOMPARE(top->childContexts().count(), 1);
0296 
0297     DUContext* contextClassA = top->childContexts().first();
0298 
0299     QCOMPARE(top->localDeclarations().count(), 1);
0300     Declaration* dec = top->localDeclarations().first();
0301     QCOMPARE(dec->kind(), Declaration::Type);
0302     QCOMPARE(dec->toString(), QString("class A"));
0303     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0304     QCOMPARE(dec->isDefinition(), true);
0305     QCOMPARE(dec->logicalInternalContext(top), contextClassA);
0306 
0307     qDebug() << contextClassA->localScopeIdentifier().toString();
0308     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0309     QCOMPARE(contextClassA->childContexts().count(), 4);
0310     QCOMPARE(contextClassA->childContexts().first()->localScopeIdentifier(), QualifiedIdentifier(u"switch"));
0311 
0312     DUContext* contextMethodBodyFoo = contextClassA->childContexts().at(1);
0313     QCOMPARE(contextMethodBodyFoo->localScopeIdentifier(), QualifiedIdentifier(u"switch"));
0314     QCOMPARE(contextMethodBodyFoo->importedParentContexts().count(), 1);
0315     QCOMPARE(contextMethodBodyFoo->childContexts().count(), 0);
0316     QVERIFY(contextMethodBodyFoo->importedParentContexts().first().context(top) ==
0317             contextClassA->childContexts().first());
0318 
0319     //switch()
0320     dec = contextClassA->localDeclarations().at(0);
0321     ClassFunctionDeclaration* funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0322     QVERIFY(funDec);
0323     QCOMPARE(funDec->kind(), Declaration::Type);
0324     QCOMPARE(funDec->identifier(), Identifier(u"switch"));
0325     QCOMPARE(funDec->accessPolicy(), Declaration::Public);
0326     QCOMPARE(funDec->isStatic(), false);
0327 
0328     {
0329         // no return means void as return type
0330         auto ftype = dec->abstractType().dynamicCast<FunctionType>();
0331         QVERIFY(ftype);
0332         auto itype = ftype->returnType().dynamicCast<IntegralType>();
0333         QVERIFY(itype->dataType() == IntegralType::TypeVoid);
0334     }
0335 
0336     //public()
0337     dec = contextClassA->localDeclarations().at(1);
0338     funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0339     QVERIFY(funDec);
0340     QCOMPARE(funDec->identifier(), Identifier(u"public"));
0341     QCOMPARE(funDec->accessPolicy(), Declaration::Protected);
0342     QCOMPARE(funDec->isStatic(), true);
0343 }
0344 
0345 void TestDUChain::declareClassWithBaseTypeMethod()
0346 {
0347     //                 0         1         2         3         4         5         6         7
0348     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0349     QByteArray method("<? class A { public function string($i) {} protected static function iterable() {} }");
0350 
0351     TopDUContext* top = parse(method, DumpNone);
0352     DUChainReleaser releaseTop(top);
0353     DUChainWriteLocker lock(DUChain::lock());
0354 
0355     QVERIFY(!top->parentContext());
0356     QCOMPARE(top->childContexts().count(), 1);
0357 
0358     DUContext* contextClassA = top->childContexts().first();
0359 
0360     QCOMPARE(top->localDeclarations().count(), 1);
0361     Declaration* dec = top->localDeclarations().first();
0362     QCOMPARE(dec->kind(), Declaration::Type);
0363     QCOMPARE(dec->toString(), QString("class A"));
0364     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0365     QCOMPARE(dec->isDefinition(), true);
0366     QCOMPARE(dec->logicalInternalContext(top), contextClassA);
0367 
0368     qDebug() << contextClassA->localScopeIdentifier().toString();
0369     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0370     QCOMPARE(contextClassA->childContexts().count(), 4);
0371     QCOMPARE(contextClassA->childContexts().first()->localScopeIdentifier(), QualifiedIdentifier(u"string"));
0372 
0373     DUContext* contextMethodBodyFoo = contextClassA->childContexts().at(1);
0374     QCOMPARE(contextMethodBodyFoo->localScopeIdentifier(), QualifiedIdentifier(u"string"));
0375     QCOMPARE(contextMethodBodyFoo->importedParentContexts().count(), 1);
0376     QCOMPARE(contextMethodBodyFoo->childContexts().count(), 0);
0377     QVERIFY(contextMethodBodyFoo->importedParentContexts().first().context(top) ==
0378             contextClassA->childContexts().first());
0379 
0380     //string()
0381     dec = contextClassA->localDeclarations().at(0);
0382     ClassFunctionDeclaration* funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0383     QVERIFY(funDec);
0384     QCOMPARE(funDec->kind(), Declaration::Type);
0385     QCOMPARE(funDec->identifier(), Identifier(u"string"));
0386     QCOMPARE(funDec->accessPolicy(), Declaration::Public);
0387     QCOMPARE(funDec->isStatic(), false);
0388 
0389     {
0390         // no return means void as return type
0391         auto ftype = dec->abstractType().dynamicCast<FunctionType>();
0392         QVERIFY(ftype);
0393         auto itype = ftype->returnType().dynamicCast<IntegralType>();
0394         QVERIFY(itype->dataType() == IntegralType::TypeVoid);
0395     }
0396 
0397     //iterable()
0398     dec = contextClassA->localDeclarations().at(1);
0399     funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
0400     QVERIFY(funDec);
0401     QCOMPARE(funDec->identifier(), Identifier(u"iterable"));
0402     QCOMPARE(funDec->accessPolicy(), Declaration::Protected);
0403     QCOMPARE(funDec->isStatic(), true);
0404 }
0405 
0406 void TestDUChain::classMemberVar()
0407 {
0408     //                 0         1         2         3         4         5         6         7
0409     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0410     QByteArray method("<? class A { public $foo; /** @var A **/ protected $bar; private static $baz = ''; var $boo = 1; }");
0411 
0412     TopDUContext* top = parse(method, DumpNone);
0413     DUChainReleaser releaseTop(top);
0414     DUChainWriteLocker lock(DUChain::lock());
0415 
0416     QVERIFY(!top->parentContext());
0417     QCOMPARE(top->childContexts().count(), 1);
0418 
0419     DUContext* contextClassA = top->childContexts().first();
0420 
0421     QCOMPARE(top->localDeclarations().count(), 1);
0422     Declaration* dec = top->localDeclarations().first();
0423     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0424     QCOMPARE(dec->isDefinition(), true);
0425     QCOMPARE(dec->logicalInternalContext(top), contextClassA);
0426 
0427     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0428     QCOMPARE(contextClassA->childContexts().count(), 0);
0429     QCOMPARE(contextClassA->localDeclarations().count(), 4);
0430 
0431     //$foo
0432     ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().first());
0433     QVERIFY(var);
0434     QCOMPARE(var->identifier(), Identifier(u"foo"));
0435     QCOMPARE(var->accessPolicy(), Declaration::Public);
0436     QCOMPARE(var->isStatic(), false);
0437     QVERIFY(var->type<IntegralType>());
0438     QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeNull);
0439 
0440     //$bar
0441     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(1));
0442     QVERIFY(var);
0443     QCOMPARE(var->identifier(), Identifier(u"bar"));
0444     QCOMPARE(var->accessPolicy(), Declaration::Protected);
0445     QCOMPARE(var->isStatic(), false);
0446     StructureType::Ptr type = var->type<StructureType>();
0447     QVERIFY(type);
0448     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0449 
0450     //$baz
0451     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(2));
0452     QVERIFY(var);
0453     QCOMPARE(var->identifier(), Identifier(u"baz"));
0454     QCOMPARE(var->accessPolicy(), Declaration::Private);
0455     QCOMPARE(var->isStatic(), true);
0456     QVERIFY(var->type<IntegralType>());
0457     QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeString);
0458 
0459     //$boo
0460     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(3));
0461     QVERIFY(var);
0462     QCOMPARE(var->identifier(), Identifier(u"boo"));
0463     QCOMPARE(var->accessPolicy(), Declaration::Public);
0464     QCOMPARE(var->isStatic(), false);
0465     QVERIFY(var->type<IntegralType>());
0466     QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeInt);
0467 }
0468 
0469 void TestDUChain::classMemberVarTypehint()
0470 {
0471     //Note: in practice, Traversable is defined by php, but this interface is not loaded in this test, so define it ourselves
0472     //                 0         1         2         3         4         5         6         7
0473     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0474     QByteArray method("<? interface Traversable { } class A { public iterable $foo; protected A $bar; private static ?string $baz; public int $boo = 1; }");
0475 
0476     TopDUContext* top = parse(method, DumpNone);
0477     DUChainReleaser releaseTop(top);
0478     DUChainWriteLocker lock(DUChain::lock());
0479 
0480     QVERIFY(!top->parentContext());
0481     QCOMPARE(top->childContexts().count(), 2);
0482 
0483     DUContext* contextClassA = top->childContexts().at(1);
0484 
0485     QCOMPARE(top->localDeclarations().count(), 2);
0486     Declaration* dec = top->localDeclarations().at(1);
0487     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0488     QCOMPARE(dec->isDefinition(), true);
0489     QCOMPARE(dec->logicalInternalContext(top), contextClassA);
0490 
0491     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0492     QCOMPARE(contextClassA->childContexts().count(), 0);
0493     QCOMPARE(contextClassA->localDeclarations().count(), 4);
0494 
0495     TypePtr<UnsureType> ut;
0496 
0497     //$foo
0498     ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().first());
0499     QVERIFY(var);
0500     QCOMPARE(var->identifier(), Identifier(u"foo"));
0501     QCOMPARE(var->accessPolicy(), Declaration::Public);
0502     QCOMPARE(var->isStatic(), false);
0503     QVERIFY(var->type<UnsureType>());
0504 
0505     ut = var->type<UnsureType>();
0506     QVERIFY(ut);
0507     QCOMPARE(2u, ut->typesSize());
0508 
0509     QVERIFY(ut->types()[0].type<IntegralType>());
0510     QVERIFY(ut->types()[0].type<IntegralType>()->dataType() == IntegralType::TypeArray);
0511 
0512     QVERIFY(ut->types()[1].type<StructureType>());
0513     QVERIFY(ut->types()[1].type<StructureType>()->declaration(top));
0514     QCOMPARE(ut->types()[1].type<StructureType>()->declaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"traversable"));
0515 
0516     //$bar
0517     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(1));
0518     QVERIFY(var);
0519     QCOMPARE(var->identifier(), Identifier(u"bar"));
0520     QCOMPARE(var->accessPolicy(), Declaration::Protected);
0521     QCOMPARE(var->isStatic(), false);
0522     StructureType::Ptr type = var->type<StructureType>();
0523     QVERIFY(type);
0524     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0525 
0526     //$baz
0527     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(2));
0528     QVERIFY(var);
0529     QCOMPARE(var->identifier(), Identifier(u"baz"));
0530     QCOMPARE(var->accessPolicy(), Declaration::Private);
0531     QCOMPARE(var->isStatic(), true);
0532     QVERIFY(var->type<UnsureType>());
0533     ut = var->type<UnsureType>();
0534     QVERIFY(ut);
0535     QCOMPARE(2u, ut->typesSize());
0536 
0537     QVERIFY(ut->types()[0].type<IntegralType>());
0538     QVERIFY(ut->types()[0].type<IntegralType>()->dataType() == IntegralType::TypeString);
0539 
0540     QVERIFY(ut->types()[1].type<IntegralType>());
0541     QVERIFY(ut->types()[1].type<IntegralType>()->dataType() == IntegralType::TypeNull);
0542 
0543     //$boo
0544     var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(3));
0545     QVERIFY(var);
0546     QCOMPARE(var->identifier(), Identifier(u"boo"));
0547     QCOMPARE(var->accessPolicy(), Declaration::Public);
0548     QCOMPARE(var->isStatic(), false);
0549     QVERIFY(var->type<IntegralType>());
0550     QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeInt);
0551 }
0552 
0553 void TestDUChain::classMemberVarAfterUse()
0554 {
0555     //                 0         1         2         3         4         5         6         7
0556     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0557     QByteArray method("<?php class B { function x() { $this->a = 1; } public $a = 1; }");
0558 
0559     TopDUContext* top = parse(method, DumpNone);
0560     DUChainReleaser releaseTop(top);
0561     DUChainWriteLocker lock(DUChain::lock());
0562 
0563     QVERIFY(!top->parentContext());
0564     QCOMPARE(top->childContexts().count(), 1);
0565     QVERIFY(top->problems().isEmpty());
0566 
0567     DUContext* contextClassB = top->childContexts().first();
0568 
0569     QCOMPARE(top->localDeclarations().count(), 1);
0570     Declaration* dec = top->localDeclarations().first();
0571     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
0572     QCOMPARE(dec->isDefinition(), true);
0573     QCOMPARE(dec->logicalInternalContext(top), contextClassB);
0574 
0575     QCOMPARE(contextClassB->localScopeIdentifier(), QualifiedIdentifier(u"b"));
0576     QCOMPARE(contextClassB->childContexts().count(), 2);
0577     QCOMPARE(contextClassB->localDeclarations().count(), 2);
0578 
0579     //$foo
0580     ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassB->localDeclarations().at(1));
0581     QVERIFY(var);
0582     QCOMPARE(var->identifier(), Identifier(u"a"));
0583     QCOMPARE(var->accessPolicy(), Declaration::Public);
0584     QCOMPARE(var->isStatic(), false);
0585     QVERIFY(var->type<IntegralType>());
0586     QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeInt);
0587     QVERIFY(var->range() == RangeInRevision(0, 54, 0, 56));
0588 }
0589 
0590 void TestDUChain::classMemberVarDocBlockType()
0591 {
0592     //                 0         1         2         3         4         5         6         7
0593     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0594     QByteArray method("<? namespace Test { class A {} }\n"
0595                       "namespace Test2 { class B { /** @var \\Test\\A **/ public $foo; } }\n"
0596     );
0597 
0598     TopDUContext* top = parse(method, DumpNone);
0599     DUChainReleaser releaseTop(top);
0600     DUChainWriteLocker lock(DUChain::lock());
0601 
0602     QVERIFY(!top->parentContext());
0603     QCOMPARE(top->childContexts().count(), 2);
0604 
0605     DUContext* contextClassA = top->childContexts().at(0)->childContexts().first();
0606     DUContext* contextClassB = top->childContexts().at(1)->childContexts().first();
0607 
0608     QCOMPARE(top->localDeclarations().count(), 2);
0609     Declaration* dec = top->childContexts().first()->localDeclarations().first();
0610     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"test::a"));
0611     QCOMPARE(dec->isDefinition(), true);
0612 
0613     QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier(u"a"));
0614     QCOMPARE(contextClassA->childContexts().count(), 0);
0615     QCOMPARE(contextClassA->localDeclarations().count(), 0);
0616 
0617     QCOMPARE(contextClassB->localScopeIdentifier(), QualifiedIdentifier(u"b"));
0618     QCOMPARE(contextClassB->childContexts().count(), 0);
0619     QCOMPARE(contextClassB->localDeclarations().count(), 1);
0620 
0621     //$foo
0622     ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassB->localDeclarations().first());
0623     QVERIFY(var);
0624     QCOMPARE(var->identifier(), Identifier(u"foo"));
0625     QCOMPARE(var->accessPolicy(), Declaration::Public);
0626     QCOMPARE(var->isStatic(), false);
0627     StructureType::Ptr type = var->type<StructureType>();
0628     QVERIFY(type);
0629     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"test::a"));
0630 }
0631 
0632 void TestDUChain::returnTypeGenerator_data()
0633 {
0634     QTest::addColumn<QString>("code");
0635 
0636     //Note: in practice, Generator is defined by php, but this class is not loaded in this test, so define it ourselves
0637     QTest::newRow("simple yield expression") << QStringLiteral("<? class Generator {} function foo() { yield 1; }\n");
0638     QTest::newRow("yield assignment expression") << QStringLiteral("<? class Generator {} function foo() { yield $a = 1; }\n");
0639     QTest::newRow("yield null-coalesce expression") << QStringLiteral("<? class Generator {} function foo() { yield ?? 1; }\n");
0640     QTest::newRow("yield boolean expression") << QStringLiteral("<? class Generator {} function foo() { yield || 1; }\n");
0641     QTest::newRow("yield additive expression") << QStringLiteral("<? class Generator {} function foo() { yield + 1; }\n");
0642     QTest::newRow("yield multiplicative expression") << QStringLiteral("<? class Generator {} function foo() { yield * 2; }\n");
0643     QTest::newRow("yield relational expression") << QStringLiteral("<? class Generator {} function foo() { yield > 1; }\n");
0644     QTest::newRow("yield equality expression") << QStringLiteral("<? class Generator {} function foo() { yield == 1; }\n");
0645     QTest::newRow("yield shift expression") << QStringLiteral("<? class Generator {} function foo() { yield >> 1; }\n");
0646     QTest::newRow("yield bit expression") << QStringLiteral("<? class Generator {} function foo() { yield | 1; }\n");
0647     QTest::newRow("multiple yield expression") << QStringLiteral("<? class Generator {} function foo() { yield 1 || yield; }\n");
0648     QTest::newRow("yield key/value expression") << QStringLiteral("<? class Generator {} function foo() { yield 'key' => 'value'; }\n");
0649 }
0650 
0651 void TestDUChain::returnTypeGenerator()
0652 {
0653     QFETCH(QString, code);
0654 
0655     TopDUContext* top = parse(code.toUtf8(), DumpNone);
0656     DUChainReleaser releaseTop(top);
0657     DUChainWriteLocker lock(DUChain::lock());
0658 
0659     QVERIFY(!top->parentContext());
0660     QCOMPARE(top->childContexts().count(), 3);
0661     QCOMPARE(top->localDeclarations().count(), 2);
0662 
0663     Declaration* dec = top->localDeclarations().at(1);
0664     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
0665     FunctionType::Ptr functionType = dec->type<FunctionType>();
0666     QVERIFY(functionType);
0667     auto retType = functionType->returnType().dynamicCast<StructureType>();
0668     QVERIFY(retType);
0669     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"generator"));
0670 }
0671 
0672 void TestDUChain::returnTypeGeneratorDelegation()
0673 {
0674     //Note: in practice, Generator is defined by php, but this class is not loaded in this test, so define it ourselves
0675     //                 0         1         2         3         4         5         6         7
0676     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0677     QByteArray method("<? class Generator {} function foo() { yield 1; } function bar() { yield from foo(); }\n");
0678 
0679     TopDUContext* top = parse(method, DumpNone);
0680     DUChainReleaser releaseTop(top);
0681     DUChainWriteLocker lock(DUChain::lock());
0682 
0683     QVERIFY(!top->parentContext());
0684     QCOMPARE(top->childContexts().count(), 5);
0685     QCOMPARE(top->localDeclarations().count(), 3);
0686 
0687     Declaration* dec = top->localDeclarations().at(1);
0688     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
0689     FunctionType::Ptr functionType = dec->type<FunctionType>();
0690     QVERIFY(functionType);
0691     auto retType = functionType->returnType().dynamicCast<StructureType>();
0692     QVERIFY(retType);
0693     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"generator"));
0694 
0695     dec = top->localDeclarations().at(2);
0696     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"bar"));
0697     functionType = dec->type<FunctionType>();
0698     QVERIFY(functionType);
0699     retType = functionType->returnType().dynamicCast<StructureType>();
0700     QVERIFY(retType);
0701     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"generator"));
0702 }
0703 
0704 void TestDUChain::returnTypeClass()
0705 {
0706     //                 0         1         2         3         4         5         6         7
0707     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0708     QByteArray method("<? class A {} function foo() { return new A(); } function bar() { $i = new A(); return $i; }");
0709 
0710     TopDUContext* top = parse(method, DumpNone);
0711     DUChainReleaser releaseTop(top);
0712     DUChainWriteLocker lock(DUChain::lock());
0713 
0714     QVERIFY(!top->parentContext());
0715     QCOMPARE(top->childContexts().count(), 5);
0716     QCOMPARE(top->localDeclarations().count(), 3);
0717 
0718     Declaration* dec = top->localDeclarations().at(1);
0719     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
0720     FunctionType::Ptr functionType = dec->type<FunctionType>();
0721     QVERIFY(functionType);
0722     auto retType = functionType->returnType().dynamicCast<StructureType>();
0723     QVERIFY(retType);
0724     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0725 
0726     dec = top->localDeclarations().at(2);
0727     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"bar"));
0728     functionType = dec->type<FunctionType>();
0729     QVERIFY(functionType);
0730     retType = functionType->returnType().dynamicCast<StructureType>();
0731     QVERIFY(retType);
0732     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0733 }
0734 
0735 void TestDUChain::declarationReturnType()
0736 {
0737     //                 0         1         2         3         4         5         6         7
0738     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0739     QByteArray method("<? class A {} function foo() { return new A(); } $i = foo(); ");
0740 
0741     TopDUContext* top = parse(method, DumpNone);
0742     DUChainReleaser releaseTop(top);
0743     DUChainWriteLocker lock(DUChain::lock());
0744 
0745     QVERIFY(!top->parentContext());
0746     QCOMPARE(top->childContexts().count(), 3);
0747     QCOMPARE(top->localDeclarations().count(), 3);
0748 
0749     Declaration* dec = top->localDeclarations().at(1);
0750     FunctionType::Ptr fType = dec->type<FunctionType>();
0751     QVERIFY(fType);
0752     QVERIFY(fType->returnType().dynamicCast<StructureType>());
0753     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0754 
0755     dec = top->localDeclarations().at(2);
0756     QCOMPARE(dec->identifier(), Identifier(u"i"));
0757     StructureType::Ptr type = dec->type<StructureType>();
0758     QVERIFY(type);
0759     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0760 }
0761 
0762 void TestDUChain::declarationReturnTypeInRecursingFunction()
0763 {
0764     //                 0         1         2         3         4         5         6         7
0765     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0766     QByteArray method("<? class A {} /** @return A **/ function foo() { $i = foo(); } ");
0767 
0768     TopDUContext* top = parse(method, DumpNone);
0769     DUChainReleaser releaseTop(top);
0770     DUChainWriteLocker lock(DUChain::lock());
0771 
0772     QList< Declaration* > decs = top->childContexts().last()->findDeclarations(Identifier(QStringLiteral("i")));
0773     QCOMPARE(decs.size(), 1);
0774     Declaration* dec = decs.first();
0775     StructureType::Ptr type = dec->type<StructureType>();
0776     QVERIFY(type);
0777     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0778 }
0779 
0780 void TestDUChain::declarationMultipleReturnTypes()
0781 {
0782     //                 0         1         2         3         4         5         6         7
0783     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0784     QByteArray method("<? class A {} function foo() { return null; return new A(); return null; }\n"
0785                       "function bar() { return 1; return 2; }");
0786 
0787     TopDUContext* top = parse(method, DumpNone);
0788     DUChainReleaser releaseTop(top);
0789     DUChainWriteLocker lock(DUChain::lock());
0790 
0791     FunctionType::Ptr fType = top->localDeclarations().at(1)->type<FunctionType>();
0792     QVERIFY(fType);
0793     qDebug() << fType->toString();
0794     auto ut = fType->returnType().dynamicCast<UnsureType>();
0795     QVERIFY(ut);
0796     QCOMPARE(2u, ut->typesSize());
0797 
0798     ///TODO: why are the types not in the correct order, i.e. null, A
0799     QVERIFY(ut->types()[0].type<StructureType>());
0800     QVERIFY(ut->types()[0].type<StructureType>()->declaration(top));
0801     QCOMPARE(ut->types()[0].type<StructureType>()->declaration(top)->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0802 
0803     QVERIFY(ut->types()[1].type<IntegralType>());
0804     QVERIFY(ut->types()[1].type<IntegralType>()->dataType() == IntegralType::TypeNull);
0805 
0806     fType = top->localDeclarations().at(2)->type<FunctionType>();
0807     QVERIFY(fType);
0808     qDebug() << fType->toString();
0809     QVERIFY(fType->returnType().dynamicCast<IntegralType>());
0810     QVERIFY(fType->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
0811 }
0812 
0813 void TestDUChain::returnTypeViaMember()
0814 {
0815     //                 0         1         2         3         4         5         6         7
0816     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0817     QByteArray method("<? class a { /** @return a **/ function fa() {} }\n"
0818                       "class b { /** @var a **/ static $astatic; /** @var a **/ var $anormal;\n"
0819                       "  function fb1($param) { $i = self::$astatic->fa($param); }\n"
0820                       "  function fb2($param) { $i = $this->anormal->fa($param); } }");
0821 
0822     TopDUContext* top = parse(method, DumpNone);
0823     DUChainReleaser releaseTop(top);
0824     DUChainWriteLocker lock(DUChain::lock());
0825 
0826     QVector<Declaration*> decs = top->localDeclarations();
0827     QCOMPARE(decs.size(), 2);
0828 
0829     ClassDeclaration* aDec = dynamic_cast<ClassDeclaration*>(decs.first());
0830     QVERIFY(aDec);
0831 
0832     ClassDeclaration* bDec = dynamic_cast<ClassDeclaration*>(decs.last());
0833     QVERIFY(bDec);
0834     QCOMPARE(bDec->logicalInternalContext(top)->localDeclarations().size(), 4);
0835 
0836     typedef QPair<QString, QString> idPair;
0837     foreach ( const idPair & pair, QList< idPair >()
0838                                         << qMakePair(QString("fb1"), QString("astatic"))
0839                                         << qMakePair(QString("fb2"), QString("anormal")) )
0840     {
0841         qDebug() << pair.first << pair.second;
0842         ClassMethodDeclaration* fDec = dynamic_cast<ClassMethodDeclaration*>(
0843             bDec->logicalInternalContext(top)->findDeclarations(Identifier(pair.first)).first()
0844         );
0845         QVERIFY(fDec);
0846 
0847         ClassMemberDeclaration* mDec = dynamic_cast<ClassMemberDeclaration*>(
0848             bDec->logicalInternalContext(top)->findDeclarations(Identifier(pair.second)).first()
0849         );
0850         QVERIFY(mDec);
0851         QVERIFY(mDec->type<StructureType>());
0852         QCOMPARE(mDec->type<StructureType>()->declaration(top), aDec);
0853 
0854         QCOMPARE(fDec->logicalInternalContext(top)->localDeclarations().size(), 1);
0855         Declaration* iDec = fDec->logicalInternalContext(top)->localDeclarations().first();
0856         QCOMPARE(iDec->identifier().toString(), QString("i"));
0857         QVERIFY(iDec->type<StructureType>());
0858         QCOMPARE(iDec->type<StructureType>()->declaration(top), aDec);
0859     }
0860 }
0861 
0862 void TestDUChain::declarationReturnTypeDocBlock()
0863 {
0864     //                 0         1         2         3         4         5         6         7
0865     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
0866     QByteArray method("<? class A { /** @return A **/ function bar() {} } "
0867                       "   class B {} "
0868                       "/** @return A **/ function foo() { return new B(); } "
0869                       "/** @return object **/ function bar() {}");
0870 
0871     TopDUContext* top = parse(method, DumpNone);
0872     DUChainReleaser releaseTop(top);
0873     DUChainWriteLocker lock(DUChain::lock());
0874 
0875     //function bar
0876     Declaration* dec = top->childContexts().at(0)->localDeclarations().at(0);
0877     FunctionType::Ptr fType = dec->type<FunctionType>();
0878     QVERIFY(fType);
0879     QVERIFY(fType->returnType().dynamicCast<StructureType>());
0880     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0881 
0882     //function foo
0883     dec = top->localDeclarations().at(2);
0884     fType = dec->type<FunctionType>();
0885     QVERIFY(fType);
0886     QVERIFY(fType->returnType().dynamicCast<StructureType>());
0887     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
0888 
0889     //function bar
0890     dec = top->localDeclarations().at(3);
0891     fType = dec->type<FunctionType>();
0892     QVERIFY(fType);
0893     QVERIFY(fType->returnType().dynamicCast<IntegralTypeExtended>());
0894     QVERIFY(fType->returnType().staticCast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeObject);
0895 
0896     //test hint in internal functions file of a type that is added later on
0897     // function
0898     QList<Declaration*> decs = top->findDeclarations(Identifier(QStringLiteral("should_return_exception")));
0899     QCOMPARE(decs.size(), 1);
0900     dec = decs.first();
0901     fType = dec->type<FunctionType>();
0902     QVERIFY(fType);
0903     QVERIFY(fType->returnType().dynamicCast<StructureType>());
0904     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"exception"));
0905     // method
0906     decs = top->findDeclarations(Identifier(QStringLiteral("internal_test_class")));
0907     QCOMPARE(decs.size(), 1);
0908     ClassDeclaration* cdec = dynamic_cast<ClassDeclaration*>(decs.first());
0909     QVERIFY(cdec);
0910     decs = cdec->logicalInternalContext(top)->findDeclarations(Identifier(QStringLiteral("should_return_exception")));
0911     QCOMPARE(decs.size(), 1);
0912     dec = decs.first();
0913     fType = dec->type<FunctionType>();
0914     QVERIFY(fType);
0915     QVERIFY(fType->returnType().dynamicCast<StructureType>());
0916     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"exception"));
0917 }
0918 
0919 void TestDUChain::declarationReturnTypeDocBlockIntegral()
0920 {
0921     QByteArray method("<? /** @return string **/ function foo() {} /** @return mixed **/ function bar() {} class A { /** @return int **/ function aaa() {} }");
0922 
0923     TopDUContext* top = parse(method, DumpNone);
0924     DUChainReleaser releaseTop(top);
0925     DUChainWriteLocker lock(DUChain::lock());
0926 
0927     //function foo
0928     FunctionType::Ptr fType = top->localDeclarations().at(0)->type<FunctionType>();
0929     QVERIFY(fType);
0930     QVERIFY(fType->returnType().dynamicCast<IntegralType>());
0931     QVERIFY(fType->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
0932 
0933     //function bar
0934     fType = top->localDeclarations().at(1)->type<FunctionType>();
0935     QVERIFY(fType);
0936     QVERIFY(fType->returnType().dynamicCast<IntegralType>());
0937     QVERIFY(fType->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
0938 
0939     //function aaa
0940     fType = top->childContexts().at(4)->localDeclarations().first()->type<FunctionType>();
0941     QVERIFY(fType);
0942     QVERIFY(fType->returnType().dynamicCast<IntegralType>());
0943     QVERIFY(fType->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
0944 }
0945 
0946 void TestDUChain::declarationReturnTypeClassChain()
0947 {
0948     QByteArray method("<? class A { /** @return this **/ function a() {} /** @return self **/ function b() {} }");
0949 
0950     TopDUContext* top = parse(method, DumpNone);
0951     DUChainReleaser releaseTop(top);
0952     DUChainWriteLocker lock(DUChain::lock());
0953 
0954     // class a
0955     DUContext* ctx = top->childContexts().first();
0956     QCOMPARE(ctx->type(), DUContext::Class);
0957     QVERIFY(ctx->owner());
0958     QVERIFY(ctx->owner()->abstractType().dynamicCast<StructureType>());
0959 
0960     //function a
0961     // FIXME
0962     QEXPECT_FAIL("", "This test fails after porting the plugin to KF5.", Abort);
0963     QVERIFY(/* func a (this) */ ctx->localDeclarations().at(0)->type<FunctionType>().data() == ctx->owner()->abstractType().data());
0964     QVERIFY(/* func b (self) */ ctx->localDeclarations().at(1)->type<FunctionType>().data() == ctx->owner()->abstractType().data());
0965 }
0966 
0967 void TestDUChain::declarationReturnTypeTypehint()
0968 {
0969     //Typehint preferred over phpdoc preferred over inferred type
0970     QByteArray method("<? /** @return string **/ function foo(): bool { return 5; }\n"
0971                       "/** @return string **/ function foo(): int { return 5.5; }\n"
0972                       "/** @return string **/ function foo(): float { return 5; }\n"
0973                       "/** @return int **/ function foo(): string { return 5.5; }\n"
0974                       "/** @return int **/ function foo(): mixed { return 5.5; }\n");
0975 
0976     TopDUContext* top = parse(method, DumpNone);
0977     DUChainReleaser releaseTop(top);
0978     DUChainWriteLocker lock(DUChain::lock());
0979 
0980     QVERIFY(!top->parentContext());
0981     QCOMPARE(top->childContexts().count(), 10);
0982     QCOMPARE(top->localDeclarations().count(), 5);
0983 
0984     FunctionType::Ptr fun = top->localDeclarations().at(0)->type<FunctionType>();
0985     QVERIFY(fun);
0986     auto returnType = fun->returnType().dynamicCast<IntegralType>();
0987     QVERIFY(returnType);
0988     QVERIFY(returnType->dataType() == IntegralType::TypeBoolean);
0989 
0990     fun = top->localDeclarations().at(1)->type<FunctionType>();
0991     QVERIFY(fun);
0992     returnType = fun->returnType().dynamicCast<IntegralType>();
0993     QVERIFY(returnType);
0994     QVERIFY(returnType->dataType() == IntegralType::TypeInt);
0995 
0996     fun = top->localDeclarations().at(2)->type<FunctionType>();
0997     QVERIFY(fun);
0998     returnType = fun->returnType().dynamicCast<IntegralType>();
0999     QVERIFY(returnType);
1000     QVERIFY(returnType->dataType() == IntegralType::TypeFloat);
1001 
1002     fun = top->localDeclarations().at(3)->type<FunctionType>();
1003     QVERIFY(fun);
1004     returnType = fun->returnType().dynamicCast<IntegralType>();
1005     QVERIFY(returnType);
1006     QVERIFY(returnType->dataType() == IntegralType::TypeString);
1007 
1008     fun = top->localDeclarations().at(4)->type<FunctionType>();
1009     QVERIFY(fun);
1010     returnType = fun->returnType().dynamicCast<IntegralType>();
1011     QVERIFY(returnType);
1012     QVERIFY(returnType->dataType() == IntegralType::TypeMixed);
1013 }
1014 
1015 void TestDUChain::declarationReturnTypeTypehintVoid()
1016 {
1017     //Typehint preferred over phpdoc preferred over inferred type
1018     QByteArray method("<? /** @return string **/ function foo(): void { return 5; }");
1019 
1020     TopDUContext* top = parse(method, DumpNone);
1021     DUChainReleaser releaseTop(top);
1022     DUChainWriteLocker lock(DUChain::lock());
1023 
1024     QVERIFY(!top->parentContext());
1025     QCOMPARE(top->childContexts().count(), 2);
1026     QCOMPARE(top->localDeclarations().count(), 1);
1027 
1028     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1029     QVERIFY(fun);
1030     auto returnType = fun->returnType().dynamicCast<IntegralType>();
1031     QVERIFY(returnType);
1032     QVERIFY(returnType->dataType() == IntegralType::TypeVoid);
1033 }
1034 
1035 void TestDUChain::declarationReturnTypeTypehintObject()
1036 {
1037     //Typehint preferred over phpdoc preferred over inferred type
1038     QByteArray method("<? /** @return string **/ function foo(): object { return new stdClass(); }");
1039 
1040     TopDUContext* top = parse(method, DumpNone);
1041     DUChainReleaser releaseTop(top);
1042     DUChainWriteLocker lock(DUChain::lock());
1043 
1044     QVERIFY(!top->parentContext());
1045     QCOMPARE(top->childContexts().count(), 2);
1046     QCOMPARE(top->localDeclarations().count(), 1);
1047 
1048     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1049     QVERIFY(fun);
1050     auto returnType = fun->returnType().dynamicCast<IntegralTypeExtended>();
1051     QVERIFY(returnType);
1052     QVERIFY(returnType->dataType() == IntegralTypeExtended::TypeObject);
1053 }
1054 
1055 void TestDUChain::declarationReturnTypeTypehintUnion()
1056 {
1057     //                 0         1         2         3         4         5
1058     //                 012345678901234567890123456789012345678901234567890
1059     QByteArray method("<? function foo(): string|int|float|bool { } ");
1060 
1061     TopDUContext* top = parse(method, DumpAll);
1062     DUChainReleaser releaseTop(top);
1063 
1064     DUChainWriteLocker lock(DUChain::lock());
1065 
1066     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1067     QVERIFY(fun);
1068     auto returnType = fun->returnType().dynamicCast<UnsureType>();
1069     QVERIFY(returnType);
1070 
1071     QCOMPARE(returnType->typesSize(), 4u);
1072     QVERIFY(returnType->types()[0].abstractType().dynamicCast<IntegralType>());
1073     QVERIFY(returnType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
1074     QVERIFY(returnType->types()[1].abstractType().dynamicCast<IntegralType>());
1075     QVERIFY(returnType->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
1076     QVERIFY(returnType->types()[2].abstractType().dynamicCast<IntegralType>());
1077     QVERIFY(returnType->types()[2].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeFloat);
1078     QVERIFY(returnType->types()[3].abstractType().dynamicCast<IntegralType>());
1079     QVERIFY(returnType->types()[3].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean);
1080 }
1081 
1082 void TestDUChain::declareTypehintFunction()
1083 {
1084     //                 0         1         2         3         4         5         6         7
1085     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1086     QByteArray method("<? class A {} function foo(A $i) { return $i; } ");
1087 
1088     TopDUContext* top = parse(method, DumpAll);
1089     DUChainReleaser releaseTop(top);
1090 
1091     DUChainWriteLocker lock(DUChain::lock());
1092 
1093     QVERIFY(!top->parentContext());
1094     QCOMPARE(top->childContexts().count(), 3);
1095     QCOMPARE(top->localDeclarations().count(), 2);
1096 
1097     Declaration* dec = top->localDeclarations().at(0);
1098     QCOMPARE(dec->internalContext(), top->childContexts().at(0));
1099     QCOMPARE(dec->uses().count(), 1);
1100     QCOMPARE(dec->uses().begin()->count(), 1);
1101 
1102     QCOMPARE(top->childContexts().at(0)->localScopeIdentifier(), QualifiedIdentifier(u"a"));
1103     QCOMPARE(top->childContexts().at(0)->childContexts().count(), 0);
1104 
1105     DUContext* contextFunctionFoo = top->childContexts().at(1);
1106     QCOMPARE(contextFunctionFoo->localScopeIdentifier(), QualifiedIdentifier(u"foo"));
1107 
1108     DUContext* contextFunctionBodyFoo = top->childContexts().at(2);
1109     QCOMPARE(contextFunctionBodyFoo->localScopeIdentifier(), QualifiedIdentifier(u"foo"));
1110     QCOMPARE(contextFunctionBodyFoo->importedParentContexts().count(), 1);
1111     QCOMPARE(contextFunctionBodyFoo->childContexts().count(), 0);
1112 
1113     QVERIFY(contextFunctionBodyFoo->importedParentContexts().first().context(top) ==
1114             contextFunctionFoo);
1115 
1116     QVERIFY(top->childContexts().at(1)->localDeclarations().first()->type<StructureType>());
1117     QCOMPARE(top->childContexts().at(1)->localDeclarations().first()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1118 
1119     FunctionType::Ptr fType = top->localDeclarations().at(1)->type<FunctionType>();
1120     QVERIFY(fType);
1121     QVERIFY(fType->returnType().dynamicCast<StructureType>());
1122     QCOMPARE(fType->returnType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1123 }
1124 
1125 void TestDUChain::declareVariadicFunction()
1126 {
1127     //                 0         1         2         3         4         5         6         7
1128     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1129     QByteArray method("<? function foo(...$i) { } ");
1130 
1131     TopDUContext* top = parse(method, DumpAll);
1132     DUChainReleaser releaseTop(top);
1133 
1134     DUChainWriteLocker lock(DUChain::lock());
1135 
1136     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1137     QVERIFY(fun);
1138     QCOMPARE(fun->arguments().count(), 1);
1139 
1140     AbstractType::Ptr arg = fun->arguments().first();
1141     QVERIFY(arg);
1142     QVERIFY(arg.dynamicCast<KDevelop::ArrayType>());
1143 
1144     AbstractType::Ptr typehint = arg.staticCast<KDevelop::ArrayType>()->elementType();
1145     QVERIFY(typehint);
1146     QVERIFY(typehint.dynamicCast<IntegralType>());
1147     QVERIFY(typehint.staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
1148 }
1149 
1150 void TestDUChain::declareTypehintVariadicFunction()
1151 {
1152     //                 0         1         2         3         4         5         6         7
1153     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1154     QByteArray method("<? class A {} function foo(A ...$i) { } ");
1155 
1156     TopDUContext* top = parse(method, DumpAll);
1157     DUChainReleaser releaseTop(top);
1158 
1159     DUChainWriteLocker lock(DUChain::lock());
1160 
1161     auto fun = top->localDeclarations().at(1)->type<FunctionType>();
1162     QVERIFY(fun);
1163     QCOMPARE(fun->arguments().count(), 1);
1164 
1165     AbstractType::Ptr arg = fun->arguments().first();
1166     QVERIFY(arg);
1167     QVERIFY(arg.dynamicCast<KDevelop::ArrayType>());
1168 
1169     auto typehint = arg.staticCast<KDevelop::ArrayType>()->elementType();
1170     QVERIFY(typehint);
1171     QCOMPARE(typehint->toString(), QStringLiteral("A"));
1172 }
1173 
1174 void TestDUChain::declareTypehintObjectFunction()
1175 {
1176     //                 0         1         2         3         4         5         6         7
1177     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1178     QByteArray method("<? function foo(object $i) { } ");
1179 
1180     TopDUContext* top = parse(method, DumpAll);
1181     DUChainReleaser releaseTop(top);
1182 
1183     DUChainWriteLocker lock(DUChain::lock());
1184 
1185     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1186     QVERIFY(fun);
1187     QCOMPARE(fun->arguments().count(), 1);
1188     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1189     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralTypeExtended::TypeObject);
1190 
1191     IntegralTypeExtended::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralTypeExtended>();
1192     QVERIFY(type);
1193     QVERIFY(type->dataType() == IntegralTypeExtended::TypeObject);
1194 }
1195 
1196 void TestDUChain::declareTypehintObjectFunctionWithNullDefault()
1197 {
1198     //                 0         1         2         3         4         5         6         7
1199     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1200     QByteArray method("<? function foo(?object $i = NULL) { } ");
1201 
1202     TopDUContext* top = parse(method, DumpAll);
1203     DUChainReleaser releaseTop(top);
1204 
1205     DUChainWriteLocker lock(DUChain::lock());
1206 
1207     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1208     QVERIFY(fun);
1209     QCOMPARE(fun->arguments().count(), 1);
1210     QVERIFY(fun->arguments().first().dynamicCast<UnsureType>());
1211 
1212     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
1213     QVERIFY(argType);
1214     QCOMPARE(argType->typesSize(), 2u);
1215     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
1216     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralTypeExtended::TypeObject);
1217     QVERIFY(argType->types()[1].abstractType().dynamicCast<IntegralType>());
1218     QVERIFY(argType->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeNull);
1219 }
1220 
1221 void TestDUChain::declareTypehintObjectFunctionWithInvalidDefaultValue()
1222 {
1223     //                 0         1         2         3         4         5         6         7
1224     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1225     QByteArray method("<? function foo(object $i = '') { } ");
1226 
1227     TopDUContext* top = parse(method, DumpAll);
1228     DUChainReleaser releaseTop(top);
1229 
1230     DUChainWriteLocker lock(DUChain::lock());
1231 
1232     QVERIFY(!top->problems().isEmpty());
1233 }
1234 
1235 void TestDUChain::declaredTypehintOverridesDetectedValue()
1236 {
1237     //                 0         1         2         3         4         5         6         7
1238     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1239     QByteArray method("<? function foo(int $i = false) { } ");
1240 
1241     TopDUContext* top = parse(method, DumpAll);
1242     DUChainReleaser releaseTop(top);
1243 
1244     DUChainWriteLocker lock(DUChain::lock());
1245 
1246     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1247     QVERIFY(fun);
1248     QCOMPARE(fun->arguments().count(), 1);
1249     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1250 
1251     IntegralType::Ptr type = fun->arguments().first().dynamicCast<IntegralType>();
1252     QVERIFY(type);
1253     QVERIFY(type->dataType() == IntegralType::TypeInt);
1254 }
1255 
1256 void TestDUChain::declareTypehintArrayFunction()
1257 {
1258     //                 0         1         2         3         4         5         6         7
1259     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1260     QByteArray method("<? function foo(array $i) { } ");
1261 
1262     TopDUContext* top = parse(method, DumpAll);
1263     DUChainReleaser releaseTop(top);
1264 
1265     DUChainWriteLocker lock(DUChain::lock());
1266 
1267     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1268     QVERIFY(fun);
1269     QCOMPARE(fun->arguments().count(), 1);
1270     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1271     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
1272 
1273     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1274     QVERIFY(type);
1275     QVERIFY(type->dataType() == IntegralType::TypeArray);
1276 }
1277 
1278 void TestDUChain::declareTypehintCallableFunction()
1279 {
1280     //                 0         1         2         3
1281     //                 0123456789012345678901234567890123
1282     QByteArray method("<? function foo(callable $i) { } ");
1283 
1284     TopDUContext* top = parse(method, DumpAll);
1285     DUChainReleaser releaseTop(top);
1286 
1287     DUChainWriteLocker lock(DUChain::lock());
1288 
1289     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1290     QVERIFY(fun);
1291     QCOMPARE(fun->arguments().count(), 1);
1292     QVERIFY(fun->arguments().first().dynamicCast<IntegralTypeExtended>());
1293     QVERIFY(fun->arguments().first().staticCast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable);
1294 
1295     IntegralTypeExtended::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralTypeExtended>();
1296     QVERIFY(type);
1297     QVERIFY(type->dataType() == IntegralTypeExtended::TypeCallable);
1298 }
1299 
1300 void Php::TestDUChain::functionWithCallableAndFunctionReturn()
1301 {
1302     QByteArray method("<? function foo(callable $i) { return $i; return function () {}; } ");
1303 
1304     TopDUContext* top = parse(method, DumpAll);
1305     DUChainReleaser releaseTop(top);
1306 
1307     DUChainWriteLocker lock(DUChain::lock());
1308 
1309     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1310     QVERIFY(fun);
1311     QCOMPARE(fun->arguments().count(), 1);
1312     QVERIFY(fun->arguments().first().dynamicCast<IntegralTypeExtended>());
1313     QVERIFY(fun->arguments().first().staticCast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable);
1314 
1315     auto retType = fun->returnType().dynamicCast<IntegralTypeExtended>();
1316     QVERIFY(retType);
1317     QVERIFY(retType->dataType() == IntegralTypeExtended::TypeCallable);
1318 }
1319 
1320 void TestDUChain::declareTypehintIterableFunction()
1321 {
1322     //Note: in practice, Traversable is defined by php, but this interface is not loaded in this test, so define it ourselves
1323     //                 0         1         2         3
1324     //                 0123456789012345678901234567890123
1325     QByteArray method("<? interface Traversable { } function foo(iterable $i) { } ");
1326 
1327     TopDUContext* top = parse(method, DumpAll);
1328     DUChainReleaser releaseTop(top);
1329 
1330     DUChainWriteLocker lock(DUChain::lock());
1331 
1332     QCOMPARE(top->localDeclarations().count(), 2);
1333     FunctionType::Ptr fun = top->localDeclarations().at(1)->type<FunctionType>();
1334     QVERIFY(fun);
1335     QCOMPARE(fun->arguments().count(), 1);
1336 
1337     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
1338     QVERIFY(argType);
1339     QCOMPARE(argType->typesSize(), 2u);
1340     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
1341     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
1342     QVERIFY(argType->types()[1].abstractType().dynamicCast<StructureType>());
1343     QCOMPARE(argType->types()[1].abstractType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"traversable"));
1344 }
1345 
1346 void TestDUChain::declareTypehintBoolFunction()
1347 {
1348     //                 0         1         2         3
1349     //                 0123456789012345678901234567890123
1350     QByteArray method("<? function foo(bool $i) { } ");
1351 
1352     TopDUContext* top = parse(method, DumpAll);
1353     DUChainReleaser releaseTop(top);
1354 
1355     DUChainWriteLocker lock(DUChain::lock());
1356 
1357     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1358     QVERIFY(fun);
1359     QCOMPARE(fun->arguments().count(), 1);
1360     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1361     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean);
1362 
1363     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1364     QVERIFY(type);
1365     QVERIFY(type->dataType() == IntegralType::TypeBoolean);
1366 }
1367 
1368 void TestDUChain::declareTypehintFloatFunction()
1369 {
1370     //                 0         1         2         3
1371     //                 0123456789012345678901234567890123
1372     QByteArray method("<? function foo(float $i) { } ");
1373 
1374     TopDUContext* top = parse(method, DumpAll);
1375     DUChainReleaser releaseTop(top);
1376 
1377     DUChainWriteLocker lock(DUChain::lock());
1378 
1379     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1380     QVERIFY(fun);
1381     QCOMPARE(fun->arguments().count(), 1);
1382     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1383     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeFloat);
1384 
1385     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1386     QVERIFY(type);
1387     QVERIFY(type->dataType() == IntegralType::TypeFloat);
1388 }
1389 
1390 void TestDUChain::declareTypehintIntFunction()
1391 {
1392     //                 0         1         2         3
1393     //                 0123456789012345678901234567890123
1394     QByteArray method("<? function foo(int $i) { } ");
1395 
1396     TopDUContext* top = parse(method, DumpAll);
1397     DUChainReleaser releaseTop(top);
1398 
1399     DUChainWriteLocker lock(DUChain::lock());
1400 
1401     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1402     QVERIFY(fun);
1403     QCOMPARE(fun->arguments().count(), 1);
1404     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1405     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
1406 
1407     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1408     QVERIFY(type);
1409     QVERIFY(type->dataType() == IntegralType::TypeInt);
1410 }
1411 
1412 void TestDUChain::declareTypehintStringFunction()
1413 {
1414     //                 0         1         2         3
1415     //                 0123456789012345678901234567890123
1416     QByteArray method("<? function foo(string $i) { } ");
1417 
1418     TopDUContext* top = parse(method, DumpAll);
1419     DUChainReleaser releaseTop(top);
1420 
1421     DUChainWriteLocker lock(DUChain::lock());
1422 
1423     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1424     QVERIFY(fun);
1425     QCOMPARE(fun->arguments().count(), 1);
1426     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1427     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
1428 
1429     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1430     QVERIFY(type);
1431     QVERIFY(type->dataType() == IntegralType::TypeString);
1432 }
1433 
1434 void TestDUChain::declareTypehintMixedFunction()
1435 {
1436     //                 0         1         2         3
1437     //                 0123456789012345678901234567890123
1438     QByteArray method("<? function foo(mixed $i) { } ");
1439 
1440     TopDUContext* top = parse(method, DumpAll);
1441     DUChainReleaser releaseTop(top);
1442 
1443     DUChainWriteLocker lock(DUChain::lock());
1444 
1445     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1446     QVERIFY(fun);
1447     QCOMPARE(fun->arguments().count(), 1);
1448     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1449     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
1450 
1451     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1452     QVERIFY(type);
1453     QVERIFY(type->dataType() == IntegralType::TypeMixed);
1454 }
1455 
1456 void TestDUChain::declareNullableTypehintArrayFunction()
1457 {
1458     //                 0         1         2         3
1459     //                 0123456789012345678901234567890123
1460     QByteArray method("<? function foo(?array $i) { } ");
1461 
1462     TopDUContext* top = parse(method, DumpAll);
1463     DUChainReleaser releaseTop(top);
1464 
1465     DUChainWriteLocker lock(DUChain::lock());
1466 
1467     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1468     QVERIFY(fun);
1469     QCOMPARE(fun->arguments().count(), 1);
1470 
1471     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
1472     QVERIFY(argType);
1473     QCOMPARE(argType->typesSize(), 2u);
1474     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
1475     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
1476     QVERIFY(argType->types()[1].abstractType().dynamicCast<IntegralType>());
1477     QVERIFY(argType->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeNull);
1478 
1479     UnsureType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<UnsureType>();
1480     QVERIFY(type);
1481     QCOMPARE(type->typesSize(), 2u);
1482     QVERIFY(type->types()[0].abstractType().dynamicCast<IntegralType>());
1483     QVERIFY(type->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
1484     QVERIFY(type->types()[1].abstractType().dynamicCast<IntegralType>());
1485     QVERIFY(type->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeNull);
1486 }
1487 
1488 void TestDUChain::declareTypehintWithPhpdocFunction()
1489 {
1490     //                 0         1         2         3
1491     //                 0123456789012345678901234567890123
1492     QByteArray method("<? /**\n * @param string $i\n */ function foo(int $i) { } ");
1493 
1494     TopDUContext* top = parse(method, DumpAll);
1495     DUChainReleaser releaseTop(top);
1496 
1497     DUChainWriteLocker lock(DUChain::lock());
1498 
1499     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1500     QVERIFY(fun);
1501     QCOMPARE(fun->arguments().count(), 1);
1502     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1503     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
1504 
1505     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1506     QVERIFY(type);
1507     QVERIFY(type->dataType() == IntegralType::TypeInt);
1508 }
1509 
1510 void TestDUChain::declareNullableTypehintMixedFunction()
1511 {
1512     //                 0         1         2         3
1513     //                 0123456789012345678901234567890123
1514     QByteArray method("<? function foo(?UnknownClass $i) { } ");
1515 
1516     TopDUContext* top = parse(method, DumpAll);
1517     DUChainReleaser releaseTop(top);
1518 
1519     DUChainWriteLocker lock(DUChain::lock());
1520 
1521     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1522     QVERIFY(fun);
1523     QCOMPARE(fun->arguments().count(), 1);
1524     QVERIFY(fun->arguments().first().dynamicCast<IntegralType>());
1525     QVERIFY(fun->arguments().first().staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
1526 
1527     IntegralType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<IntegralType>();
1528     QVERIFY(type);
1529     QVERIFY(type->dataType() == IntegralType::TypeMixed);
1530 }
1531 
1532 void TestDUChain::declareTypehintNullableIterableFunction()
1533 {
1534     //Note: in practice, Traversable is defined by php, but this interface is not loaded in this test, so define it ourselves
1535     //                 0         1         2         3
1536     //                 0123456789012345678901234567890123
1537     QByteArray method("<? interface Traversable { } function foo(?iterable $i) { } ");
1538 
1539     TopDUContext* top = parse(method, DumpAll);
1540     DUChainReleaser releaseTop(top);
1541 
1542     DUChainWriteLocker lock(DUChain::lock());
1543 
1544     QCOMPARE(top->localDeclarations().count(), 2);
1545     FunctionType::Ptr fun = top->localDeclarations().at(1)->type<FunctionType>();
1546     QVERIFY(fun);
1547     QCOMPARE(fun->arguments().count(), 1);
1548 
1549     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
1550     QVERIFY(argType);
1551     QCOMPARE(argType->typesSize(), 3u);
1552     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
1553     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
1554     QVERIFY(argType->types()[1].abstractType().dynamicCast<StructureType>());
1555     QCOMPARE(argType->types()[1].abstractType().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"traversable"));
1556     QVERIFY(argType->types()[2].abstractType().dynamicCast<IntegralType>());
1557     QVERIFY(argType->types()[2].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeNull);
1558 }
1559 
1560 void TestDUChain::declareTypehintUnionFunction()
1561 {
1562     //                 0         1         2         3         4         5
1563     //                 012345678901234567890123456789012345678901234567890
1564     QByteArray method("<? function foo(string|int|float|bool $i) { } ");
1565 
1566     TopDUContext* top = parse(method, DumpAll);
1567     DUChainReleaser releaseTop(top);
1568 
1569     DUChainWriteLocker lock(DUChain::lock());
1570 
1571     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
1572     QVERIFY(fun);
1573     QCOMPARE(fun->arguments().count(), 1);
1574 
1575     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
1576     QVERIFY(argType);
1577     QCOMPARE(argType->typesSize(), 4u);
1578     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
1579     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
1580     QVERIFY(argType->types()[1].abstractType().dynamicCast<IntegralType>());
1581     QVERIFY(argType->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
1582     QVERIFY(argType->types()[2].abstractType().dynamicCast<IntegralType>());
1583     QVERIFY(argType->types()[2].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeFloat);
1584     QVERIFY(argType->types()[3].abstractType().dynamicCast<IntegralType>());
1585     QVERIFY(argType->types()[3].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean);
1586 }
1587 
1588 void TestDUChain::classImplementsInterface()
1589 {
1590     //                 0         1         2         3         4         5         6         7
1591     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1592     QByteArray method("<? interface I { } class A implements I { }");
1593 
1594     TopDUContext* top = parse(method, DumpAll);
1595     DUChainReleaser releaseTop(top);
1596 
1597     DUChainWriteLocker lock(DUChain::lock());
1598     QCOMPARE(top->childContexts().count(), 2);
1599     QCOMPARE(top->localDeclarations().count(), 2);
1600 
1601     //interface I
1602     Declaration* dec = top->localDeclarations().at(0);
1603     QVERIFY(dec->isDefinition());
1604     QCOMPARE(dec->identifier(), Identifier(u"i"));
1605     QCOMPARE(dec->toString(), QString("interface I"));
1606     StructureType::Ptr typeI = dec->type<StructureType>();
1607     QCOMPARE(typeI->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
1608     QVERIFY(typeI->declaration(top) == dec);
1609     ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
1610     QVERIFY(classDec);
1611     QCOMPARE(classDec->classType(), ClassDeclarationData::Interface);
1612 
1613     QCOMPARE(dec->internalContext(), top->childContexts().at(0));
1614     QCOMPARE(dec->internalContext()->childContexts().count(), 0);
1615     QCOMPARE(dec->internalContext()->importedParentContexts().count(), 0);
1616     QCOMPARE(dec->internalContext()->localScopeIdentifier(), QualifiedIdentifier(u"i"));
1617 
1618     QCOMPARE(dec->uses().count(), 1);
1619     QCOMPARE(dec->uses().begin()->count(), 1);
1620 
1621     IndexedType indexedTypeI = classDec->indexedType();
1622 
1623     //class A
1624     dec = top->localDeclarations().at(1);
1625     QVERIFY(dec->isDefinition());
1626     QCOMPARE(dec->identifier(), Identifier(u"a"));
1627     StructureType::Ptr typeA = dec->type<StructureType>();
1628     QCOMPARE(typeA->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1629     QVERIFY(typeA->declaration(top) == dec);
1630     classDec = dynamic_cast<ClassDeclaration*>(dec);
1631     QVERIFY(classDec);
1632     QCOMPARE(classDec->classType(), ClassDeclarationData::Class);
1633 
1634     QCOMPARE(dec->internalContext(), top->childContexts().at(1));
1635     QCOMPARE(dec->internalContext()->childContexts().count(), 0);
1636     QCOMPARE(dec->internalContext()->localScopeIdentifier(), QualifiedIdentifier(u"a"));
1637     //class A imports interface I context
1638     QCOMPARE(dec->internalContext()->importedParentContexts().count(), 1);
1639     QVERIFY(dec->internalContext()->importedParentContexts().at(0).context(top) == top->childContexts().at(0));
1640 
1641     QCOMPARE(classDec->baseClassesSize(), 1u);
1642     QCOMPARE(classDec->baseClasses()[0].baseClass, indexedTypeI);
1643 
1644     QCOMPARE(dec->uses().count(), 0);
1645 }
1646 
1647 void TestDUChain::classExtends()
1648 {
1649     //                 0         1         2         3         4         5         6         7
1650     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1651     QByteArray method("<? class A { } class B extends A { } ");
1652 
1653     TopDUContext* top = parse(method, DumpNone);
1654     DUChainReleaser releaseTop(top);
1655 
1656     DUChainWriteLocker lock(DUChain::lock());
1657     QCOMPARE(top->childContexts().count(), 2);
1658     QCOMPARE(top->localDeclarations().count(), 2);
1659 
1660     //class A
1661     Declaration* dec = top->localDeclarations().at(0);
1662     QVERIFY(dec->isDefinition());
1663     QCOMPARE(dec->identifier(), Identifier(u"a"));
1664     StructureType::Ptr typeA = dec->type<StructureType>();
1665     QCOMPARE(typeA->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1666     QVERIFY(typeA->declaration(top) == dec);
1667     ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
1668     QVERIFY(classDec);
1669     QCOMPARE(classDec->classType(), ClassDeclarationData::Class);
1670 
1671     QCOMPARE(dec->internalContext(), top->childContexts().at(0));
1672     QCOMPARE(dec->internalContext()->childContexts().count(), 0);
1673     QCOMPARE(dec->internalContext()->importedParentContexts().count(), 0);
1674     QCOMPARE(dec->internalContext()->localScopeIdentifier(), QualifiedIdentifier(u"a"));
1675 
1676     QCOMPARE(dec->uses().count(), 1);
1677     QCOMPARE(dec->uses().begin()->count(), 1);
1678 
1679     IndexedType indexedTypeA = classDec->indexedType();
1680 
1681     //class B
1682     dec = top->localDeclarations().at(1);
1683     QVERIFY(dec->isDefinition());
1684     QCOMPARE(dec->identifier(), Identifier(u"b"));
1685     StructureType::Ptr typeB = dec->type<StructureType>();
1686     QCOMPARE(typeB->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1687     QVERIFY(typeB->declaration(top) == dec);
1688     classDec = dynamic_cast<ClassDeclaration*>(dec);
1689     QVERIFY(classDec);
1690     QCOMPARE(classDec->classType(), ClassDeclarationData::Class);
1691 
1692     QCOMPARE(dec->internalContext(), top->childContexts().at(1));
1693     QCOMPARE(dec->internalContext()->childContexts().count(), 0);
1694     QCOMPARE(dec->internalContext()->localScopeIdentifier(), QualifiedIdentifier(u"b"));
1695     //class B imports class A context
1696     QCOMPARE(dec->internalContext()->importedParentContexts().count(), 1);
1697     QVERIFY(dec->internalContext()->importedParentContexts().at(0).context(top) == top->childContexts().at(0));
1698 
1699     QCOMPARE(classDec->baseClassesSize(), 1u);
1700     QCOMPARE(classDec->baseClasses()[0].baseClass, indexedTypeA);
1701 
1702     QCOMPARE(dec->uses().count(), 0);
1703 }
1704 
1705 
1706 void TestDUChain::staticMethod()
1707 {
1708     //                 0         1         2         3         4         5         6         7
1709     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1710     QByteArray method("<? class B {} class A { static function foo() { return new B(); } } $i = A::foo();");
1711 
1712     TopDUContext* top = parse(method, DumpAll);
1713     DUChainReleaser releaseTop(top);
1714 
1715     DUChainWriteLocker lock(DUChain::lock());
1716 
1717     StructureType::Ptr type = top->localDeclarations().at(2)->type<StructureType>();
1718     QVERIFY(type);
1719     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1720 }
1721 
1722 void TestDUChain::ownStaticMethod()
1723 {
1724     //                 0         1         2         3         4         5         6         7
1725     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1726     QByteArray method("<? class B {} class A { static function foo() { return new B(); } function bar() { $i = self::foo(); $j = A::foo(); } } ");
1727 
1728     TopDUContext* top = parse(method, DumpAll);
1729     DUChainReleaser releaseTop(top);
1730 
1731     DUChainWriteLocker lock(DUChain::lock());
1732 
1733     QVERIFY(top->childContexts().at(1));
1734     QVERIFY(top->childContexts().at(1)->localDeclarations().at(0));
1735     QVERIFY(top->childContexts().at(1)->localDeclarations().at(0)->type<FunctionType>());
1736     AbstractType::Ptr ret = top->childContexts().at(1)->localDeclarations().at(0)
1737                             ->type<FunctionType>()->returnType();
1738 
1739     QVERIFY(ret.dynamicCast<StructureType>());
1740     QCOMPARE(ret.staticCast<StructureType>()->declaration(top), top->localDeclarations().at(0));
1741 
1742     QVERIFY(top->childContexts().at(1)->childContexts().at(1 + 2));
1743     QVERIFY(top->childContexts().at(1)->childContexts().at(1 + 2)->localDeclarations().at(0));
1744     QVERIFY(top->childContexts().at(1)->childContexts().at(1 + 2)->localDeclarations().at(0)->type<StructureType>());
1745     QCOMPARE(top->childContexts().at(1)->childContexts().at(1 + 2)->localDeclarations().at(0)
1746              ->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1747     QCOMPARE(top->childContexts().at(1)->childContexts().at(1 + 2)->localDeclarations().at(1)
1748              ->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1749 }
1750 void TestDUChain::thisVar()
1751 {
1752     //                 0         1         2         3         4         5         6         7
1753     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1754     QByteArray method("<? class A { function x() { return $this; } function y() { return $this->x(); } } ");
1755 
1756     TopDUContext* top = parse(method, DumpNone);
1757     DUChainReleaser releaseTop(top);
1758 
1759     DUChainWriteLocker lock(DUChain::lock());
1760 
1761     FunctionType::Ptr fn = top->childContexts().at(0)->localDeclarations().at(0)->type<FunctionType>();
1762     QVERIFY(fn);
1763     auto cls = fn->returnType().dynamicCast<StructureType>();
1764     QVERIFY(cls);
1765     QCOMPARE(cls->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1766 
1767     fn = top->childContexts().at(0)->localDeclarations().at(1)->type<FunctionType>();
1768     QVERIFY(fn);
1769     cls = fn->returnType().dynamicCast<StructureType>();
1770     QVERIFY(cls);
1771     QCOMPARE(cls->qualifiedIdentifier(), QualifiedIdentifier(u"a"));
1772 }
1773 
1774 void TestDUChain::objectFunctionCall()
1775 {
1776     //                 0         1         2         3         4         5         6         7
1777     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1778     QByteArray method("<? class B {} class A { function x() { return new B(); } function y() { $a = new A(); return $a->x(); } } ");
1779 
1780     TopDUContext* top = parse(method, DumpNone);
1781     DUChainReleaser releaseTop(top);
1782 
1783     DUChainWriteLocker lock(DUChain::lock());
1784 
1785     FunctionType::Ptr fn = top->childContexts().at(1)->localDeclarations().at(0)->type<FunctionType>();
1786     QVERIFY(fn);
1787     auto cls = fn->returnType().dynamicCast<StructureType>();
1788     QVERIFY(cls);
1789     QCOMPARE(cls->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1790 
1791     fn = top->childContexts().at(1)->localDeclarations().at(1)->type<FunctionType>();
1792     QVERIFY(fn);
1793     cls = fn->returnType().dynamicCast<StructureType>();
1794     QVERIFY(cls);
1795     QCOMPARE(cls->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1796 }
1797 
1798 void TestDUChain::objectFunctionCall2()
1799 {
1800     //                 0         1         2         3         4         5         6         7
1801     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1802     QByteArray method("<? class C {} class B { function c() { return new C(); } } class A { function x() { return new B(); } function y() { $a = new A(); return $a->x()->c(); } } ");
1803 
1804     TopDUContext* top = parse(method, DumpNone);
1805     DUChainReleaser releaseTop(top);
1806 
1807     DUChainWriteLocker lock(DUChain::lock());
1808 
1809     FunctionType::Ptr fn = top->childContexts().at(2)->localDeclarations().at(1)->type<FunctionType>();
1810     QVERIFY(fn);
1811     auto cls = fn->returnType().dynamicCast<StructureType>();
1812     QVERIFY(cls);
1813     QCOMPARE(cls->qualifiedIdentifier(), QualifiedIdentifier(u"c"));
1814 }
1815 
1816 void TestDUChain::objectFunctionCall3()
1817 {
1818     //                 0         1         2         3         4         5         6         7
1819     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1820     QByteArray method("<? class B {} class A { function b() { return new B(); } } $i = new A(); $j = $i->b();");
1821 
1822     TopDUContext* top = parse(method, DumpNone);
1823     DUChainReleaser releaseTop(top);
1824 
1825     DUChainWriteLocker lock(DUChain::lock());
1826 
1827     QCOMPARE(top->localDeclarations().at(2)->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
1828     QCOMPARE(top->localDeclarations().at(2)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a"));;
1829     QCOMPARE(top->localDeclarations().at(3)->qualifiedIdentifier(), QualifiedIdentifier(u"j"));
1830     QCOMPARE(top->localDeclarations().at(3)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));;
1831 }
1832 
1833 void TestDUChain::objectVariable()
1834 {
1835     //                 0         1         2         3         4         5         6         7
1836     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1837     QByteArray method("<? class B {} class A { /** @var B **/ public $foo; } $a = new A(); $i = $a->foo;");
1838 
1839     TopDUContext* top = parse(method, DumpNone);
1840     DUChainReleaser releaseTop(top);
1841     DUChainWriteLocker lock(DUChain::lock());
1842 
1843     QCOMPARE(top->localDeclarations().at(3)->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
1844     QCOMPARE(top->localDeclarations().at(3)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));;
1845 }
1846 
1847 void TestDUChain::staticMemberVariable()
1848 {
1849     //                 0         1         2         3         4         5         6         7
1850     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1851     QByteArray method("<? class B {} class A { /** @var B **/ public static $foo; } $i = A::$foo;");
1852 
1853     TopDUContext* top = parse(method, DumpAll);
1854     DUChainReleaser releaseTop(top);
1855     DUChainWriteLocker lock(DUChain::lock());
1856 
1857     QCOMPARE(top->localDeclarations().at(2)->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
1858     QCOMPARE(top->localDeclarations().at(2)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));;
1859 }
1860 void TestDUChain::ownStaticMemberVariable()
1861 {
1862     //                 0         1         2         3         4         5         6         7
1863     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1864     QByteArray method("<? class B {} class A { /** @var B **/ public static $foo; function bar() { $i = self::$foo; $j = A::$foo; }} ");
1865 
1866     TopDUContext* top = parse(method, DumpNone);
1867     DUChainReleaser releaseTop(top);
1868     DUChainWriteLocker lock(DUChain::lock());
1869 
1870     DUContext* barContext = top->childContexts().at(1)->childContexts().at(1);
1871     QCOMPARE(barContext->localDeclarations().at(0)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1872     QCOMPARE(barContext->localDeclarations().at(1)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
1873 }
1874 
1875 void TestDUChain::classConst_data()
1876 {
1877     QTest::addColumn<QString>("classBody");
1878     QTest::addColumn<int>("problems");
1879 
1880     QTest::newRow("int") << "const C = 1;" << 0;
1881     QTest::newRow("string") << "const C = 'asdf';" << 0;
1882     QTest::newRow("float") << "const C = 0.5;" << 0;
1883     QTest::newRow("bool") << "const C = true;" << 0;
1884     QTest::newRow("selfConst") << "const C2 = 1; const C = self::C2;" << 0;
1885     QTest::newRow("parentConst") << "const C = parent::P;" << 0;
1886     QTest::newRow("null") << "const C = null;" << 0;
1887     QTest::newRow("array") << "const C = array();" << 0;
1888     QTest::newRow("expression") << "const C = 'foo' . 'foo';" << 0;
1889 }
1890 
1891 void TestDUChain::classConst()
1892 {
1893     QFETCH(QString, classBody);
1894     QFETCH(int, problems);
1895 
1896     QString fullClass("<? class B { const P = 1; } class A extends B { " + classBody + " } ");
1897 
1898     TopDUContext* top = parse(fullClass.toUtf8(), DumpNone);
1899     DUChainReleaser releaseTop(top);
1900     DUChainWriteLocker lock;
1901 
1902     QCOMPARE(top->childContexts().count(), 2);
1903     QCOMPARE(top->problems().count(), problems);
1904 
1905     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::C")).count(), 1);
1906     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::C")).first()->context(), top->childContexts().last());
1907 }
1908 
1909 void TestDUChain::classConstWithTypeHint()
1910 {
1911     //                 0         1         2         3         4         5         6         7
1912     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1913     QByteArray method("<? class A { /**\n* @var integer\n**/\nconst C = 1; } ");
1914 
1915     TopDUContext* top = parse(method, DumpNone);
1916     DUChainReleaser releaseTop(top);
1917     DUChainWriteLocker lock(DUChain::lock());
1918 
1919     QCOMPARE(top->childContexts().count(), 1);
1920     QCOMPARE(top->problems().count(), 0);
1921 
1922     QList< Declaration* > decs = top->findDeclarations(QualifiedIdentifier(u"a::C"));
1923     QCOMPARE(decs.count(), 1);
1924     QCOMPARE(decs.first()->context(), top->childContexts().last());
1925 
1926     auto type = decs.first()->abstractType().dynamicCast<IntegralType>();
1927     QVERIFY(type);
1928     QCOMPARE(type->dataType(), IntegralType::TypeInt);
1929     QVERIFY(type->modifiers() & AbstractType::ConstModifier);
1930 }
1931 
1932 void TestDUChain::classConstVisibility()
1933 {
1934     //                 0         1         2         3         4         5         6         7
1935     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1936     QByteArray method("<? class A { public const B = 1; protected const C = 1; private const D = 1; } ");
1937 
1938     TopDUContext* top = parse(method, DumpNone);
1939     DUChainReleaser releaseTop(top);
1940     DUChainWriteLocker lock(DUChain::lock());
1941 
1942     QCOMPARE(top->childContexts().count(), 1);
1943     QCOMPARE(top->problems().count(), 0);
1944 
1945     QList< Declaration* > decs = top->findDeclarations(QualifiedIdentifier(u"a::B"));
1946     QCOMPARE(decs.count(), 1);
1947     QCOMPARE(decs.first()->context(), top->childContexts().last());
1948 
1949     auto type = decs.first()->abstractType().dynamicCast<IntegralType>();
1950     QVERIFY(type);
1951     QCOMPARE(type->dataType(), IntegralType::TypeInt);
1952     QVERIFY(type->modifiers() & AbstractType::ConstModifier);
1953 
1954     ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
1955     QVERIFY(cmdec->accessPolicy() == Declaration::Public);
1956 
1957     decs = top->findDeclarations(QualifiedIdentifier(u"a::C"));
1958     QCOMPARE(decs.count(), 1);
1959     QCOMPARE(decs.first()->context(), top->childContexts().last());
1960 
1961     type = decs.first()->abstractType().dynamicCast<IntegralType>();
1962     QVERIFY(type);
1963     QCOMPARE(type->dataType(), IntegralType::TypeInt);
1964     QVERIFY(type->modifiers() & AbstractType::ConstModifier);
1965 
1966     cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
1967     QVERIFY(cmdec->accessPolicy() == Declaration::Protected);
1968 
1969     decs = top->findDeclarations(QualifiedIdentifier(u"a::D"));
1970     QCOMPARE(decs.count(), 1);
1971     QCOMPARE(decs.first()->context(), top->childContexts().last());
1972 
1973     type = decs.first()->abstractType().dynamicCast<IntegralType>();
1974     QVERIFY(type);
1975     QCOMPARE(type->dataType(), IntegralType::TypeInt);
1976     QVERIFY(type->modifiers() & AbstractType::ConstModifier);
1977 
1978     cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
1979     QVERIFY(cmdec->accessPolicy() == Declaration::Private);
1980 }
1981 
1982 void TestDUChain::semiReservedClassConst()
1983 {
1984     //                 0         1         2         3         4         5         6         7
1985     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
1986     QByteArray method("<? class A { const SWITCH = 1; const PUBLIC = 'foo'; const STRING = 'bar'; } ");
1987 
1988     TopDUContext* top = parse(method, DumpNone);
1989     DUChainReleaser releaseTop(top);
1990     DUChainWriteLocker lock(DUChain::lock());
1991 
1992     QCOMPARE(top->childContexts().count(), 1);
1993     QCOMPARE(top->problems().count(), 0);
1994 
1995     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::SWITCH")).count(), 1);
1996     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::SWITCH")).first()->context(), top->childContexts().last());
1997 
1998     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::PUBLIC")).count(), 1);
1999     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::PUBLIC")).first()->context(), top->childContexts().last());
2000 
2001     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::STRING")).count(), 1);
2002     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a::STRING")).first()->context(), top->childContexts().last());
2003 }
2004 
2005 void TestDUChain::illegalClassConst_data()
2006 {
2007     QTest::addColumn<QString>("code");
2008 
2009     QTest::newRow("final const") << QStringLiteral("<? class A { final const C = 1; } ");
2010     QTest::newRow("static const") << QStringLiteral("<? class A { static const C = 1; } ");
2011     QTest::newRow("abstract const") << QStringLiteral("<? class A { abstract const C = 1; } ");
2012 }
2013 
2014 void TestDUChain::illegalClassConst()
2015 {
2016     QFETCH(QString, code);
2017 
2018     TopDUContext* top = parse(code.toUtf8(), DumpNone);
2019     QVERIFY(top);
2020     DUChainReleaser releaseTop(top);
2021     DUChainWriteLocker lock;
2022 
2023     QCOMPARE(top->problems().count(), 1);
2024 }
2025 
2026 void TestDUChain::fileConst_data()
2027 {
2028     QTest::addColumn<QString>("code");
2029     QTest::addColumn<int>("problems");
2030     QTest::addColumn<uint>("dataType");
2031 
2032     QTest::newRow("int") << "const C = 1;" << 0 << (uint) IntegralType::TypeInt;
2033     QTest::newRow("string") << "const C = 'asdf';" << 0 << (uint) IntegralType::TypeString;
2034     QTest::newRow("float") << "const C = 0.5;" << 0 << (uint) IntegralType::TypeFloat;
2035     QTest::newRow("bool") << "const C = true;" << 0 << (uint) IntegralType::TypeBoolean;
2036     QTest::newRow("array") << "const C = array();" << 0 << (uint) IntegralType::TypeArray;
2037     QTest::newRow("expression") << "const C = 'foo' . 'foo';" << 0 << (uint) IntegralType::TypeString;
2038 }
2039 
2040 void TestDUChain::fileConst()
2041 {
2042     QFETCH(QString, code);
2043     QFETCH(int, problems);
2044     QFETCH(uint, dataType);
2045 
2046     code.prepend("<?php ");
2047 
2048     TopDUContext* top = parse(code.toUtf8(), DumpNone);
2049     QVERIFY(top);
2050     DUChainReleaser releaseTop(top);
2051     DUChainWriteLocker lock;
2052 
2053     QCOMPARE(top->problems().count(), problems);
2054 
2055     QList< Declaration* > decs = top->findDeclarations(QualifiedIdentifier(QStringLiteral("C")));
2056     QCOMPARE(decs.count(), 1);
2057     auto type = decs.first()->abstractType().dynamicCast<IntegralType>();
2058     QVERIFY(type);
2059     QCOMPARE(type->dataType(), dataType);
2060     QVERIFY(type->modifiers() & AbstractType::ConstModifier);
2061 }
2062 
2063 void TestDUChain::semiReservedFileConst()
2064 {
2065     //                 0         1         2         3         4         5         6         7
2066     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2067     QByteArray method("<? const SWITCH = 1; ");
2068 
2069     TopDUContext* top = parse(method, DumpNone);
2070     QVERIFY(!top);
2071 }
2072 
2073 void TestDUChain::define()
2074 {
2075     // the last define tests that we don't crash under that circumstance
2076     //                 0         1         2         3         4         5         6         7
2077     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2078     QByteArray method("<? define('FOO', 'foo'); function x() { define('BAR', 'bar'); } define(); ");
2079 
2080     TopDUContext* top = parse(method, DumpNone);
2081     DUChainReleaser releaseTop(top);
2082     DUChainWriteLocker lock(DUChain::lock());
2083 
2084     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"FOO")).count(), 1);
2085     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"BAR")).count(), 1);
2086     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"FOO")).first()->context(), top);
2087     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"BAR")).first()->context(), top);
2088 
2089     QVERIFY(top->findDeclarations(QualifiedIdentifier(u"FOO")).first()->abstractType()->modifiers() & AbstractType::ConstModifier);
2090     QVERIFY(top->findDeclarations(QualifiedIdentifier(u"BAR")).first()->abstractType()->modifiers() & AbstractType::ConstModifier);
2091 }
2092 void TestDUChain::defaultFunctionParam()
2093 {
2094     //                 0         1         2         3         4         5         6         7
2095     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2096     QByteArray method("<? function foo($a, $b = false, $c = null) {} ");
2097 
2098     TopDUContext* top = parse(method, DumpNone);
2099     DUChainReleaser releaseTop(top);
2100     DUChainWriteLocker lock(DUChain::lock());
2101 
2102     AbstractFunctionDeclaration* fun = dynamic_cast<AbstractFunctionDeclaration*>(top->localDeclarations().first());
2103     QVERIFY(fun);
2104 
2105     QCOMPARE(fun->defaultParametersSize(), 3u);
2106     QVERIFY(fun->defaultParameters()[0].isEmpty());
2107     QCOMPARE(fun->defaultParameters()[1].str(), QString("false"));
2108     QCOMPARE(fun->defaultParameters()[2].str(), QString("null"));
2109 }
2110 
2111 void TestDUChain::defaultFunctionParamWithTypehint() {
2112     QByteArray method("<? function foo(array $i = array()) { } ");
2113 
2114     TopDUContext* top = parse(method, DumpAll);
2115     DUChainReleaser releaseTop(top);
2116 
2117     DUChainWriteLocker lock(DUChain::lock());
2118 
2119     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
2120     QVERIFY(fun);
2121     QCOMPARE(fun->arguments().count(), 1);
2122 
2123     auto argType = fun->arguments().first().dynamicCast<IntegralType>();
2124     QVERIFY(argType);
2125     QVERIFY(argType->dataType() == IntegralType::TypeArray);
2126 }
2127 
2128 void TestDUChain::nullDefaultFunctionParamWithTypehint()
2129 {
2130     //                 0         1         2         3         4         5         6         7
2131     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2132     QByteArray method("<? function foo(array $i = null) { } ");
2133 
2134     TopDUContext* top = parse(method, DumpAll);
2135     DUChainReleaser releaseTop(top);
2136 
2137     DUChainWriteLocker lock(DUChain::lock());
2138 
2139     FunctionType::Ptr fun = top->localDeclarations().first()->type<FunctionType>();
2140     QVERIFY(fun);
2141     QCOMPARE(fun->arguments().count(), 1);
2142 
2143     auto argType = fun->arguments().first().dynamicCast<UnsureType>();
2144     QVERIFY(argType);
2145     QCOMPARE(argType->typesSize(), 2u);
2146     QVERIFY(argType->types()[0].abstractType().dynamicCast<IntegralType>());
2147     QVERIFY(argType->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
2148     QVERIFY(argType->types()[1].abstractType().dynamicCast<IntegralType>());
2149     QVERIFY(argType->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeNull);
2150 }
2151 
2152 void TestDUChain::globalFunction()
2153 {
2154     //                 0         1         2         3         4         5         6         7
2155     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2156     QByteArray method("<? substr(); ");
2157 
2158     TopDUContext* top = parse(method, DumpAll);
2159     DUChainReleaser releaseTop(top);
2160     DUChainWriteLocker lock(DUChain::lock());
2161 
2162     QCOMPARE(top->importedParentContexts().count(), 1);
2163     QVERIFY(DUChain::self()->chainForDocument(internalFunctionFile()));
2164     QCOMPARE(DUChain::self()->chainForDocument(internalFunctionFile()), top->importedParentContexts().first().context(top));
2165 
2166     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"substr")).count(), 1);
2167 }
2168 
2169 void TestDUChain::globalVariableFromInternalFunctions()
2170 {
2171     //                 0         1         2         3         4         5         6         7
2172     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2173     QByteArray method("<? substr(); ");
2174 
2175     TopDUContext* top = parse(method, DumpAll);
2176     DUChainReleaser releaseTop(top);
2177     DUChainWriteLocker lock(DUChain::lock());
2178 
2179     QCOMPARE(top->importedParentContexts().count(), 1);
2180     QVERIFY(DUChain::self()->chainForDocument(internalFunctionFile()));
2181     QCOMPARE(DUChain::self()->chainForDocument(internalFunctionFile()), top->importedParentContexts().first().context(top));
2182 
2183     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"_GET")).count(), 1);
2184 }
2185 
2186 void TestDUChain::newObjectFromOtherFile()
2187 {
2188 
2189     TopDUContext* addTop = parseAdditionalFile(IndexedString("/duchaintest/foo.php"), "<?php class Foo { } ");
2190     DUChainReleaser releaseAddTop(addTop);
2191     //                 0         1         2         3         4         5         6         7
2192     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2193     QByteArray method("<? $a = new Foo(); ");
2194     TopDUContext* top = parse(method, DumpNone);
2195     DUChainReleaser releaseTop(top);
2196     DUChainWriteLocker lock(DUChain::lock());
2197 
2198     QVERIFY(hasImportedParentContext(top, addTop));
2199 
2200     QCOMPARE(top->localDeclarations().first()->type<StructureType>()->declaration(top),
2201              addTop->localDeclarations().first());
2202 }
2203 
2204 void TestDUChain::unknownReturnType()
2205 {
2206     //                 0         1         2         3         4         5         6         7
2207     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2208     QByteArray method("<? function foo() {} $a = foo();");
2209 
2210     TopDUContext* top = parse(method, DumpNone);
2211     DUChainReleaser releaseTop(top);
2212     DUChainWriteLocker lock(DUChain::lock());
2213 
2214     //function bar
2215     Declaration* dec = top->localDeclarations().at(0);
2216     FunctionType::Ptr fType = dec->type<FunctionType>();
2217     QVERIFY(fType);
2218     QVERIFY(fType->returnType().dynamicCast<IntegralType>());
2219     QVERIFY(fType->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeVoid);
2220 }
2221 
2222 void TestDUChain::staticFunctionCallFromOtherFile()
2223 {
2224 
2225     TopDUContext* addTop = parseAdditionalFile(IndexedString("/duchaintest/foo2.php"), "<?php class Foo { public static function a() {} } ");
2226     DUChainReleaser releaseAddTop(addTop);
2227     TopDUContext* top = parse("<? Foo::a(); ", DumpNone);
2228     DUChainReleaser releaseTop(top);
2229     DUChainWriteLocker lock(DUChain::lock());
2230     QVERIFY(hasImportedParentContext(top, addTop));
2231 }
2232 
2233 void TestDUChain::classConstantFromOtherFile()
2234 {
2235 
2236     TopDUContext* addTop = parseAdditionalFile(IndexedString("/duchaintest/foo2.php"), "<?php class Foo { const BAR = 0; } ");
2237     DUChainReleaser releaseAddTop(addTop);
2238     TopDUContext* top = parse("<? Foo::BAR; ", DumpNone);
2239     DUChainReleaser releaseTop(top);
2240     DUChainWriteLocker lock(DUChain::lock());
2241     QVERIFY(hasImportedParentContext(top, addTop));
2242 }
2243 
2244 void TestDUChain::globalFunctionCallFromOtherFile()
2245 {
2246 
2247     TopDUContext* addTop = parseAdditionalFile(IndexedString("/duchaintest/foo3.php"), "<?php function a() {} ");
2248     DUChainReleaser releaseAddTop(addTop);
2249     TopDUContext* top = parse("<? a(); ", DumpNone);
2250     DUChainReleaser releaseTop(top);
2251     DUChainWriteLocker lock(DUChain::lock());
2252     QVERIFY(hasImportedParentContext(top, addTop));
2253 }
2254 
2255 void TestDUChain::constantFromOtherFile()
2256 {
2257 
2258     TopDUContext* addTop = parseAdditionalFile(IndexedString("/duchaintest/foo3.php"), "<?php define('A', 0); ");
2259     DUChainReleaser releaseAddTop(addTop);
2260     TopDUContext* top = parse("<? define('B', 0); A; ", DumpNone);
2261     DUChainReleaser releaseTop(top);
2262     DUChainWriteLocker lock(DUChain::lock());
2263     QVERIFY(hasImportedParentContext(top, addTop));
2264 }
2265 
2266 void TestDUChain::singleton()
2267 {
2268     //                 0         1         2         3         4         5         6         7
2269     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2270     QByteArray method("<? class A { public static function self() { static $i; if(!$i) $i = new self(); return $i; }}");
2271 
2272     TopDUContext* top = parse(method, DumpAll);
2273     DUChainReleaser releaseTop(top);
2274     DUChainWriteLocker lock(DUChain::lock());
2275 
2276     FunctionType::Ptr fun = top->childContexts().first()->localDeclarations().first()->type<FunctionType>();
2277     QVERIFY(fun);
2278     auto ret = fun->returnType().dynamicCast<StructureType>();
2279     qDebug() << fun->returnType()->toString();
2280     QVERIFY(ret);
2281     QCOMPARE(ret->declaration(top), top->localDeclarations().first());
2282 }
2283 
2284 void TestDUChain::internalFunctions()
2285 {
2286     return; //disabled because it is too slow
2287     QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevphpsupport/phpfunctions.php"));
2288     QFile file(fileName);
2289     file.open(QIODevice::ReadOnly | QIODevice::Text);
2290     TopDUContext* top = parse(file.readAll(), DumpNone);
2291     DUChainReleaser releaseTop(top);
2292     DUChainWriteLocker lock(DUChain::lock());
2293 }
2294 
2295 void TestDUChain::trueFalse()
2296 {
2297     //                 0         1         2         3         4         5         6         7
2298     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2299     QByteArray method("<? $a = true; $b = false; ");
2300 
2301     TopDUContext* top = parse(method, DumpAll);
2302     DUChainReleaser releaseTop(top);
2303     DUChainWriteLocker lock(DUChain::lock());
2304 
2305     QVERIFY(top->localDeclarations().at(0)->type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
2306     QVERIFY(top->localDeclarations().at(1)->type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
2307 }
2308 
2309 void TestDUChain::null()
2310 {
2311     QByteArray method("<? $a = null; ");
2312 
2313     TopDUContext* top = parse(method, DumpAll);
2314     DUChainReleaser releaseTop(top);
2315     DUChainWriteLocker lock(DUChain::lock());
2316 
2317     QVERIFY(top->localDeclarations().at(0)->type<IntegralType>()->dataType() == IntegralType::TypeNull);
2318 }
2319 
2320 void TestDUChain::array()
2321 {
2322     QByteArray method("<? $a = array(); $b = array(1, 2, 3); $b[] = 'test';");
2323 
2324     TopDUContext* top = parse(method, DumpAll);
2325     DUChainReleaser releaseTop(top);
2326     DUChainWriteLocker lock(DUChain::lock());
2327 
2328     QVERIFY(top->localDeclarations().at(0)->type<IntegralType>()->dataType() == IntegralType::TypeArray);
2329     QVERIFY(top->localDeclarations().at(1)->type<IntegralType>()->dataType() == IntegralType::TypeArray);
2330     // $b[] = 'test'; is not a redeclaration of b! Esp. it's type should not change.
2331     QCOMPARE(top->findDeclarations(Identifier(u"b")).count(), 1);
2332 }
2333 
2334 void TestDUChain::functionDocBlock()
2335 {
2336     {
2337         TopDUContext* top = parse("<? /**\n *Foo\n **/\nfunction foo() {} ", DumpNone);
2338         DUChainReleaser releaseTop(top);
2339         DUChainWriteLocker lock(DUChain::lock());
2340         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo"));
2341     }
2342 
2343     {
2344         TopDUContext* top = parse("<? /**\n *Bar\n **/\nclass A { /**\n *Foo\n **/\nfunction foo() {} }", DumpNone);
2345         DUChainReleaser releaseTop(top);
2346         DUChainWriteLocker lock(DUChain::lock());
2347         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Bar"));
2348         QCOMPARE(top->childContexts().first()->localDeclarations().first()->comment(), QByteArray("Foo"));
2349     }
2350 
2351     {
2352         TopDUContext* top = parse("<? /**\n *Foo\n **/\ninterface A { }", DumpNone);
2353         DUChainReleaser releaseTop(top);
2354         DUChainWriteLocker lock(DUChain::lock());
2355         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo"));
2356     }
2357 
2358     {
2359         TopDUContext* top = parse("<? class A { /**\n *Foo\n **/\npublic $foo; }", DumpNone);
2360         DUChainReleaser releaseTop(top);
2361         DUChainWriteLocker lock(DUChain::lock());
2362         QCOMPARE(top->childContexts().first()->localDeclarations().first()->comment(), QByteArray("Foo"));
2363     }
2364 
2365     {
2366         TopDUContext* top = parse("<? class A { /**\n *Foo\n **/\nconst FOO=0; }", DumpNone);
2367         DUChainReleaser releaseTop(top);
2368         DUChainWriteLocker lock(DUChain::lock());
2369         QCOMPARE(top->childContexts().first()->localDeclarations().first()->comment(), QByteArray("Foo"));
2370     }
2371 
2372     {
2373         TopDUContext* top = parse("<?\n/// Foo\n/// Bar\nfunction foo() {} ", DumpNone);
2374         DUChainReleaser releaseTop(top);
2375         DUChainWriteLocker lock(DUChain::lock());
2376         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo\n Bar"));
2377     }
2378 
2379     {
2380         // same as above but with indentation
2381         TopDUContext* top = parse("<?\n  /// Foo\n  /// Bar\n  function foo() {} ", DumpNone);
2382         DUChainReleaser releaseTop(top);
2383         DUChainWriteLocker lock(DUChain::lock());
2384         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo\n Bar"));
2385     }
2386 }
2387 
2388 void TestDUChain::variableDocBlock()
2389 {
2390     {
2391         TopDUContext* top = parse("<? /**\n *Foo\n **/\n$a = 0; /**\n *Foo\n **/\nstatic $b;", DumpAll);
2392         DUChainReleaser releaseTop(top);
2393         DUChainWriteLocker lock(DUChain::lock());
2394         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo"));
2395         QCOMPARE(top->localDeclarations().at(1)->comment(), QByteArray("Foo"));
2396     }
2397     {
2398         TopDUContext* top = parse("<? /// Foo\n$a = 0; /// Foo\nstatic $b;", DumpAll);
2399         DUChainReleaser releaseTop(top);
2400         DUChainWriteLocker lock(DUChain::lock());
2401         QCOMPARE(top->localDeclarations().first()->comment(), QByteArray("Foo"));
2402         QCOMPARE(top->localDeclarations().at(1)->comment(), QByteArray("Foo"));
2403     }
2404 }
2405 
2406 void TestDUChain::functionDocBlockParams()
2407 {
2408     TopDUContext* top = parse("<? class A {} /**\n * @param\tint\n *\t@param A\n *  @param   mixed **/\nfunction foo($a, $b, $c, $d) {} ", DumpNone);
2409     {
2410         DUChainReleaser releaseTop(top);
2411         DUChainWriteLocker lock(DUChain::lock());
2412 
2413         QCOMPARE(top->localDeclarations().at(1)->type<FunctionType>()->arguments().count(), 4);
2414 
2415         AbstractType::Ptr arg = top->localDeclarations().at(1)->type<FunctionType>()->arguments().at(0);
2416         QVERIFY(arg.dynamicCast<IntegralType>());
2417         QVERIFY(arg.staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
2418         QVERIFY(top->childContexts().at(1)->localDeclarations().at(0)->type<IntegralType>());
2419         QVERIFY(top->childContexts().at(1)->localDeclarations().at(0)->type<IntegralType>()->dataType() == IntegralType::TypeInt);
2420 
2421         arg = top->localDeclarations().at(1)->type<FunctionType>()->arguments().at(1);
2422         QVERIFY(arg.dynamicCast<StructureType>());
2423         QCOMPARE(arg.staticCast<StructureType>()->declaration(top), top->localDeclarations().at(0));
2424         QCOMPARE(top->childContexts().at(1)->localDeclarations().at(1)->type<StructureType>()->declaration(top), top->localDeclarations().at(0));
2425 
2426         arg = top->localDeclarations().at(1)->type<FunctionType>()->arguments().at(2);
2427         QVERIFY(arg.dynamicCast<IntegralType>());
2428         QVERIFY(arg.staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
2429 
2430         arg = top->localDeclarations().at(1)->type<FunctionType>()->arguments().at(3);
2431         QVERIFY(arg.dynamicCast<IntegralType>());
2432         QVERIFY(arg.staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed);
2433     }
2434 }
2435 
2436 void TestDUChain::memberFunctionDocBlockParams()
2437 {
2438     TopDUContext* top = parse("<? class A { /**\n * @param bool\n * @param A\n * @param array\n **/\nfunction foo($a, $b, $c) {} }", DumpNone);
2439     {
2440         DUChainReleaser releaseTop(top);
2441         DUChainWriteLocker lock(DUChain::lock());
2442 
2443         QCOMPARE(top->childContexts().first()->localDeclarations().first()->type<FunctionType>()->arguments().count(), 3);
2444 
2445         AbstractType::Ptr arg = top->childContexts().first()->localDeclarations().first()->type<FunctionType>()->arguments().at(0);
2446         QVERIFY(arg.dynamicCast<IntegralType>());
2447         QVERIFY(arg.staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean);
2448 
2449         arg = top->childContexts().first()->localDeclarations().first()->type<FunctionType>()->arguments().at(1);
2450         QVERIFY(arg.dynamicCast<StructureType>());
2451         QCOMPARE(arg.staticCast<StructureType>()->declaration(top), top->localDeclarations().at(0));
2452 
2453         arg = top->childContexts().first()->localDeclarations().first()->type<FunctionType>()->arguments().at(2);
2454         QVERIFY(arg.dynamicCast<IntegralType>());
2455         QVERIFY(arg.staticCast<IntegralType>()->dataType() == IntegralType::TypeArray);
2456     }
2457 }
2458 
2459 void TestDUChain::foreachLoop()
2460 {
2461     {
2462     TopDUContext* top = parse("<? $a = array(1); foreach($a as $k=>$i) { $i; }", DumpAll);
2463     DUChainReleaser releaseTop(top);
2464     DUChainWriteLocker lock(DUChain::lock());
2465 
2466     QCOMPARE(top->localDeclarations().count(), 3);
2467     QCOMPARE(top->localDeclarations().at(1)->qualifiedIdentifier(), QualifiedIdentifier(u"k"));
2468     QVERIFY(top->localDeclarations().at(1)->abstractType().dynamicCast<IntegralType>());
2469     QCOMPARE(top->localDeclarations().at(1)->abstractType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed));
2470     QCOMPARE(top->localDeclarations().at(2)->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
2471     QVERIFY(top->localDeclarations().at(2)->abstractType().dynamicCast<IntegralType>());
2472     QCOMPARE(top->localDeclarations().at(2)->abstractType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed));
2473     }
2474     {
2475     // bug: https://bugs.kde.org/show_bug.cgi?id=237110
2476     TopDUContext* top = parse("<? $a = array(1); foreach($a as $b) { $c = new stdclass; }", DumpAll);
2477     DUChainReleaser releaseTop(top);
2478     DUChainWriteLocker lock(DUChain::lock());
2479 
2480     QCOMPARE(top->localDeclarations().count(), 3);
2481     QCOMPARE(top->localDeclarations().at(1)->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
2482     qDebug() << top->localDeclarations().at(1)->toString();
2483     QVERIFY(top->localDeclarations().at(1)->abstractType().dynamicCast<IntegralType>());
2484     QCOMPARE(top->localDeclarations().at(1)->abstractType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed));
2485     QCOMPARE(top->localDeclarations().at(2)->qualifiedIdentifier(), QualifiedIdentifier(u"c"));
2486     QVERIFY(top->localDeclarations().at(2)->abstractType().dynamicCast<StructureType>());
2487     QCOMPARE(top->localDeclarations().at(2)->abstractType().staticCast<StructureType>()->qualifiedIdentifier().toString(), QString("stdclass"));
2488     }
2489 }
2490 
2491 void TestDUChain::php4StyleConstructor()
2492 {
2493     //                 0         1         2         3         4         5         6         7
2494     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2495     QByteArray method("<? class Aa { function Aa() { $this->bb(); } } ");
2496     TopDUContext* top = parse(method, DumpAll);
2497     DUChainReleaser releaseTop(top);
2498     DUChainWriteLocker lock(DUChain::lock());
2499 
2500     Declaration* dec = top->childContexts().first()->localDeclarations().at(0);
2501     QVERIFY(dec);
2502     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"aa::aa"));
2503     ClassFunctionDeclaration* classFuncDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
2504     QVERIFY(classFuncDec);
2505     QVERIFY(classFuncDec->isConstructor());
2506 }
2507 
2508 void TestDUChain::constructor()
2509 {
2510     //                 0         1         2         3         4         5         6         7
2511     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2512     {
2513         QByteArray method("<? class foobar { function __construct() {} } ");
2514         TopDUContext* top = parse(method, DumpNone);
2515         DUChainReleaser releaseTop(top);
2516         DUChainWriteLocker lock(DUChain::lock());
2517 
2518         Declaration* dec = top->childContexts().first()->localDeclarations().at(0);
2519         QVERIFY(dec);
2520         ClassFunctionDeclaration* classFuncDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
2521         QVERIFY(classFuncDec);
2522         QVERIFY(!classFuncDec->isDestructor());
2523         QVERIFY(classFuncDec->isConstructor());
2524     }
2525     {
2526         QByteArray method("<? class foobar { function foobar() {} } ");
2527         TopDUContext* top = parse(method, DumpNone);
2528         DUChainReleaser releaseTop(top);
2529         DUChainWriteLocker lock(DUChain::lock());
2530 
2531         Declaration* dec = top->childContexts().first()->localDeclarations().at(0);
2532         QVERIFY(dec);
2533         ClassFunctionDeclaration* classFuncDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
2534         QVERIFY(classFuncDec);
2535         QVERIFY(!classFuncDec->isDestructor());
2536         QVERIFY(classFuncDec->isConstructor());
2537     }
2538 }
2539 
2540 void TestDUChain::destructor()
2541 {
2542     //                 0         1         2         3         4         5         6         7
2543     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2544     QByteArray method("<? class foobar { function __destruct() {} } ");
2545     TopDUContext* top = parse(method, DumpNone);
2546     DUChainReleaser releaseTop(top);
2547     DUChainWriteLocker lock(DUChain::lock());
2548 
2549     Declaration* dec = top->childContexts().first()->localDeclarations().at(0);
2550     QVERIFY(dec);
2551     ClassFunctionDeclaration* classFuncDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
2552     QVERIFY(classFuncDec);
2553     QVERIFY(classFuncDec->isDestructor());
2554     QVERIFY(!classFuncDec->isConstructor());
2555 }
2556 
2557 void TestDUChain::functionInFunction()
2558 {
2559     //                 0         1         2         3         4         5         6         7
2560     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2561     QByteArray method("<? function aaa() { function bbb() { } }");
2562     TopDUContext* top = parse(method, DumpAll);
2563     DUChainReleaser releaseTop(top);
2564     DUChainWriteLocker lock(DUChain::lock());
2565 
2566     QCOMPARE(top->localDeclarations().at(0)->qualifiedIdentifier(), QualifiedIdentifier(u"aaa"));
2567 }
2568 
2569 void TestDUChain::objectWithClassName()
2570 {
2571     //                 0         1         2         3         4         5         6         7
2572     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2573     QByteArray method("<? class setupPage {} $setupPage = new setupPage; $setupPage->foo();");
2574     TopDUContext* top = parse(method, DumpNone, QUrl(QStringLiteral("file:///internal/testObjectWithClassName.php")));
2575     DUChainReleaser releaseTop(top);
2576 
2577     // update top (the pointer will be the same)
2578     QByteArray method2("<? $setupPage = new setupPage; $setupPage->foo();");
2579     TopDUContext* top2 = parse(method2, DumpNone, QUrl(QStringLiteral("file:///internal/testObjectWithClassName.php")));
2580     QVERIFY(top2 == top);
2581 }
2582 
2583 void TestDUChain::largeNumberOfDeclarations()
2584 {
2585     TopDUContext* top = new TopDUContext(IndexedString(QUrl(QStringLiteral("file:///internal/testurl"))), RangeInRevision(0, 0, 6000, 0), nullptr);
2586     DUChain::self()->addDocumentChain(top);
2587     DUChainReleaser releaseTop(top);
2588     DUChainWriteLocker lock(DUChain::lock());
2589     for (int i = 0; i < 6000; ++i) {
2590         RangeInRevision newRange(i, 0, i, 1);
2591         auto* dec = new Declaration(newRange, top);
2592         dec->setIdentifier(Identifier(QStringLiteral("dec%0").arg(i)));
2593         dec->setAbstractType(AbstractType::Ptr(nullptr));
2594     }
2595 }
2596 
2597 void TestDUChain::staticVariable()
2598 {
2599     //                 0         1         2         3         4         5         6         7
2600     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2601     QByteArray method("<? define('AA', 0); function aaa() { static $foo; static $bar=0; static $baz='a'; static $bam=array(); static $woid=+'1'; static $a=AA; }");
2602     TopDUContext* top = parse(method, DumpAll);
2603     DUChainReleaser releaseTop(top);
2604     DUChainWriteLocker lock(DUChain::lock());
2605 
2606     QCOMPARE(top->childContexts().at(1)->localDeclarations().count(), 6);
2607 
2608     QCOMPARE(top->childContexts().at(1)->localDeclarations().first()->qualifiedIdentifier(), QualifiedIdentifier(u"aaa::foo"));
2609     QVERIFY(top->childContexts().at(1)->localDeclarations().first()->type<IntegralType>());
2610     QCOMPARE(top->childContexts().at(1)->localDeclarations().first()->type<IntegralType>()->dataType(), (uint)IntegralType::TypeMixed);
2611 
2612     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(1)->qualifiedIdentifier(), QualifiedIdentifier(u"aaa::bar"));
2613     QVERIFY(top->childContexts().at(1)->localDeclarations().at(1)->type<IntegralType>());
2614     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(1)->type<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
2615 
2616     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(2)->qualifiedIdentifier(), QualifiedIdentifier(u"aaa::baz"));
2617     QVERIFY(top->childContexts().at(1)->localDeclarations().at(2)->type<IntegralType>());
2618     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(2)->type<IntegralType>()->dataType(), (uint)IntegralType::TypeString);
2619 
2620     QVERIFY(top->childContexts().at(1)->localDeclarations().at(3)->type<IntegralType>());
2621     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(3)->type<IntegralType>()->dataType(), (uint)IntegralType::TypeArray);
2622 
2623     QVERIFY(top->childContexts().at(1)->localDeclarations().at(4)->type<IntegralType>());
2624     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(4)->type<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
2625 
2626     QVERIFY(top->childContexts().at(1)->localDeclarations().at(5)->type<IntegralType>());
2627     QCOMPARE(top->childContexts().at(1)->localDeclarations().at(5)->type<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
2628 }
2629 
2630 void TestDUChain::returnTypeTwoDeclarations()
2631 {
2632     //                 0         1         2         3         4         5         6         7
2633     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2634     QByteArray method("<? function foo() { $i='a'; $i=0; return $i; } ");
2635 
2636     TopDUContext* top = parse(method, DumpAll);
2637     DUChainReleaser releaseTop(top);
2638     DUChainWriteLocker lock(DUChain::lock());
2639 
2640     Declaration* dec = top->localDeclarations().at(0);
2641     FunctionType::Ptr functionType = dec->type<FunctionType>();
2642     QVERIFY(functionType);
2643     auto retType = functionType->returnType().dynamicCast<UnsureType>();
2644     QVERIFY(retType);
2645     QCOMPARE(retType->typesSize(), 2u);
2646     QVERIFY(retType->types()[0].abstractType().dynamicCast<IntegralType>());
2647     QCOMPARE(retType->types()[0].abstractType().staticCast<IntegralType>()->dataType(), (uint)IntegralType::TypeString);
2648     QVERIFY(retType->types()[1].abstractType().dynamicCast<IntegralType>());
2649     QCOMPARE(retType->types()[1].abstractType().staticCast<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
2650 }
2651 
2652 void TestDUChain::globalVariableNotVisibleInFunction()
2653 {
2654     //                 0         1         2         3         4         5         6         7
2655     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2656     QByteArray method("<? $a = 0; function foo() { $a; }");
2657 
2658     TopDUContext* top = parse(method, DumpAll);
2659     DUChainReleaser releaseTop(top);
2660     DUChainWriteLocker lock(DUChain::lock());
2661 
2662     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a")).first()->uses().count(), 0);
2663 }
2664 
2665 void TestDUChain::globalVariableInFunction()
2666 {
2667     //                 0         1         2         3         4         5         6         7
2668     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2669     QByteArray method("<? $a = 0; function foo() { global $a; $a; }");
2670 
2671     TopDUContext* top = parse(method, DumpAll);
2672     DUChainReleaser releaseTop(top);
2673     DUChainWriteLocker lock(DUChain::lock());
2674 
2675     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a")).first()->uses().count(), 1);
2676 }
2677 
2678 void TestDUChain::nonGlobalVariableInFunction()
2679 {
2680     // bug: https://bugs.kde.org/show_bug.cgi?id=240920
2681 
2682     //                 0         1         2         3         4         5         6         7
2683     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2684     QByteArray method("<? $a = 0; function foo() { $a = 1; }");
2685 
2686     TopDUContext* top = parse(method, DumpNone);
2687     DUChainReleaser releaseTop(top);
2688     DUChainWriteLocker lock;
2689 
2690     QCOMPARE(top->findLocalDeclarations(Identifier(u"a")).count(), 1);
2691     QCOMPARE(top->findLocalDeclarations(Identifier(u"a")).first()->uses().count(), 0);
2692 
2693     QCOMPARE(top->childContexts().count(), 2);
2694     QCOMPARE(top->childContexts().last()->findLocalDeclarations(Identifier(u"a")).count(), 1);
2695     QCOMPARE(top->childContexts().last()->findLocalDeclarations(Identifier(u"a")).first()->uses().count(), 0);
2696 }
2697 
2698 void TestDUChain::superglobalInFunction()
2699 {
2700     //                 0         1         2         3         4         5         6         7
2701     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2702     QByteArray method("<? $_GET; function foo() { $_GET; }");
2703 
2704     TopDUContext* top = parse(method, DumpAll);
2705     DUChainReleaser releaseTop(top);
2706     DUChainWriteLocker lock(DUChain::lock());
2707 
2708     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"_GET")).count(), 1);
2709     Declaration* dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("_GET"))).first();
2710     QVERIFY(dynamic_cast<VariableDeclaration*>(dec));
2711     QVERIFY(static_cast<VariableDeclaration*>(dec)->isSuperglobal());
2712     QCOMPARE(dec->uses().keys().count(), 1);
2713     QCOMPARE(dec->uses().values().count(), 1);
2714     QCOMPARE(dec->uses().values().first().count(), 2);
2715     QCOMPARE(dec->uses().values().first().first(), RangeInRevision(0, 3, 0, 8));
2716     QCOMPARE(dec->uses().values().first().at(1), RangeInRevision(0, 27, 0, 32));
2717 }
2718 
2719 void TestDUChain::returnWithoutFunction()
2720 {
2721     //yes, this is possible in php, you then have $a as return value of an include call
2722     QByteArray method("<? $a = 0; return $a; ");
2723 
2724     TopDUContext* top = parse(method, DumpAll);
2725     DUChainReleaser releaseTop(top);
2726 }
2727 
2728 void TestDUChain::circularInheritance()
2729 {
2730     //circular inheritance is not allowed in PHP and should not crash kdevelop
2731     QByteArray method("<? class a extends b {} class b extends c {} class c extends a {}");
2732 
2733     TopDUContext* top = parse(method, DumpNone);
2734     DUChainReleaser releaseTop(top);
2735 
2736     DUChainWriteLocker lock(DUChain::lock());
2737 
2738     QVERIFY(top->localDeclarations().at(2)->internalContext()->importedParentContexts().empty());
2739     QCOMPARE(top->localDeclarations().at(1)->internalContext()->importedParentContexts().count(), 1);
2740     QCOMPARE(top->localDeclarations().at(1)->internalContext()->importedParentContexts().first().context(top),
2741              top->localDeclarations().at(2)->internalContext());
2742     QCOMPARE(top->localDeclarations().at(0)->internalContext()->importedParentContexts().count(), 1);
2743     QCOMPARE(top->localDeclarations().at(0)->internalContext()->importedParentContexts().first().context(top),
2744              top->localDeclarations().at(1)->internalContext());
2745 }
2746 
2747 void TestDUChain::circularInterface()
2748 {
2749     QByteArray method("<? interface a {} class b implements a {} class c extends b implements a {}");
2750 
2751     TopDUContext* top = parse(method, DumpNone);
2752     DUChainReleaser releaseTop(top);
2753 
2754     DUChainWriteLocker lock(DUChain::lock());
2755 
2756     QCOMPARE(top->problems().count(), 0);
2757 
2758     QVERIFY(top->localDeclarations().at(0)->internalContext()->importedParentContexts().empty());
2759     QCOMPARE(top->localDeclarations().at(1)->internalContext()->importedParentContexts().count(), 1);
2760     QCOMPARE(top->localDeclarations().at(1)->internalContext()->importedParentContexts().first().context(top),
2761              top->localDeclarations().at(0)->internalContext());
2762     QCOMPARE(top->localDeclarations().at(2)->internalContext()->importedParentContexts().count(), 1);
2763     QCOMPARE(top->localDeclarations().at(2)->internalContext()->importedParentContexts().first().context(top),
2764              top->localDeclarations().at(1)->internalContext());
2765 }
2766 
2767 void TestDUChain::findDeclarations()
2768 {
2769     DUChainWriteLocker lock(DUChain::lock());
2770 
2771     TopDUContext* top1 = new TopDUContext(IndexedString(QUrl(QStringLiteral("file:///internal/testfile1"))), RangeInRevision(0, 0, 0, 10), nullptr);
2772     DUChainReleaser releaseTop1(top1);
2773     DUChain::self()->addDocumentChain(top1);
2774     TopDUContext* top2 = new TopDUContext(IndexedString(QUrl(QStringLiteral("file:///internal/testfile2"))), RangeInRevision(0, 0, 0, 10), nullptr);
2775     DUChainReleaser releaseTop2(top2);
2776     DUChain::self()->addDocumentChain(top2);
2777 
2778     Declaration* declaration = new Declaration(RangeInRevision(0, 0, 0, 3), top1);
2779     declaration->setIdentifier(Identifier(QStringLiteral("foo")));
2780 
2781     QVERIFY(!top1->usingImportsCache());
2782     QVERIFY(!top2->usingImportsCache());
2783 
2784     QCOMPARE(1, top1->findDeclarations(Identifier(u"foo")).count());
2785     QCOMPARE(0, top2->findDeclarations(Identifier(u"foo")).count());
2786     top2->addImportedParentContext(top1);
2787 
2788     QVERIFY(!top1->usingImportsCache());
2789     QVERIFY(!top2->usingImportsCache());
2790 
2791     QCOMPARE(1, top2->findDeclarations(Identifier(u"foo")).count());
2792     top2->clearImportedParentContexts();
2793     QCOMPARE(top2->importedParentContexts().size(), 0);
2794 
2795     QVERIFY(!top1->usingImportsCache());
2796     QVERIFY(!top2->usingImportsCache());
2797 
2798     QCOMPARE(0, top2->findDeclarations(Identifier(u"foo")).count());
2799     top2->addImportedParentContext(top1);
2800     QCOMPARE(1, top2->findDeclarations(Identifier(u"foo")).count());
2801 }
2802 
2803 void TestDUChain::memberTypeAfterMethod()
2804 {
2805     //                 0         1         2         3         4         5         6         7
2806     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2807     QByteArray method("<? class A { function foo(){} public $bar; }");
2808 
2809     TopDUContext* top = parse(method, DumpNone);
2810     DUChainReleaser releaseTop(top);
2811     DUChainWriteLocker lock(DUChain::lock());
2812 
2813     DUContext* contextClassA = top->childContexts().first();
2814 
2815     // function foo
2816     {
2817         ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().first());
2818         QVERIFY(var);
2819         QCOMPARE(var->identifier(), Identifier(u"foo"));
2820         QCOMPARE(var->accessPolicy(), Declaration::Public);
2821         QCOMPARE(var->isStatic(), false);
2822         QVERIFY(var->type<FunctionType>());
2823         auto ret = var->type<FunctionType>()->returnType().dynamicCast<IntegralType>();
2824         QVERIFY(ret);
2825         QVERIFY(ret->dataType() == IntegralType::TypeVoid);
2826     }
2827 
2828     // public $bar
2829     {
2830         ClassMemberDeclaration* var = dynamic_cast<ClassMemberDeclaration*>(contextClassA->localDeclarations().at(1));
2831         QVERIFY(var);
2832         QCOMPARE(var->identifier(), Identifier(u"bar"));
2833         QCOMPARE(var->accessPolicy(), Declaration::Public);
2834         QCOMPARE(var->isStatic(), false);
2835         QVERIFY(var->type<IntegralType>());
2836         QVERIFY(var->type<IntegralType>()->dataType() == IntegralType::TypeNull);
2837     }
2838 }
2839 
2840 
2841 void TestDUChain::catchDeclaration()
2842 {
2843     //                 0         1         2         3         4         5         6         7
2844     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2845     QByteArray method("<? try {} catch (Exception $e) {}");
2846 
2847     TopDUContext* top = parse(method, DumpAll);
2848     DUChainReleaser releaseTop(top);
2849     DUChainWriteLocker lock(DUChain::lock());
2850 
2851     VariableDeclaration* ex = dynamic_cast<VariableDeclaration*>(top->localDeclarations().first());
2852     QVERIFY(ex);
2853     QCOMPARE(ex->identifier(), Identifier(u"e"));
2854     QVERIFY(ex->type<StructureType>());
2855     QCOMPARE(QualifiedIdentifier(u"exception"), ex->type<StructureType>()->declaration(top)->qualifiedIdentifier());
2856 }
2857 
2858 void TestDUChain::multiCatchDeclaration()
2859 {
2860     //                 0         1         2         3         4         5         6         7
2861     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2862     QByteArray method("<? class ExceptionA {}; class ExceptionB {}; try {} catch (ExceptionA | ExceptionB $e) {}");
2863 
2864     TopDUContext* top = parse(method, DumpAll);
2865     DUChainReleaser releaseTop(top);
2866     DUChainWriteLocker lock(DUChain::lock());
2867 
2868     QCOMPARE(top->problems().count(), 0);
2869 
2870     VariableDeclaration* ex = dynamic_cast<VariableDeclaration*>(top->localDeclarations().at(2));
2871     QVERIFY(ex);
2872     QCOMPARE(ex->identifier(), Identifier(u"e"));
2873     QVERIFY(ex->type<UnsureType>());
2874     TypePtr<UnsureType> ut = ex->type<UnsureType>();
2875     QVERIFY(ut);
2876     QCOMPARE((uint)2, ut->typesSize());
2877     QVERIFY(ut->types()[0].type<StructureType>());
2878     QCOMPARE(QualifiedIdentifier(u"exceptiona"), ut->types()[0].type<StructureType>()->declaration(top)->qualifiedIdentifier());
2879     QVERIFY(ut->types()[1].type<StructureType>());
2880     QCOMPARE(QualifiedIdentifier(u"exceptionb"), ut->types()[1].type<StructureType>()->declaration(top)->qualifiedIdentifier());
2881 }
2882 
2883 void TestDUChain::resourceType()
2884 {
2885     //                 0         1         2         3         4         5         6         7
2886     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2887     QByteArray method("<? /**\n * @return resource\n**/\nfunction test() {}");
2888 
2889     TopDUContext* top = parse(method, DumpAll);
2890     DUChainReleaser releaseTop(top);
2891     DUChainWriteLocker lock(DUChain::lock());
2892 
2893     FunctionDeclaration* fun = dynamic_cast<FunctionDeclaration*>(top->localDeclarations().first());
2894     QVERIFY(fun);
2895     auto ftype = fun->abstractType().dynamicCast<FunctionType>();
2896     QVERIFY(ftype);
2897     auto rtype = ftype->returnType().dynamicCast<IntegralType>();
2898     QVERIFY(rtype);
2899     QCOMPARE(rtype->toString(), QString("resource"));
2900     QVERIFY(rtype->dataType() == IntegralTypeExtended::TypeResource);
2901 }
2902 
2903 void TestDUChain::foreachIterator()
2904 {
2905     QByteArray code;
2906     code.append("<? class B {} class A implements Iterator {");
2907     code.append("public function rewind() {} ");
2908     code.append("/**\n * @return B\n */public function current() {} ");
2909     code.append("public function key() {} ");
2910     code.append("public function next() {} ");
2911     code.append("public function valid() {} ");
2912     code.append("} ");
2913     code.append("$a = new A();");
2914     code.append("foreach($a as $i) { $i; }");
2915     TopDUContext* top = parse(code, DumpAST);
2916     DUChainReleaser releaseTop(top);
2917     DUChainWriteLocker lock(DUChain::lock());
2918 
2919     Declaration* iDec = top->localDeclarations().at(3);
2920     QCOMPARE(iDec->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
2921     QVERIFY(iDec->type<StructureType>());
2922     QCOMPARE(iDec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
2923     QVERIFY(top->localDeclarations().first() == iDec->type<StructureType>()->declaration(top));
2924 }
2925 
2926 void TestDUChain::foreachIterator2()
2927 {
2928     QByteArray code;
2929     code.append("<? class B {} class A implements Iterator {");
2930     code.append("public function rewind() {} ");
2931     code.append("/**\n * @return B\n */public function current() {} ");
2932     code.append("public function key() {} ");
2933     code.append("public function next() {} ");
2934     code.append("public function valid() {} ");
2935     code.append("} ");
2936     code.append("foreach(new A() as $i) { $i; }");
2937     TopDUContext* top = parse(code, DumpAST);
2938     DUChainReleaser releaseTop(top);
2939     DUChainWriteLocker lock(DUChain::lock());
2940 
2941     QCOMPARE(top->localDeclarations().size(), 3);
2942     Declaration* iDec = top->localDeclarations().at(2);
2943     QCOMPARE(iDec->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
2944     qDebug() << iDec->abstractType()->toString();
2945     QVERIFY(iDec->type<StructureType>());
2946     QCOMPARE(iDec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
2947     QVERIFY(top->localDeclarations().first() == iDec->type<StructureType>()->declaration(top));
2948 }
2949 
2950 void TestDUChain::foreachIterator3()
2951 {
2952     QByteArray code;
2953     code.append("<? class B {} class A implements Iterator {");
2954     code.append("public function rewind() {} ");
2955     code.append("/**\n * @return B\n */public function current() {} ");
2956     code.append("public function key() {} ");
2957     code.append("public function next() {} ");
2958     code.append("public function valid() {} ");
2959     code.append("} ");
2960     code.append("class C extends A { }");
2961     code.append("foreach(new C() as $i) { $i; }");
2962     TopDUContext* top = parse(code, DumpAST);
2963     DUChainReleaser releaseTop(top);
2964     DUChainWriteLocker lock(DUChain::lock());
2965 
2966     Declaration* iDec = top->localDeclarations().at(3);
2967     QCOMPARE(iDec->qualifiedIdentifier(), QualifiedIdentifier(u"i"));
2968     QVERIFY(iDec->type<StructureType>());
2969     QCOMPARE(iDec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"b"));
2970     QVERIFY(top->localDeclarations().first() == iDec->type<StructureType>()->declaration(top));
2971 }
2972 
2973 void TestDUChain::foreachIterator4()
2974 {
2975     // see also: https://bugs.kde.org/show_bug.cgi?id=276603
2976     QByteArray code = "<?\n"
2977                       "class A {\n"
2978                       "  public static $s;\n"
2979                       "  function foo() {\n"
2980                       "    foreach(array(1,2) as $this->i){}\n"
2981                       "    foreach(array(1,2) as $this->k => $this->v){}\n"
2982                       "    foreach(array(1,2) as A::$s){}\n"
2983                       "  }\n"
2984                       "}\n";
2985 
2986     TopDUContext* top = parse(code, DumpNone);
2987     DUChainReleaser releaseTop(top);
2988     DUChainWriteLocker lock;
2989     QVERIFY(top->problems().isEmpty());
2990 
2991     Declaration* aDec = top->localDeclarations().first();
2992 
2993     DUContext* fooCtx = top->childContexts().first()->childContexts().last();
2994     QVERIFY(fooCtx->owner());
2995 
2996     QCOMPARE(aDec->uses().size(), 1);
2997     QCOMPARE(aDec->uses().begin()->size(), 4);
2998 }
2999 
3000 void TestDUChain::foreachArray()
3001 {
3002     {
3003     QByteArray code = "<?\n"
3004                       "class Foo {};\n"
3005                       "/// @param Foo[]\n"
3006                       "function bar($a) {\n"
3007                       "    foreach($a as $e){ $e; }\n"
3008                       "}\n";
3009     TopDUContext* top = parse(code, DumpNone);
3010     DUChainReleaser releaseTop(top);
3011     DUChainWriteLocker lock(DUChain::lock());
3012 
3013     DUContext* barContext = top->childContexts().last();
3014     QCOMPARE(barContext->localScopeIdentifier(), QualifiedIdentifier(u"bar"));
3015 
3016     Declaration* eDec = barContext->localDeclarations().first();
3017     QCOMPARE(eDec->qualifiedIdentifier(), QualifiedIdentifier(u"bar::e"));
3018     QVERIFY(eDec->type<StructureType>());
3019     QCOMPARE(eDec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
3020     }
3021 }
3022 
3023 void TestDUChain::returnThis()
3024 {
3025     QByteArray code("<? class A { \n/**\n * @return $this\n */\npublic function x() {} } ");
3026     TopDUContext* top = parse(code, DumpAST);
3027     DUChainReleaser releaseTop(top);
3028     DUChainWriteLocker lock(DUChain::lock());
3029 
3030     Declaration* dec = top->childContexts().first()->localDeclarations().first();
3031     QVERIFY(dec->type<FunctionType>());
3032     AbstractType::Ptr t = dec->type<FunctionType>()->returnType();
3033     qDebug() << t->toString();
3034     QVERIFY(t.dynamicCast<StructureType>());
3035     QVERIFY(t.staticCast<StructureType>()->declaration(top) == top->localDeclarations().first());
3036 }
3037 
3038 void TestDUChain::unsureReturnType()
3039 {
3040     QByteArray code("<? /**\n * @return bool|int\n */\nfunction x() {} ");
3041     TopDUContext* top = parse(code, DumpAST);
3042     DUChainReleaser releaseTop(top);
3043     DUChainWriteLocker lock(DUChain::lock());
3044 
3045     Declaration* dec = top->localDeclarations().first();
3046     QVERIFY(dec->type<FunctionType>());
3047     auto ut = dec->type<FunctionType>()->returnType().dynamicCast<UnsureType>();
3048     QVERIFY(ut);
3049     QCOMPARE((uint)2, ut->typesSize());
3050     QVERIFY(ut->types()[0].type<IntegralType>());
3051     QVERIFY(ut->types()[0].type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
3052     QVERIFY(ut->types()[1].type<IntegralType>());
3053     QVERIFY(ut->types()[1].type<IntegralType>()->dataType() == IntegralType::TypeInt);
3054 }
3055 
3056 
3057 void TestDUChain::unsureReturnType2()
3058 {
3059     QByteArray code("<? class A {} class B {}\n/**\n * @return A|B\n */\nfunction x() {} ");
3060     TopDUContext* top = parse(code, DumpAST);
3061     DUChainReleaser releaseTop(top);
3062     DUChainWriteLocker lock(DUChain::lock());
3063 
3064     Declaration* dec = top->localDeclarations().at(2);
3065     QVERIFY(dec->type<FunctionType>());
3066     auto ut = dec->type<FunctionType>()->returnType().dynamicCast<UnsureType>();
3067     QVERIFY(ut);
3068     QCOMPARE((uint)2, ut->typesSize());
3069     QVERIFY(ut->types()[0].type<StructureType>());
3070     QCOMPARE(ut->types()[0].type<StructureType>()->toString(), QString("A"));
3071     QVERIFY(ut->types()[1].type<StructureType>());
3072     QCOMPARE(ut->types()[1].type<StructureType>()->toString(), QString("B"));
3073 }
3074 
3075 void TestDUChain::unsureReturnType3()
3076 {
3077     QByteArray code("<? function x() { if(rand(0,1)) return false; else return 1; return \"a\"; } ");
3078     TopDUContext* top = parse(code, DumpAST);
3079     DUChainReleaser releaseTop(top);
3080     DUChainWriteLocker lock(DUChain::lock());
3081 
3082     Declaration* dec = top->localDeclarations().at(0);
3083     QVERIFY(dec->type<FunctionType>());
3084     qDebug() << dec->type<FunctionType>()->returnType()->toString();
3085     auto ut = dec->type<FunctionType>()->returnType().dynamicCast<UnsureType>();
3086     QVERIFY(ut);
3087     QCOMPARE((uint)3, ut->typesSize());
3088     QVERIFY(ut->types()[0].type<IntegralType>());
3089     QVERIFY(ut->types()[0].type<IntegralType>()->dataType() == IntegralType::TypeInt);
3090     QVERIFY(ut->types()[1].type<IntegralType>());
3091     QVERIFY(ut->types()[1].type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
3092     QVERIFY(ut->types()[2].type<IntegralType>());
3093     QVERIFY(ut->types()[2].type<IntegralType>()->dataType() == IntegralType::TypeString);
3094 }
3095 
3096 void TestDUChain::unsureReturnType4()
3097 {
3098     QByteArray code("<? \n/**\n * @param bool|int\n */\nfunction x($a) { return $a; } ");
3099     TopDUContext* top = parse(code, DumpAST);
3100     DUChainReleaser releaseTop(top);
3101     DUChainWriteLocker lock(DUChain::lock());
3102 
3103     Declaration* dec = top->localDeclarations().first();
3104     QVERIFY(dec->type<FunctionType>());
3105     auto ut = dec->type<FunctionType>()->returnType().dynamicCast<UnsureType>();
3106     QVERIFY(ut);
3107     QCOMPARE((uint)2, ut->typesSize());
3108     QVERIFY(ut->types()[0].type<IntegralType>());
3109     QVERIFY(ut->types()[0].type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
3110     QVERIFY(ut->types()[1].type<IntegralType>());
3111     QVERIFY(ut->types()[1].type<IntegralType>()->dataType() == IntegralType::TypeInt);
3112 }
3113 
3114 void TestDUChain::referencedArgument()
3115 {
3116     // php does not return references
3117     QByteArray code("<? \nfunction x(&$a) { $a = 1; return $a; } ");
3118     TopDUContext* top = parse(code, DumpAST);
3119     DUChainReleaser releaseTop(top);
3120     DUChainWriteLocker lock(DUChain::lock());
3121 
3122     Declaration* dec = top->localDeclarations().first();
3123     QVERIFY(dec->type<FunctionType>());
3124     qDebug() << dec->abstractType()->toString();
3125     auto aType = dec->type<FunctionType>()->returnType().dynamicCast<IntegralType>();
3126     QVERIFY(aType);
3127     QCOMPARE(aType->dataType(), (uint)IntegralType::TypeInt);
3128     QCOMPARE(top->childContexts().first()->type(), DUContext::Function);
3129     auto rType = top->childContexts().first()->localDeclarations().first()->abstractType().dynamicCast<ReferenceType>();
3130     QVERIFY(rType);
3131     QVERIFY(rType->baseType()->equals(aType.data()));
3132 }
3133 
3134 void TestDUChain::unsureReferencedArgument()
3135 {
3136     // php does not return references
3137     QByteArray code("<? \nfunction x(&$a) { $a = 1; $a = 'asdf'; return $a; } ");
3138     TopDUContext* top = parse(code, DumpAST);
3139     DUChainReleaser releaseTop(top);
3140     DUChainWriteLocker lock(DUChain::lock());
3141 
3142     Declaration* dec = top->localDeclarations().first();
3143     QVERIFY(dec->type<FunctionType>());
3144     qDebug() << dec->abstractType()->toString();
3145     auto aType = dec->type<FunctionType>()->returnType().dynamicCast<UnsureType>();
3146     QVERIFY(aType);
3147     QCOMPARE(aType->typesSize(), 2u);
3148     QCOMPARE(aType->types()[0].abstractType().staticCast<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
3149     QCOMPARE(aType->types()[1].abstractType().staticCast<IntegralType>()->dataType(), (uint)IntegralType::TypeString);
3150     QCOMPARE(top->childContexts().first()->type(), DUContext::Function);
3151     auto rType = top->childContexts().first()->localDeclarations().first()->abstractType().dynamicCast<ReferenceType>();
3152     QVERIFY(rType);
3153     QVERIFY(rType->baseType()->equals(aType.data()));
3154 }
3155 
3156 void TestDUChain::defaultArgument()
3157 {
3158     // php does not return references
3159     QByteArray code("<? \nfunction x($a = 1) {} ");
3160     TopDUContext* top = parse(code, DumpAST);
3161     DUChainReleaser releaseTop(top);
3162     DUChainWriteLocker lock(DUChain::lock());
3163 
3164     Declaration* dec = top->childContexts().first()->localDeclarations().first();
3165     QVERIFY(dec->type<IntegralType>());
3166     QCOMPARE(dec->type<IntegralType>()->dataType(), (uint)IntegralType::TypeInt);
3167 }
3168 
3169 void TestDUChain::declareMemberOutOfClass()
3170 {
3171     //               0         1         2         3         4         5         6         7
3172     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3173     QByteArray code("<? class foo{ protected $prot; private $priv; }\n"
3174                     // allowed, should only declare asdf once
3175                     "$bar = new foo; $bar->asdf = true; $bar->asdf = false;\n"
3176                     // not allowed:
3177                     "$bar->prot = 1;\n"
3178                     // not allowed:
3179                     "$bar->priv = 1;");
3180     TopDUContext* top = parse(code, DumpAST);
3181     DUChainReleaser releaseTop(top);
3182     DUChainWriteLocker lock(DUChain::lock());
3183 
3184     { // $bar is only declared once
3185         QList<Declaration*> decs = top->findLocalDeclarations(Identifier(QStringLiteral("bar")));
3186         QCOMPARE(decs.size(), 1);
3187         Declaration *dec = decs.first();
3188         QVERIFY(dec->type<StructureType>());
3189         QVERIFY(dec->type<StructureType>()->declaration(top)->identifier().nameEquals(Identifier(u"foo")));
3190 
3191         // while we are at it, compare uses
3192         QCOMPARE(dec->uses().keys().count(), 1);
3193         QCOMPARE(dec->uses().values().count(), 1);
3194         QCOMPARE(dec->uses().values().first().count(), 4);
3195         qDebug() << dec->uses().values().first().at(0).castToSimpleRange();
3196         QCOMPARE(dec->uses().values().first().at(0), RangeInRevision(1, 16, 1, 20));
3197         qDebug() << dec->uses().values().first().at(1).castToSimpleRange();
3198         QCOMPARE(dec->uses().values().first().at(1), RangeInRevision(1, 35, 1, 39));
3199         qDebug() << dec->uses().values().first().at(2).castToSimpleRange();
3200         QCOMPARE(dec->uses().values().first().at(2), RangeInRevision(2, 0, 2, 4));
3201         qDebug() << dec->uses().values().first().at(3).castToSimpleRange();
3202         QCOMPARE(dec->uses().values().first().at(3), RangeInRevision(3, 0, 3, 4));
3203     }
3204 
3205     { // check if asdf got declared
3206         QList<Declaration*> decs = top->childContexts().first()->findDeclarations(Identifier(QStringLiteral("asdf")));
3207         // the type of both assignments to $bar->asdf are the same, hence it should only be declared once
3208         QCOMPARE(decs.size(), 1);
3209         ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3210         QVERIFY(cmdec);
3211         QVERIFY(cmdec->accessPolicy() == Declaration::Public);
3212         QVERIFY(!cmdec->isStatic());
3213         QVERIFY(cmdec->type<IntegralType>());
3214         QVERIFY(cmdec->type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
3215     }
3216 
3217     // check that prot and priv don't get redeclared
3218     QCOMPARE(top->problems().count(), 2);
3219     QCOMPARE(top->problems().at(0)->finalLocation().start().line(), 2);
3220     QCOMPARE(top->problems().at(1)->finalLocation().start().line(), 3);
3221 }
3222 
3223 void TestDUChain::declareMemberOutOfClass2()
3224 {
3225     // see also: https://bugs.kde.org/show_bug.cgi?id=283356
3226     //               0         1         2         3         4         5         6         7
3227     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3228     QByteArray code("<? $a = new A();\n"
3229                     // allowed, should re-use existing declaration
3230                     "$a->x = 1;\n"
3231                     "class A { var $x = 1; }");
3232     TopDUContext* top = parse(code, DumpAST);
3233     QVERIFY(top);
3234     // update
3235     top = parse(code, DumpNone, top->url().toUrl(), ReferencedTopDUContext(top));
3236     DUChainReleaser releaseTop(top);
3237     DUChainWriteLocker lock;
3238 
3239     QVERIFY(top->problems().isEmpty());
3240 
3241     QList<Declaration*> decs = top->findLocalDeclarations(Identifier(QStringLiteral("a")));
3242     QCOMPARE(decs.size(), 2);
3243     {
3244         Declaration *dec = decs.first();
3245         QVERIFY(dynamic_cast<VariableDeclaration*>(dec));
3246         QVERIFY(dec->type<StructureType>());
3247         QVERIFY(dec->type<StructureType>()->declaration(top)->identifier().nameEquals(Identifier(u"a")));
3248     }
3249     {
3250         Declaration *dec = decs.last();
3251         QVERIFY(dynamic_cast<ClassDeclaration*>(dec));
3252         QVERIFY(dec->type<StructureType>());
3253         QVERIFY(dec->type<StructureType>()->declaration(top)->identifier().nameEquals(Identifier(u"a")));
3254     }
3255 
3256     { // check if x got declared
3257         QList<Declaration*> decs = top->childContexts().first()->findDeclarations(Identifier(QStringLiteral("x")));
3258         // the type of both assignments to $a->x are the same, hence it should only be declared once
3259         QCOMPARE(decs.size(), 1);
3260         ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3261         QVERIFY(cmdec);
3262         QVERIFY(cmdec->accessPolicy() == Declaration::Public);
3263         QVERIFY(!cmdec->isStatic());
3264         QVERIFY(cmdec->type<IntegralType>());
3265         QCOMPARE(cmdec->type<IntegralType>()->dataType(), (uint) IntegralType::TypeInt);
3266     }
3267 }
3268 
3269 void TestDUChain::declareMemberInClassMethod()
3270 {
3271     QByteArray code("<? class foo { private $priv = 0; protected $prot = 0; } class bar extends foo {\n"
3272                     // should declare member variable asdf (once!) as public
3273                     " function test() { $this->asdf = true; $this->asdf = false; }\n"
3274                     // should only declare bar once as private
3275                     " private $xyz = 0; function test2() { $this->xyz = 42; }\n"
3276                     // should create a local declaration for the private attribute
3277                     " function test3() { $this->prot = 42;\n$this->priv = 42; }\n"
3278                     " }");
3279     TopDUContext* top = parse(code, DumpAST);
3280     DUChainReleaser releaseTop(top);
3281     DUChainWriteLocker lock(DUChain::lock());
3282 
3283     { // asdf
3284         QList<Declaration*> decs = top->childContexts().last()->findLocalDeclarations(Identifier(QStringLiteral("asdf")));
3285         QCOMPARE(decs.size(), 1);
3286         ClassMemberDeclaration *dec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3287         QVERIFY(dec);
3288         QVERIFY(dec->accessPolicy() == Declaration::Public);
3289         QVERIFY(!dec->isStatic());
3290         QVERIFY(dec->type<IntegralType>());
3291         QVERIFY(dec->type<IntegralType>()->dataType() == IntegralType::TypeBoolean);
3292     }
3293 
3294     { // xyz
3295         QList<Declaration*> decs = top->childContexts().last()->findLocalDeclarations(Identifier(QStringLiteral("xyz")));
3296         QCOMPARE(decs.size(), 1);
3297         ClassMemberDeclaration *dec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3298         QVERIFY(dec);
3299         QVERIFY(dec->accessPolicy() == Declaration::Private);
3300         QVERIFY(!dec->isStatic());
3301         QVERIFY(dec->type<IntegralType>());
3302         QVERIFY(dec->type<IntegralType>()->dataType() == IntegralType::TypeInt);
3303     }
3304 
3305     { // priv
3306         QList<Declaration*> decs = top->childContexts().last()->findLocalDeclarations(Identifier(QStringLiteral("priv")));
3307         QCOMPARE(decs.size(), 1);
3308         ClassMemberDeclaration *dec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3309         QVERIFY(dec);
3310         QVERIFY(dec->accessPolicy() == Declaration::Public);
3311         QVERIFY(!dec->isStatic());
3312         QVERIFY(dec->type<IntegralType>());
3313         QVERIFY(dec->type<IntegralType>()->dataType() == IntegralType::TypeInt);
3314     }
3315 
3316     { // prot
3317         QVERIFY(top->childContexts().last()->findLocalDeclarations(Identifier(u"prot")).isEmpty());
3318     }
3319 
3320     QCOMPARE(top->problems().count(), 0);
3321 }
3322 
3323 void TestDUChain::thisRedeclaration()
3324 {
3325     //               0         1         2         3         4         5         6         7
3326     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3327     QByteArray code("<? class foo{ function foo(){ $this->test = true; $this = false;} }");
3328     TopDUContext* top = parse(code, DumpAST);
3329     DUChainReleaser releaseTop(top);
3330     DUChainWriteLocker lock(DUChain::lock());
3331 
3332     // only $this = false is a problem, $this->test = true is perfectly valid
3333     QCOMPARE(top->problems().count(), 1);
3334     qDebug() << top->problems().first()->finalLocation();
3335     QVERIFY(top->problems().first()->finalLocation() == KDevelop::DocumentRange(top->url(), KTextEditor::Range(0, 50, 0, 55)));
3336 }
3337 
3338 void TestDUChain::implicitArrayDeclaration()
3339 {
3340     ///TODO: adapt to unsure type once it's supported
3341     {
3342     //               0         1         2         3         4         5         6         7
3343     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3344     QByteArray code("<? $a[1] = true;");
3345     TopDUContext* top = parse(code, DumpAST);
3346     DUChainReleaser releaseTop(top);
3347     DUChainWriteLocker lock(DUChain::lock());
3348 
3349     QList<Declaration*> decs = top->findDeclarations(Identifier(QStringLiteral("a")));
3350     QCOMPARE(decs.size(), 1);
3351     VariableDeclaration* vdec = dynamic_cast<VariableDeclaration*>(decs.first());
3352     QVERIFY(vdec);
3353     QVERIFY(vdec->type<IntegralType>());
3354     QVERIFY(vdec->type<IntegralType>()->dataType() == IntegralType::TypeArray);
3355     }
3356 
3357     {
3358     //               0         1         2         3         4         5         6         7
3359     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3360     QByteArray code("<? $b = 1; $a[$b] = true;");
3361     TopDUContext* top = parse(code, DumpAST);
3362     DUChainReleaser releaseTop(top);
3363     DUChainWriteLocker lock(DUChain::lock());
3364 
3365     QList<Declaration*> decs = top->findDeclarations(Identifier(QStringLiteral("a")));
3366     QCOMPARE(decs.size(), 1);
3367     VariableDeclaration* vdec = dynamic_cast<VariableDeclaration*>(decs.first());
3368     QVERIFY(vdec);
3369     QVERIFY(vdec->type<IntegralType>());
3370     QVERIFY(vdec->type<IntegralType>()->dataType() == IntegralType::TypeArray);
3371     }
3372 
3373     {
3374     //               0         1         2         3         4         5         6         7
3375     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3376     QByteArray code("<? class foo{} $bar = new foo; $bar->a[1] = true;");
3377     TopDUContext* top = parse(code, DumpAST);
3378     DUChainReleaser releaseTop(top);
3379     DUChainWriteLocker lock(DUChain::lock());
3380 
3381     QList<Declaration*> decs = top->childContexts().first()->findDeclarations(Identifier(QStringLiteral("a")));
3382     QCOMPARE(decs.size(), 1);
3383     ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3384     QVERIFY(cmdec);
3385     QVERIFY(cmdec->type<IntegralType>());
3386     QVERIFY(cmdec->type<IntegralType>()->dataType() == IntegralType::TypeArray);
3387     }
3388 
3389     {
3390     //               0         1         2         3         4         5         6         7
3391     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3392     QByteArray code("<? class foo{} $bar = new foo; $b = 1; $bar->a[$b] = true;");
3393     TopDUContext* top = parse(code, DumpAST);
3394     DUChainReleaser releaseTop(top);
3395     DUChainWriteLocker lock(DUChain::lock());
3396 
3397     QList<Declaration*> decs = top->childContexts().first()->findDeclarations(Identifier(QStringLiteral("a")));
3398     QCOMPARE(decs.size(), 1);
3399     ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3400     QVERIFY(cmdec);
3401     QVERIFY(cmdec->type<IntegralType>());
3402     QVERIFY(cmdec->type<IntegralType>()->dataType() == IntegralType::TypeArray);
3403     }
3404 }
3405 
3406 void TestDUChain::implicitReferenceDeclaration()
3407 {
3408     {
3409     //               0         1         2         3         4         5         6         7
3410     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3411     QByteArray code("<? function asdf(&$foo) {} asdf($bar);");
3412     TopDUContext* top = parse(code, DumpAST);
3413     DUChainReleaser releaseTop(top);
3414     DUChainWriteLocker lock(DUChain::lock());
3415 
3416     QList<Declaration*> decs = top->findDeclarations(Identifier(QStringLiteral("bar")));
3417     QCOMPARE(decs.size(), 1);
3418     QVERIFY(dynamic_cast<VariableDeclaration*>(decs.first()));
3419     QVERIFY(decs.first()->type<IntegralType>());
3420     qDebug() << decs.first()->type<IntegralType>()->dataType() << decs.first()->toString();
3421     QVERIFY(decs.first()->type<IntegralType>()->dataType() == IntegralType::TypeNull);
3422     }
3423 
3424     {
3425     // a user reported a crash with the code example below
3426 
3427     //               0         1         2         3         4         5         6         7
3428     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3429     QByteArray code("<? function test(&$p) {} class foo{ private $a; function test() {test($this->a);}  }");
3430     TopDUContext* top = parse(code, DumpAST);
3431     DUChainReleaser releaseTop(top);
3432     DUChainWriteLocker lock(DUChain::lock());
3433 
3434     QVERIFY( top->childContexts().last()->localScopeIdentifier() == QualifiedIdentifier(u"foo"));
3435 
3436     // a is already declared
3437     QList<Declaration*> decs = top->childContexts().last()->findDeclarations(Identifier(QStringLiteral("a")));
3438     QCOMPARE(decs.size(), 1);
3439     ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3440     QVERIFY(cmdec);
3441     QVERIFY(cmdec->type<IntegralType>());
3442 
3443     qDebug() << cmdec->type<IntegralType>()->dataType() << cmdec->toString();
3444     QVERIFY(cmdec->type<IntegralType>()->dataType() == IntegralType::TypeNull);
3445     }
3446 }
3447 
3448 void TestDUChain::classContextRange()
3449 {
3450     //               0         1         2         3         4         5         6         7
3451     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3452     QByteArray code("<?php class A { } $a = new A; ?> <?php $b = 1; $a->foobar = 1; $a->barFoo= 0;");
3453     TopDUContext* top = parse(code, DumpAST);
3454     DUChainReleaser releaseTop(top);
3455     DUChainWriteLocker lock(DUChain::lock());
3456 
3457     QCOMPARE(top->childContexts().first()->range(), KDevelop::RangeInRevision(0, 6, 0, 17));
3458     QCOMPARE(top->childContexts().first()->localDeclarations().count(), 2);
3459 }
3460 
3461 void TestDUChain::lateClassMembers()
3462 {
3463     //               0         1         2         3         4         5         6         7
3464     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3465     QByteArray code("<?php class A { function f() { $this->val = 'b'; } private $val = 'a'; } ");
3466     TopDUContext* top = parse(code, DumpAST);
3467     DUChainReleaser releaseTop(top);
3468     DUChainWriteLocker lock(DUChain::lock());
3469 
3470     ClassDeclaration* cdec = dynamic_cast<ClassDeclaration*>(top->localDeclarations().first());
3471     QVERIFY(cdec);
3472     QList<Declaration*> decs = cdec->logicalInternalContext(top)->findDeclarations(Identifier(QStringLiteral("val")));
3473     QCOMPARE(decs.count(), 1);
3474     ClassMemberDeclaration* cmdec = dynamic_cast<ClassMemberDeclaration*>(decs.first());
3475     QVERIFY(cmdec);
3476     QCOMPARE(cmdec->accessPolicy(), Declaration::Private);
3477 }
3478 
3479 void TestDUChain::list()
3480 {
3481     foreach ( const QString& code, QStringList() << "<?php list($i, $j, $k) = array(1,2,3);"
3482                                                  << "<?php $a = array(1,2,3); list($i,$j,$k) = $a;"
3483                                                  << "<?php function t() { return array(1,2,3); } list($i,$j,$k) = t();" )
3484     {
3485         //               0         1         2         3         4         5         6         7
3486         //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3487         TopDUContext* top = parse(code.toUtf8(), DumpAST);
3488         DUChainReleaser releaseTop(top);
3489         DUChainWriteLocker lock(DUChain::lock());
3490 
3491         foreach ( const QString& identifier, QStringList() << "i" << "j" << "k" ) {
3492             qDebug() << "searching for declaration of " << identifier;
3493             QList<Declaration*> decs = top->findDeclarations(Identifier(identifier));
3494             QCOMPARE(decs.size(), 1);
3495             Declaration *dec = decs.first();
3496             QVERIFY(dec->type<IntegralType>());
3497             QCOMPARE(dec->type<IntegralType>()->dataType(), (uint) IntegralType::TypeMixed);
3498             ///TODO: support arrays better and compare to actual type
3499         }
3500     }
3501 }
3502 
3503 void TestDUChain::alternateDocCommentTypeHints()
3504 {
3505     //               0         1         2         3         4         5         6         7
3506     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3507     TopDUContext* top = parse("<?php class test {\n/// @var test\nprivate static $test;\n}", DumpAST);
3508     DUChainReleaser releaseTop(top);
3509     DUChainWriteLocker lock(DUChain::lock());
3510 
3511     ClassDeclaration* cdec = dynamic_cast<ClassDeclaration*>(top->localDeclarations().first());
3512     QVERIFY(cdec);
3513     QVERIFY(cdec->type<StructureType>());
3514     QVector<Declaration*> decs = cdec->logicalInternalContext(top)->localDeclarations();
3515     QCOMPARE(decs.size(), 1);
3516     Declaration *dec = decs.first();
3517     QVERIFY(dec->type<StructureType>());
3518     QCOMPARE(dec->type<StructureType>()->declaration(top), cdec);
3519 }
3520 
3521 void TestDUChain::findFunctionArgs()
3522 {
3523     //               0         1         2         3         4         5         6         7
3524     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3525     TopDUContext* top = parse("<?php function foo($bar, $asdf) {}", DumpNone);
3526     DUChainReleaser releaseTop(top);
3527     DUChainWriteLocker lock(DUChain::lock());
3528 
3529     FunctionDeclaration* funcDec = dynamic_cast<FunctionDeclaration*>(top->localDeclarations().first());
3530     QVERIFY(funcDec);
3531     QVERIFY(funcDec->internalContext());
3532     QVERIFY(funcDec->internalFunctionContext());
3533     QVERIFY(funcDec->internalContext()->imports(funcDec->internalFunctionContext()));
3534 
3535     QList<Declaration*> decs;
3536     foreach ( Declaration* arg, funcDec->internalFunctionContext()->localDeclarations() ) {
3537         decs = funcDec->internalContext()->findDeclarations(arg->identifier());
3538         QCOMPARE(decs.size(), 1);
3539         decs = funcDec->internalContext()->findDeclarations(arg->qualifiedIdentifier());
3540         qDebug() << arg->qualifiedIdentifier().toString();
3541         QEXPECT_FAIL("", "strangely the arg dec is only found with its identifier, not by its qualifiedidentifier...", Continue);
3542         QCOMPARE(decs.size(), 1);
3543     }
3544 }
3545 
3546 void TestDUChain::undeclaredPropertyInString()
3547 {
3548     // testcase for bug 209814
3549 
3550     //               0         1         2         3         4         5         6         7
3551     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3552     TopDUContext* top = parse("<?php class foo { var $foo; function bar() { \"$this->baz\"; } }", DumpNone);
3553     DUChainReleaser releaseTop(top);
3554     DUChainWriteLocker lock(DUChain::lock());
3555 
3556     QCOMPARE(top->childContexts().size(), 1);
3557     DUContext* classCtx = top->childContexts().first();
3558     QVERIFY(classCtx->type() == DUContext::Class);
3559     QCOMPARE(classCtx->localDeclarations().size(), 2);
3560     QCOMPARE(classCtx->findDeclarations(Identifier(u"foo")).size(), 1);
3561     QCOMPARE(classCtx->findDeclarations(Identifier(u"bar")).size(), 1);
3562 }
3563 
3564 void TestDUChain::undeclaredVarPropertyInString()
3565 {
3566     // testcase for bug 210043
3567 
3568     //               0         1         2         3         4         5         6         7
3569     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3570     TopDUContext* top = parse("<?php \"$a->baz\";", DumpNone);
3571     DUChainReleaser releaseTop(top);
3572     DUChainWriteLocker lock(DUChain::lock());
3573     // just don't crash
3574 }
3575 
3576 void TestDUChain::upcommingClassInString()
3577 {
3578     // testcase for bug 232687
3579 
3580     //               0         1         2         3         4         5         6         7
3581     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3582     TopDUContext* top = parse("<?php\n"
3583                                 "class A {\n"
3584                                 "    function A () {\n"
3585                                 "        $b = new B();\n"
3586                                 "        echo \"$b->blah\";\n"
3587                                 "    }\n"
3588                                 "}\n"
3589                                 "class B {\n"
3590                                 "  var $blah;\n"
3591                                 "}\n", DumpNone);
3592     DUChainReleaser releaseTop(top);
3593     DUChainWriteLocker lock(DUChain::lock());
3594     // just don't crash
3595 }
3596 
3597 void TestDUChain::namespaces()
3598 {
3599     //               0         1         2         3         4         5         6         7
3600     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3601     TopDUContext* top = parse("<?php\n"
3602                               "namespace asdf{\n"
3603                               "function a(){}\n"
3604                               "define('b', 0);\n"
3605                               "class c {}\n"
3606                               "}\n"
3607                               "namespace NS1\\NS2 {\n"
3608                               "function a(){}\n"
3609                               "define('b', 0);\n"
3610                               "class c {}\n"
3611                               "}\n"
3612                               "namespace {\n"
3613                               "function a(){}\n"
3614                               "}\n"
3615                               , DumpAll);
3616     DUChainReleaser releaseTop(top);
3617     DUChainWriteLocker lock;
3618 
3619     QCOMPARE(top->problems().count(), 0);
3620 
3621     QCOMPARE(top->childContexts().size(), 4);
3622     QCOMPARE(top->childContexts().at(0)->localScopeIdentifier().toString(), QString("asdf"));
3623     QCOMPARE(top->childContexts().at(1)->localScopeIdentifier().toString(), QString("ns1"));
3624 
3625     QCOMPARE(top->childContexts().at(2)->type(), DUContext::Function);
3626     QCOMPARE(top->childContexts().at(3)->localScopeIdentifier().toString(), QString("a"));
3627 
3628     QCOMPARE(top->localDeclarations().size(), 3);
3629     QCOMPARE(top->localDeclarations().at(0)->kind(), Declaration::Namespace);
3630     QCOMPARE(top->localDeclarations().at(1)->kind(), Declaration::Namespace);
3631     QCOMPARE(top->localDeclarations().at(2)->kind(), Declaration::Type);
3632 
3633     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"asdf")).size(), 1);
3634     QCOMPARE(top->childContexts().at(0)->localDeclarations().size(), 3);
3635     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"asdf::a")).size(), 1);
3636     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"asdf::b")).size(), 1);
3637     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"asdf::c")).size(), 1);
3638 
3639     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1")).size(), 1);
3640     QCOMPARE(top->childContexts().at(1)->localDeclarations().size(), 1);
3641     QCOMPARE(top->childContexts().at(1)->localDeclarations().first()->kind(), Declaration::Namespace);
3642     ///TODO: support \ as separator
3643     QCOMPARE(top->childContexts().at(1)->localDeclarations().first()->qualifiedIdentifier().toString(), QString("ns1::ns2"));
3644     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2")).size(), 1);
3645     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2")).first()->logicalInternalContext(top)->localDeclarations().size(), 3);
3646     QCOMPARE(top->childContexts().at(1)->childContexts().size(), 1);
3647     QCOMPARE(top->childContexts().at(1)->childContexts().first()->localDeclarations().size(), 3);
3648     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2")).first()->logicalInternalContext(top)->localDeclarations().first()->qualifiedIdentifier().toString(),
3649              QString("ns1::ns2::a"));
3650     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2::a")).size(), 1);
3651     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2::b")).size(), 1);
3652     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"ns1::ns2::c")).size(), 1);
3653 
3654     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"a")).size(), 1);
3655     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"b")).size(), 0);
3656     QCOMPARE(top->findDeclarations(QualifiedIdentifier(u"c")).size(), 0);
3657 
3658     ///TODO: prevent redeclarations of namespaces
3659 }
3660 
3661 void TestDUChain::namespacesNoCurly()
3662 {
3663     //               0         1         2         3         4         5         6         7
3664     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3665     TopDUContext* top = parse("<?php\n"
3666                               "namespace asdf;\n"
3667                               "function a(){}\n"
3668                               "define('b', 0);\n"
3669                               "class c {}\n"
3670                               "\n"
3671                               "namespace NS1\\NS2;\n"
3672                               "function a(){}\n"
3673                               "define('b', 0);\n"
3674                               "class c {}\n"
3675                               "\n"
3676                               , DumpAll);
3677     DUChainReleaser releaseTop(top);
3678     DUChainWriteLocker lock;
3679 
3680     QCOMPARE(top->problems().count(), 0);
3681     foreach(ProblemPointer p, top->problems()) {
3682         qDebug() << p->description() << p->explanation() << p->finalLocation();
3683     }
3684     QCOMPARE(top->childContexts().size(), 2);
3685     QCOMPARE(top->childContexts().at(0)->localScopeIdentifier().toString(), QString("asdf"));
3686     QCOMPARE(top->childContexts().at(1)->localScopeIdentifier().toString(), QString("ns1"));
3687 
3688     QCOMPARE(top->localDeclarations().size(), 2);
3689     QCOMPARE(top->localDeclarations().at(0)->kind(), Declaration::Namespace);
3690     QCOMPARE(top->localDeclarations().at(1)->kind(), Declaration::Namespace);
3691 }
3692 
3693 void TestDUChain::namespacesBaseType()
3694 {
3695     //               0         1         2         3         4         5         6         7
3696     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3697     TopDUContext* top = parse("<?php\n"
3698                               "namespace string;\n"
3699                               "function a(){}\n"
3700                               "define('b', 0);\n"
3701                               "class c {}\n"
3702                               "\n"
3703                               , DumpAll);
3704     DUChainReleaser releaseTop(top);
3705     DUChainWriteLocker lock;
3706 
3707     QCOMPARE(top->problems().count(), 0);
3708     foreach(ProblemPointer p, top->problems()) {
3709         qDebug() << p->description() << p->explanation() << p->finalLocation();
3710     }
3711     QCOMPARE(top->childContexts().size(), 1);
3712     QCOMPARE(top->childContexts().at(0)->localScopeIdentifier().toString(), QString("string"));
3713 
3714     QCOMPARE(top->localDeclarations().size(), 1);
3715     QCOMPARE(top->localDeclarations().at(0)->kind(), Declaration::Namespace);
3716 }
3717 
3718 void TestDUChain::useNamespace()
3719 {
3720     //               0         1         2         3         4         5         6         7
3721     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3722     TopDUContext* top = parse("<?php\n"
3723                               "namespace ns1\\ns2 {\n"
3724                               "function a(){}\n"
3725                               "const b = 0;\n"
3726                               "class c {}\n"
3727                               "}\n"
3728                               "namespace ns3\\ns4 {\n"
3729                               "function a(){}\n"
3730                               "const b = 0;\n"
3731                               "class c {}\n"
3732                               "}\n"
3733                               "namespace {\n"
3734                               "use ns1\\ns2, ns3\\ns4 as ns5;\n"
3735                               "use \\ns3\\ns4 as ns6;\n"
3736                               "}\n"
3737                               , DumpNone);
3738     QVERIFY(top);
3739     DUChainReleaser releaseTop(top);
3740     DUChainWriteLocker lock;
3741 
3742     QCOMPARE(top->localDeclarations().count(), 5);
3743 
3744     Declaration* dec = top->localDeclarations().at(2);
3745     QCOMPARE(dec->qualifiedIdentifier().toString(), QString("ns2"));
3746     QVERIFY(dynamic_cast<NamespaceAliasDeclaration*>(dec));
3747 
3748     dec = top->localDeclarations().at(3);
3749     QCOMPARE(dec->qualifiedIdentifier().toString(), QString("ns5"));
3750     QVERIFY(dynamic_cast<NamespaceAliasDeclaration*>(dec));
3751 
3752     dec = top->localDeclarations().at(4);
3753     QCOMPARE(dec->qualifiedIdentifier().toString(), QString("ns6"));
3754     QVERIFY(dynamic_cast<NamespaceAliasDeclaration*>(dec));
3755     ///TODO: find out why this is explicitly required
3756     QVERIFY(!dynamic_cast<NamespaceAliasDeclaration*>(dec)->importIdentifier().explicitlyGlobal());
3757 }
3758 
3759 void TestDUChain::useGroupedNamespace_data()
3760 {
3761     QTest::addColumn<QString>("code");
3762 
3763     QTest::newRow("class group") <<
3764         QStringLiteral("<?php\n"
3765                        "namespace ns1\\ns2 { class A {} }\n"
3766                        "namespace ns1\\ns2 { class B {} }\n"
3767                        "namespace ns3 {\n"
3768                        "use ns1\\ns2\\{A,B};\n"
3769                        "}\n");
3770     QTest::newRow("class group with partial namespace") <<
3771         QStringLiteral("<?php\n"
3772                        "namespace ns1\\ns2 { class A {} }\n"
3773                        "namespace ns1\\ns3 { class B {} }\n"
3774                        "namespace ns3 {\n"
3775                        "use ns1\\{ns2\\A,ns3\\B};\n"
3776                        "}\n");
3777     QTest::newRow("class group with trailing comma") <<
3778         QStringLiteral("<?php\n"
3779                        "namespace ns1\\ns2 { class A {} }\n"
3780                        "namespace ns1\\ns2 { class B {} }\n"
3781                        "namespace ns3 {\n"
3782                        "use ns1\\ns2\\{A,B,};\n"
3783                        "}\n");
3784     QTest::newRow("function group") <<
3785         QStringLiteral("<?php\n"
3786                        "namespace ns1\\ns2 { function foo(){} }\n"
3787                        "namespace ns1\\ns2 { function bar(){} }\n"
3788                        "namespace ns3 {\n"
3789                        "use function ns1\\ns2\\{foo,bar};\n"
3790                        "}\n");
3791     QTest::newRow("function group with partial namespace") <<
3792         QStringLiteral("<?php\n"
3793                        "namespace ns1\\ns2 { function foo(){} }\n"
3794                        "namespace ns1\\ns3 { function bar(){} }\n"
3795                        "namespace ns3 {\n"
3796                        "use function ns1\\{ns2\\foo,ns3\\bar};\n"
3797                        "}\n");
3798     QTest::newRow("function group with trailing comma") <<
3799         QStringLiteral("<?php\n"
3800                        "namespace ns1\\ns2 { function foo(){} }\n"
3801                        "namespace ns1\\ns2 { function bar(){} }\n"
3802                        "namespace ns3 {\n"
3803                        "use function ns1\\ns2\\{foo,bar,};\n"
3804                        "}\n");
3805     QTest::newRow("constant group") <<
3806         QStringLiteral("<?php\n"
3807                        "namespace ns1\\ns2 { const A = ''; }\n"
3808                        "namespace ns1\\ns2 { const B = ''; }\n"
3809                        "namespace ns3 {\n"
3810                        "use const ns1\\ns2\\{A,B};\n"
3811                        "}\n");
3812     QTest::newRow("constant group with partial namespace") <<
3813         QStringLiteral("<?php\n"
3814                        "namespace ns1\\ns2 { const A = ''; }\n"
3815                        "namespace ns1\\ns3 { const B = ''; }\n"
3816                        "namespace ns3 {\n"
3817                        "use const ns1\\{ns2\\A,ns3\\B};\n"
3818                        "}\n");
3819     QTest::newRow("constant group with trailing comma") <<
3820         QStringLiteral("<?php\n"
3821                        "namespace ns1\\ns2 { const A = ''; }\n"
3822                        "namespace ns1\\ns2 { const B = ''; }\n"
3823                        "namespace ns3 {\n"
3824                        "use const ns1\\ns2\\{A,B,};\n"
3825                        "}\n");
3826     QTest::newRow("mixed group") <<
3827         QStringLiteral("<?php\n"
3828                        "namespace ns1\\ns2 { const A = ''; function foo(){} }\n"
3829                        "namespace ns1\\ns2 { class C {} }\n"
3830                        "namespace ns3 {\n"
3831                        "use ns1\\ns2\\{C, const A, function foo};\n"
3832                        "}\n");
3833     QTest::newRow("mixed group with trailing comma") <<
3834         QStringLiteral("<?php\n"
3835                        "namespace ns1\\ns2 { const A = ''; function foo(){} }\n"
3836                        "namespace ns1\\ns2 { class C {} }\n"
3837                        "namespace ns3 {\n"
3838                        "use ns1\\ns2\\{C, const A, function foo,};\n"
3839                        "}\n");
3840 }
3841 
3842 void TestDUChain::useGroupedNamespace()
3843 {
3844     QFETCH(QString, code);
3845 
3846     TopDUContext* top = parse(code.toUtf8(), DumpNone);
3847     QVERIFY(top);
3848     DUChainReleaser releaseTop(top);
3849     DUChainWriteLocker lock;
3850 
3851     QVERIFY(top->problems().isEmpty());
3852 }
3853 
3854 void TestDUChain::useInvalidGroupedNamespace_data()
3855 {
3856     QTest::addColumn<QString>("code");
3857 
3858     QTest::newRow("mixed in function import") <<
3859         QStringLiteral("<?php\n"
3860                        "namespace ns1\\ns2 { const A = ''; function foo(){} }\n"
3861                        "namespace ns1\\ns2 { class C {} }\n"
3862                        "namespace ns3 {\n"
3863                        "use function ns1\\ns2\\{C, const A, function foo};\n"
3864                        "}\n");
3865     QTest::newRow("mixed in constant import") <<
3866         QStringLiteral("<?php\n"
3867                        "namespace ns1\\ns2 { const A = ''; function foo(){} }\n"
3868                        "namespace ns1\\ns2 { class C {} }\n"
3869                        "namespace ns3 {\n"
3870                        "use const ns1\\ns2\\{C, const A, function foo,};\n"
3871                        "}\n");
3872 }
3873 
3874 void TestDUChain::useInvalidGroupedNamespace()
3875 {
3876     QFETCH(QString, code);
3877 
3878     TopDUContext* top = parse(code.toUtf8(), DumpNone);
3879     QVERIFY(top);
3880     DUChainReleaser releaseTop(top);
3881     DUChainWriteLocker lock;
3882 
3883     QVERIFY(!top->problems().isEmpty());
3884 }
3885 
3886 void TestDUChain::useBaseTypeNamespace()
3887 {
3888     //               0         1         2         3         4         5         6         7
3889     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3890     TopDUContext* top = parse("<?php\n"
3891                               "namespace ns1\\string {\n"
3892                               "function a(){}\n"
3893                               "const b = 0;\n"
3894                               "class c {}\n"
3895                               "}\n"
3896                               "namespace ns3\\iterable {\n"
3897                               "function a(){}\n"
3898                               "const b = 0;\n"
3899                               "class c {}\n"
3900                               "}\n"
3901                               "namespace {\n"
3902                               "use ns1\\string, ns3\\iterable;\n"
3903                               "}\n"
3904                               , DumpNone);
3905     QVERIFY(top);
3906     DUChainReleaser releaseTop(top);
3907     DUChainWriteLocker lock;
3908 
3909     QCOMPARE(top->localDeclarations().count(), 4);
3910 
3911     Declaration* dec = top->localDeclarations().at(2);
3912     QCOMPARE(dec->qualifiedIdentifier().toString(), QString("string"));
3913     QVERIFY(dynamic_cast<NamespaceAliasDeclaration*>(dec));
3914 
3915     dec = top->localDeclarations().at(3);
3916     QCOMPARE(dec->qualifiedIdentifier().toString(), QString("iterable"));
3917     QVERIFY(dynamic_cast<NamespaceAliasDeclaration*>(dec));
3918 }
3919 
3920 void TestDUChain::useNamespaceBaseTypeAlias()
3921 {
3922     //               0         1         2         3         4         5         6         7
3923     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3924     TopDUContext* top = parse("<?php\n"
3925                               "namespace ns1\\string {\n"
3926                               "function a(){}\n"
3927                               "const b = 0;\n"
3928                               "class c {}\n"
3929                               "}\n"
3930                               "namespace ns3\\iterable {\n"
3931                               "function a(){}\n"
3932                               "const b = 0;\n"
3933                               "class c {}\n"
3934                               "}\n"
3935                               "namespace {\n"
3936                               "use ns1\\string as iterable, ns3\\iterable as bool;\n"
3937                               "}\n"
3938                               , DumpNone);
3939     QVERIFY(top);
3940     DUChainReleaser releaseTop(top);
3941     DUChainWriteLocker lock(DUChain::lock());
3942 
3943     QCOMPARE(top->problems().count(), 2);
3944 }
3945 
3946 void TestDUChain::namespaceStaticVar()
3947 {
3948     //               0         1         2         3         4         5         6         7
3949     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3950     TopDUContext* top = parse("<?php\n"
3951                               "namespace ns {\n"
3952                               "class c{ static public $foo; }\n"
3953                               "}\n"
3954                               "namespace {\n"
3955                               "\\ns\\c::$foo;\n"
3956                               "}\n"
3957                               , DumpNone);
3958     QVERIFY(top);
3959     DUChainReleaser releaseTop(top);
3960     DUChainWriteLocker lock;
3961 
3962     QVERIFY(top->problems().isEmpty());
3963     Declaration* fooDec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("ns::c::foo"))).first();
3964     QVERIFY(fooDec);
3965 
3966     QVERIFY(!fooDec->uses().isEmpty());
3967     QVERIFY(!fooDec->uses().begin()->isEmpty());
3968     QCOMPARE(fooDec->uses().begin()->begin()->start.line, 5);
3969 }
3970 
3971 void TestDUChain::namespacedCatch()
3972 {
3973     // see also: https://bugs.kde.org/show_bug.cgi?id=281451
3974     //               0         1         2         3         4         5         6         7
3975     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
3976     TopDUContext* top = parse("<?php\n"
3977                               "namespace ns {\n"
3978                               "class e{}\n"
3979                               "}\n"
3980                               "namespace {\n"
3981                               "try { /* ... */ }\n"
3982                               "catch(\\ns\\e $exception) { /* ... */ }"
3983                               "}\n"
3984                               , DumpNone);
3985     QVERIFY(top);
3986     DUChainReleaser releaseTop(top);
3987     DUChainWriteLocker lock;
3988 
3989     QVERIFY(top->problems().isEmpty());
3990     Declaration* eDec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("ns::e"))).first();
3991     QVERIFY(eDec);
3992 
3993     QVERIFY(!eDec->uses().isEmpty());
3994     QVERIFY(!eDec->uses().begin()->isEmpty());
3995     QCOMPARE(eDec->uses().begin()->begin()->start.line, 6);
3996 }
3997 
3998 struct TestUse {
3999     TestUse(const QString& _id, Declaration::Kind _kind, int _uses)
4000         : id(_id), kind(_kind), uses(_uses)
4001     {}
4002     TestUse()
4003     {}
4004     QualifiedIdentifier id;
4005     Declaration::Kind kind;
4006     int uses;
4007 };
4008 
4009 Q_DECLARE_METATYPE ( TestUse )
4010 Q_DECLARE_METATYPE ( QList<TestUse> )
4011 
4012 void TestDUChain::errorRecovery_data()
4013 {
4014     QTest::addColumn<QString>("code");
4015 
4016     QTest::addColumn< QList<TestUse> >("usesMap");
4017 
4018     QTest::newRow("conditional") << QStringLiteral("<?php $a = 1; if ( false ) { in va lid } $a = 2; ")
4019                                  << (QList<TestUse>()
4020                                     << TestUse(QStringLiteral("a"), Declaration::Instance, 1));
4021 
4022     QTest::newRow("namespace") << QStringLiteral("<?php namespace foo { const a = 1; } namespace y { -a sdflyxjcv 91348 } namespace { foo\\a; }")
4023                                  << (QList<TestUse>()
4024                                     << TestUse(QStringLiteral("foo"), Declaration::Namespace, 1)
4025                                     << TestUse(QStringLiteral("y"), Declaration::Namespace, 0)
4026                                     << TestUse(QStringLiteral("foo::a"), Declaration::Instance, 1));
4027 
4028     QTest::newRow("class") << QStringLiteral("<?php class foo { const bar = 1; invalid static function func() {} } foo::bar; foo::func;")
4029                                  << (QList<TestUse>()
4030                                     << TestUse(QStringLiteral("foo"), Declaration::Type, 0)
4031                                     << TestUse(QStringLiteral("foo::bar"), Declaration::Instance, 1)
4032                                     << TestUse(QStringLiteral("foo::func"), Declaration::Type, 1)
4033                                     );
4034 }
4035 
4036 void TestDUChain::errorRecovery()
4037 {
4038     QFETCH(QString, code);
4039     QFETCH(QList<TestUse>, usesMap);
4040 
4041     TopDUContext* top = parse(code.toLocal8Bit(), DumpAll);
4042 
4043     QVERIFY(top);
4044 
4045     DUChainReleaser releaseTop(top);
4046     DUChainWriteLocker lock;
4047 
4048     foreach ( const TestUse& use, usesMap ) {
4049         QList< Declaration* > decs = top->findDeclarations(use.id);
4050         QCOMPARE(decs.count(), 1);
4051         Declaration* dec = decs.first();
4052         QCOMPARE(dec->kind(), use.kind);
4053         if (use.uses) {
4054             QCOMPARE(dec->uses().count(), 1);
4055             QCOMPARE(dec->uses().begin()->count(), use.uses);
4056         }
4057     }
4058 }
4059 
4060 void TestDUChain::varStatic()
4061 {
4062     //bug: https://bugs.kde.org/244076
4063 
4064     //               0         1         2         3         4         5         6         7
4065     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
4066     TopDUContext* top = parse("<?php\n"
4067                               "class c { const a = 1; static function foo() {} }\n"
4068                               "$o = 'c';\n"
4069                               "$o::a;\n"
4070                               "$o::foo();\n"
4071                               , DumpNone);
4072     QVERIFY(top);
4073     DUChainReleaser releaseTop(top);
4074     DUChainWriteLocker lock;
4075     QVERIFY(top->problems().empty());
4076 
4077     // we cannot support anything though :(
4078 }
4079 
4080 void TestDUChain::staticNowdoc()
4081 {
4082     //               0         1         2         3         4         5         6         7
4083     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
4084     TopDUContext* top = parse("<?php\n"
4085                               "class c {\n"
4086                               "public $bar = <<<'FOO'\nbar\nFOO;\n"
4087                               "public const C = <<<'FOO'\nbar\nFOO;\n"
4088                               "}\n"
4089                               , DumpNone);
4090     QVERIFY(top);
4091     DUChainReleaser releaseTop(top);
4092     DUChainWriteLocker lock;
4093     QVERIFY(top->problems().empty());
4094 
4095     QCOMPARE(top->childContexts().first()->localDeclarations().count(), 2);
4096     QCOMPARE(top->childContexts().first()->localDeclarations().first()->type<IntegralType>()->dataType(),
4097              static_cast<uint>(IntegralType::TypeString));
4098     QCOMPARE(top->childContexts().first()->localDeclarations().last()->type<IntegralType>()->dataType(),
4099              static_cast<uint>(IntegralType::TypeString));
4100 }
4101 
4102 void TestDUChain::curlyVarAfterObj()
4103 {
4104     // bug: https://bugs.kde.org/show_bug.cgi?id=241645
4105 
4106     //               0         1         2         3         4         5         6         7
4107     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
4108     TopDUContext* top = parse("<?php\n"
4109                               "class c {\n"
4110                               "public $bar = 'foo';\n"
4111                               "public $asdf = 'bar';\n"
4112                               "public function foo(){}\n"
4113                               "}\n"
4114                               "$a = new c;\n"
4115                               "$a->{$a->bar}();\n"
4116                               "$a->{$a->asdf};\n"
4117                               , DumpNone);
4118     QVERIFY(top);
4119 
4120     DUChainReleaser releaseTop(top);
4121     DUChainWriteLocker lock;
4122     QVERIFY(top->problems().empty());
4123 }
4124 
4125 void TestDUChain::embeddedHTML_data()
4126 {
4127     QTest::addColumn<QString>("code");
4128 
4129     QTest::newRow("if") << QStringLiteral("<?php if ( true ) : ?>\n<?php endif; ?>");
4130     QTest::newRow("elseif") << QStringLiteral("<?php if ( true ) : ?>\n<?php elseif ( false ) : ?>\n<?php endif; ?>");
4131     QTest::newRow("foreach") << QStringLiteral("<?php foreach ( array(1,2) as $i ) : ?>\n<?php endforeach; ?>\n");
4132     QTest::newRow("switch") << QStringLiteral("<?php switch ( 1 ) : case 1: ?>\n<?php break; endswitch; ?>\n");
4133     QTest::newRow("for") << QStringLiteral("<?php for ( ;; ) : ?>\n<?php endfor; ?>\n");
4134     QTest::newRow("while") << QStringLiteral("<?php while ( true ) : ?>\n<?php endwhile; ?>\n");
4135     QTest::newRow("else") << QStringLiteral("<?php if (true):\n echo 'ok1';\n else:\n echo 'ok2';\n endif; ?>");
4136 
4137 }
4138 
4139 void TestDUChain::embeddedHTML()
4140 {
4141     QFETCH(QString, code);
4142 
4143     TopDUContext* top = parse(code.toLocal8Bit(), DumpNone);
4144 
4145     QVERIFY(top);
4146 
4147     DUChainReleaser releaseTop(top);
4148     DUChainWriteLocker lock;
4149     QVERIFY(top->problems().empty());
4150 }
4151 
4152 void TestDUChain::cases()
4153 {
4154     // testcase for bug https://bugs.kde.org/show_bug.cgi?id=245832
4155     TopDUContext* top = parse("<?php switch(1) { case 1:\n case 2:\n break; default: break; }", DumpNone);
4156 
4157     QVERIFY(top);
4158 
4159     DUChainReleaser releaseTop(top);
4160     DUChainWriteLocker lock;
4161     QVERIFY(top->problems().empty());
4162 }
4163 
4164 void TestDUChain::closureParser()
4165 {
4166     // testcase for the parser after closures where introduced,
4167     // to make sure nothing brakes and all parser conflicts are resolved
4168     TopDUContext* top = parse("<?php\n"
4169                               "$lambda1 = function() {return 0;};\n"
4170                               "$lambda2 = function() use ($lambda1) {return 0;};\n"
4171                               "$lambda3 = function & () use (&$lambda2, $lambda1) {return 0;};\n"
4172                               "$lambda4 = function & ($a, &$b, stdClass $c) use (&$lambda2, $lambda1) {return 0;};\n"
4173                               "\n"
4174                               "class a {\n"
4175                               "  function foo() {}\n"
4176                               "  function & bar() {}\n"
4177                               "}\n"
4178                               "function foo() {}\n"
4179                               "function & bar() {}\n", DumpNone);
4180 
4181     QVERIFY(top);
4182 
4183     DUChainReleaser releaseTop(top);
4184     DUChainWriteLocker lock;
4185     QVERIFY(top->problems().empty());
4186 }
4187 
4188 void TestDUChain::closures()
4189 {
4190     TopDUContext* top = parse("<?php $l = function($a, stdClass $b) { return 0; };\n", DumpNone);
4191     QVERIFY(top);
4192     DUChainReleaser releaseTop(top);
4193     DUChainWriteLocker lock;
4194     QVERIFY(top->problems().isEmpty());
4195 
4196     QCOMPARE(top->localDeclarations().count(), 2);
4197     Declaration* l = top->localDeclarations().first();
4198     QCOMPARE(l->identifier().toString(), QString("l"));
4199     Declaration* closure = top->localDeclarations().last();
4200     QVERIFY(closure->identifier().isEmpty());
4201 
4202     FunctionType::Ptr funcType = closure->type<FunctionType>();
4203     QVERIFY(funcType);
4204 
4205     QCOMPARE(funcType->arguments().count(), 2);
4206     QVERIFY(funcType->arguments().at(0).dynamicCast<IntegralType>());
4207     QCOMPARE(funcType->arguments().at(0).staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed));
4208     QVERIFY(funcType->arguments().at(1).dynamicCast<StructureType>());
4209     QCOMPARE(funcType->arguments().at(1).staticCast<StructureType>()->qualifiedIdentifier().toString(), QString("stdclass"));
4210 
4211     QVERIFY(funcType->returnType().dynamicCast<IntegralType>());
4212     QCOMPARE(funcType->returnType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeInt));
4213 
4214     QVERIFY(l->abstractType()->equals(closure->abstractType().constData()));
4215 }
4216 
4217 void TestDUChain::closureEmptyUse()
4218 {
4219     // test case for: https://bugs.kde.org/show_bug.cgi?id=267105
4220     // don't crash but report parse error
4221     TopDUContext* top = parse("<?php $c = function ($v) use () { return $v > 2; };\n", DumpNone);
4222     QVERIFY(top);
4223     DUChainReleaser releaseTop(top);
4224     DUChainWriteLocker lock;
4225     QCOMPARE(top->problems().size(), 1);
4226 }
4227 
4228 void TestDUChain::iifeParser()
4229 {
4230     // testcase for bug https://bugs.kde.org/show_bug.cgi?id=370515
4231     TopDUContext* top = parse("<?php\n"
4232                               "$lambda1 = (function() {return 5;})();\n"
4233                               "$lambda2 = (function($a) {return $a;})(50);\n"
4234                               "$lambda3 = (function($a){ return function($b) use ($a){echo $a + $b;};})(50); \n"
4235                               "$lambda4 = (function ($a){echo $a;})(10) + (function ($a){echo $a ;})(20);"
4236                               , DumpNone);
4237 
4238     QVERIFY(top);
4239 
4240     DUChainReleaser releaseTop(top);
4241     DUChainWriteLocker lock;
4242     QVERIFY(top->problems().empty());
4243 }
4244 
4245 void TestDUChain::iife()
4246 {
4247     TopDUContext* top = parse("<?php $l = (function($a){ return function($b) use ($a){echo $a + $b;};})(50); \n", DumpNone);
4248     QVERIFY(top);
4249     DUChainReleaser releaseTop(top);
4250     DUChainWriteLocker lock;
4251     QVERIFY(top->problems().isEmpty());
4252     QCOMPARE(top->localDeclarations().count(), 2);
4253     Declaration* l = top->localDeclarations().first();
4254     QCOMPARE(l->identifier().toString(), QString("l"));
4255     Declaration* iife = top->localDeclarations().last();
4256     QVERIFY(iife->identifier().isEmpty());
4257 }
4258 
4259 void TestDUChain::gotoTest()
4260 {
4261     TopDUContext* top = parse("<?php goto dest; dest: \n", DumpNone);
4262     QVERIFY(top);
4263     DUChainReleaser releaseTop(top);
4264     DUChainWriteLocker lock;
4265     QVERIFY(top->problems().isEmpty());
4266 
4267     ///TODO: create declaration for destination label
4268     ///TODO: create use for goto label
4269     ///TODO: report error when trying to jump into loop or switch statement
4270 }
4271 
4272 void TestDUChain::ternary()
4273 {
4274     TopDUContext* top = parse("<?php $a = true ? 1 : 2; $b = false ?: 3; \n", DumpNone);
4275     QVERIFY(top);
4276     DUChainReleaser releaseTop(top);
4277     DUChainWriteLocker lock;
4278     QVERIFY(top->problems().isEmpty());
4279 }
4280 
4281 void TestDUChain::bug296709()
4282 {
4283     // see also: https://bugs.kde.org/show_bug.cgi?id=296709
4284     //               0         1         2         3         4         5         6         7
4285     //               01234567890123456789012345678901234567890123456789012345678901234567890123456789
4286     TopDUContext* top = parse(
4287                     "<?php\n"
4288                     "foreach(array() as $a) {\n"
4289                     "  $a[0] = 1;\n"
4290                     "}\n", DumpAll);
4291     QVERIFY(top);
4292     DUChainReleaser releaseTop(top);
4293     DUChainWriteLocker lock;
4294     QVERIFY(top->problems().isEmpty());
4295     QList< Declaration* > decs = top->findLocalDeclarations(Identifier(QStringLiteral("a")));
4296     QCOMPARE(decs.size(), 1);
4297     QCOMPARE(decs.at(0)->range(), RangeInRevision(1, 19, 1, 21));
4298     QCOMPARE(decs.at(0)->uses().count(), 1);
4299     QCOMPARE(decs.at(0)->uses().begin()->count(), 1);
4300     QCOMPARE(decs.at(0)->uses().begin()->first(), RangeInRevision(2, 2, 2, 4));
4301 }
4302 
4303 
4304 void TestDUChain::declareFinalMethod()
4305 {
4306     //                 0         1         2         3         4         5         6         7
4307     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
4308     QByteArray method("<? class A { public final function foo() {} }");
4309 
4310     TopDUContext* top = parse(method, DumpAll);
4311     DUChainReleaser releaseTop(top);
4312     DUChainWriteLocker lock(DUChain::lock());
4313 
4314     QVERIFY(!top->parentContext());
4315     QCOMPARE(top->childContexts().count(), 1);
4316 
4317     DUContext* contextClassA = top->childContexts().first();
4318 
4319     Declaration* dec = contextClassA->localDeclarations().at(0);
4320     ClassFunctionDeclaration* funDec = dynamic_cast<ClassFunctionDeclaration*>(dec);
4321     QVERIFY(funDec);
4322     QCOMPARE(funDec->qualifiedIdentifier(), QualifiedIdentifier(u"a::foo"));
4323     QVERIFY(funDec->isFinal());
4324 }
4325 
4326 void Php::TestDUChain::testTodoExtractor()
4327 {
4328     //                 0         1         2         3         4         5         6         7
4329     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
4330     QByteArray method("<?\n"
4331                       "/* TODO: bla */\n"
4332                       "/// FIXME blub");
4333 
4334     QVERIFY(KDevelop::ICore::self()->languageController()->completionSettings()->todoMarkerWords().contains("TODO"));
4335     QVERIFY(KDevelop::ICore::self()->languageController()->completionSettings()->todoMarkerWords().contains("FIXME"));
4336 
4337     TopDUContext* top = parse(method, DumpAll);
4338     DUChainReleaser releaseTop(top);
4339     DUChainWriteLocker lock(DUChain::lock());
4340 
4341     QVERIFY(top);
4342     QCOMPARE(top->problems().size(), 2);
4343     QCOMPARE(top->problems().at(0)->description(), QString("TODO: bla"));
4344     QCOMPARE(top->problems().at(0)->range(), RangeInRevision(1, 3, 1, 12));
4345     QCOMPARE(top->problems().at(1)->description(), QString("FIXME blub"));
4346     QCOMPARE(top->problems().at(1)->range(), RangeInRevision(2, 4, 2, 14));
4347 }
4348 
4349 void TestDUChain::useThisAsArray()
4350 {
4351     QByteArray method("<?php\n"
4352                       "  interface ArrayAccess{} "
4353                       "  class A implements \\ArrayAccess\n"
4354                       "  {\n"
4355                       "      $values = [];\n"
4356                       "      function offsetGet($offset) { return $this->values[$offset]; }\n"
4357                       "      function offsetSet($offset, $value) { $this->values[$offset] = $value; }\n"
4358                       "      function offsetExists($offset) { return array_key_exists($offset, $this->values); }\n"
4359                       "      function offsetUnset($offset) { unset($this->values[$offset]); }\n"
4360                       "      function setTest() { $this['test'] = 'test'; }  \n"
4361                       "  }\n");
4362     
4363     TopDUContext* top = parse(method);
4364     QVERIFY(top);
4365     DUChainReleaser releaseTop(top);
4366     DUChainWriteLocker lock(DUChain::lock());
4367     
4368     QCOMPARE(top->importedParentContexts().count(), 1);
4369     QVERIFY(DUChain::self()->chainForDocument(internalFunctionFile()));
4370     QCOMPARE(DUChain::self()->chainForDocument(internalFunctionFile()), top->importedParentContexts().first().context(top));
4371     
4372     QVERIFY(top->problems().isEmpty());
4373 }
4374 
4375 void TestDUChain::wrongUseOfThisAsArray()
4376 {
4377     // missing functions from \ArrayAccess and not declared abstract
4378     QByteArray method("<?php\n"
4379                       "  interface ArrayAccess{} "
4380                       "  class A implements \\ArrayAccess\n"
4381                       "  {\n"
4382                       "      public function setTest() { $this['test'] = 'test'; }  \n"
4383                       "  }\n");
4384 
4385     TopDUContext* top = parse(method);
4386     QVERIFY(top);
4387     DUChainReleaser releaseTop(top);
4388     DUChainWriteLocker lock(DUChain::lock());
4389     
4390     QCOMPARE(top->problems().size(),1);
4391 }
4392 
4393 void TestDUChain::staticFunctionClassPhp54()
4394 {
4395     QByteArray method("<?php\n"
4396                       "  class A\n"
4397                       "  {\n"
4398                       "      public static function func() {}  \n"
4399                       "  }\n"
4400                       " A::{'func'}();  \n");
4401 
4402     TopDUContext* top = parse(method);
4403     QVERIFY(top);
4404     DUChainReleaser releaseTop(top);
4405     DUChainWriteLocker lock(DUChain::lock());
4406 
4407     QVERIFY(top->problems().isEmpty());
4408     QCOMPARE(top->localDeclarations().count(),1);
4409 
4410     Declaration* dec = top->localDeclarations().at(0);
4411     ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec);
4412     QCOMPARE(classDec->uses().count(),1);
4413 }
4414 
4415 void TestDUChain::functionArgumentUnpacking_data()
4416 {
4417     QTest::addColumn<QString>("code");
4418 
4419     QTest::newRow("unpackVariable") << QStringLiteral("<? $a = [ 1,2 ];\n$b = [ 3,4 ];\nfunction aaa($c,$d,$e,$f) { }\naaa(...$a, ...$b);\n");
4420 
4421     QTest::newRow("unpackLiteral") << QStringLiteral("<? function aaa($c,$d,$e,$f) { }\naaa(...[1,2], ...[3,4]);\n");
4422 
4423     QTest::newRow("unpackFunction") << QStringLiteral("<? function bbb() { return [1,2,3,4]; }\nfunction aaa($c,$d,$e,$f) { }\naaa(...bbb());\n");
4424 }
4425 
4426 void TestDUChain::functionArgumentUnpacking()
4427 {
4428     QFETCH(QString, code);
4429 
4430     TopDUContext* top = parse(code.toUtf8(), DumpNone);
4431     QVERIFY(top);
4432     DUChainReleaser releaseTop(top);
4433     DUChainWriteLocker lock(DUChain::lock());
4434 
4435     QVERIFY(top->problems().isEmpty());
4436 }
4437 
4438 void TestDUChain::illegalExpression_data()
4439 {
4440     QTest::addColumn<QString>("code");
4441 
4442     QTest::newRow("equality expression") << QStringLiteral("<? 1 == '1' == 1.1;\n");
4443     QTest::newRow("relational expression") << QStringLiteral("<? 3 > 2 > 1;\n");
4444     QTest::newRow("double print expression") << QStringLiteral("<? print 1 print 2;\n");
4445     QTest::newRow("standalone print statement") << QStringLiteral("<? print;\n");
4446     QTest::newRow("expression inside isset()") << QStringLiteral("<? isset($a || $b);\n");
4447     QTest::newRow("instanceof with dynamic property of static class") << QStringLiteral("<? $a instanceof A->foo;\n");
4448     QTest::newRow("instanceof with class constant") << QStringLiteral("<? $a instanceof A::FOO;\n");
4449     QTest::newRow("instanceof with class method") << QStringLiteral("<? $a instanceof $b->foo();\n");
4450     QTest::newRow("instanceof with static method") << QStringLiteral("<? $a instanceof A::foo();\n");
4451 }
4452 
4453 void TestDUChain::illegalExpression()
4454 {
4455     QFETCH(QString, code);
4456 
4457     TopDUContext* top = parse(code.toUtf8(), DumpNone);
4458     QVERIFY(!top);
4459 }
4460 
4461 void TestDUChain::printExpression_data()
4462 {
4463     QTest::addColumn<QString>("code");
4464 
4465     QTest::newRow("simple print expression") << QStringLiteral("<? print 1;\n");
4466     QTest::newRow("print assignment") << QStringLiteral("<? print $a = 1;\n");
4467     QTest::newRow("print boolean expression") << QStringLiteral("<? print $a || $b;\n");
4468     QTest::newRow("double print token") << QStringLiteral("<? print print 1;\n");
4469     QTest::newRow("concatenated print expression") << QStringLiteral("<? print 1 . print 2;\n");
4470     QTest::newRow("boolean-chained print expression") << QStringLiteral("<? print 1 || print 2 && print 3;\n");
4471     QTest::newRow("ternary-chained print expression") << QStringLiteral("<? print 1 ? print 2 : print 3;\n");
4472     QTest::newRow("assignment-chained print expression") << QStringLiteral("<? print $a = $b += print 1;\n");
4473     QTest::newRow("null-coalesce-chained print expression") << QStringLiteral("<? print $a ?? print 1;\n");
4474     QTest::newRow("bit-chained print expression") << QStringLiteral("<? print 1 | print 2 & print 3 ^ print 4;\n");
4475     QTest::newRow("include print expression") << QStringLiteral("<? include print 'string';\n");
4476     QTest::newRow("print expression inside empty()") << QStringLiteral("<? empty(print 1);\n");
4477 }
4478 
4479 void TestDUChain::printExpression()
4480 {
4481     QFETCH(QString, code);
4482 
4483     TopDUContext* top = parse(code.toUtf8(), DumpNone);
4484     QVERIFY(top);
4485     DUChainReleaser releaseTop(top);
4486     DUChainWriteLocker lock(DUChain::lock());
4487 
4488     QVERIFY(top->problems().isEmpty());
4489 }
4490 
4491 void TestDUChain::simpleExpression_data()
4492 {
4493     QTest::addColumn<QString>("code");
4494 
4495     QTest::newRow("string concat") << QStringLiteral("<? $var = 'string ' . 'concat';\n");
4496     QTest::newRow("variable concat") << QStringLiteral("<? $foo = 'concat'; $var = 'string ' . $foo;\n");
4497     QTest::newRow("variable array concat") << QStringLiteral("<? $arr = [ 'concat' ]; $var = 'string ' . $arr[1];\n");
4498     QTest::newRow("constant concat") << QStringLiteral("<? const FOO = 'concat'; $var = 'string ' . FOO;\n");
4499     QTest::newRow("constant array concat") << QStringLiteral("<? const ARR = [ 'concat' ]; $var = 'string ' . ARR[1];\n");
4500 }
4501 
4502 void TestDUChain::simpleExpression()
4503 {
4504     QFETCH(QString, code);
4505 
4506     TopDUContext* top = parse(code.toUtf8(), DumpNone);
4507     QVERIFY(top);
4508     DUChainReleaser releaseTop(top);
4509     DUChainWriteLocker lock(DUChain::lock());
4510 
4511     QVERIFY(top->problems().isEmpty());
4512 }
4513 
4514 void TestDUChain::generatorAssignment()
4515 {
4516     //Note: in practice, Generator is defined by php, but this class is not loaded in this test, so define it ourselves
4517     //                 0         1         2         3         4         5         6         7
4518     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
4519     QByteArray method("<? class Generator {} function foo() { $a = yield 1; }\n");
4520 
4521     TopDUContext* top = parse(method, DumpNone);
4522     QVERIFY(top);
4523     DUChainReleaser releaseTop(top);
4524     DUChainWriteLocker lock(DUChain::lock());
4525 
4526     QVERIFY(!top->parentContext());
4527     QCOMPARE(top->childContexts().count(), 3);
4528     QCOMPARE(top->localDeclarations().count(), 2);
4529 
4530     Declaration* dec = top->localDeclarations().at(1);
4531     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
4532     FunctionType::Ptr functionType = dec->type<FunctionType>();
4533     QVERIFY(functionType);
4534     auto retType = functionType->returnType().dynamicCast<StructureType>();
4535     QVERIFY(retType);
4536     QCOMPARE(retType->qualifiedIdentifier(), QualifiedIdentifier(u"generator"));
4537 
4538     dec = top->childContexts().at(2)->localDeclarations().at(0);
4539     QCOMPARE(dec->identifier(), Identifier(u"a"));
4540     IntegralType::Ptr type = dec->type<IntegralType>();
4541     QVERIFY(type);
4542     QVERIFY(type->dataType() == IntegralType::TypeMixed);
4543 }
4544 
4545 void TestDUChain::generatorClosure()
4546 {
4547     //Note: in practice, Generator is defined by php, but this class is not loaded in this test, so define it ourselves
4548     //                 0         1         2         3         4         5         6         7
4549     //                 01234567890123456789012345678901234567890123456789012345678901234567890123456789
4550     QByteArray method("<? class Generator {} function foo() { $a = function() { $b = function() { yield 1; }; }; }\n");
4551 
4552     TopDUContext* top = parse(method, DumpNone);
4553     QVERIFY(top);
4554     DUChainReleaser releaseTop(top);
4555     DUChainWriteLocker lock(DUChain::lock());
4556 
4557     QVERIFY(!top->parentContext());
4558     QCOMPARE(top->childContexts().count(), 3);
4559     QCOMPARE(top->localDeclarations().count(), 2);
4560 
4561     Declaration* dec = top->localDeclarations().at(1);
4562     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier(u"foo"));
4563     FunctionType::Ptr functionType = dec->type<FunctionType>();
4564     QVERIFY(functionType);
4565     auto retType = functionType->returnType().dynamicCast<IntegralType>();
4566     QVERIFY(retType);
4567     QVERIFY(retType->dataType() == IntegralType::TypeVoid);
4568 
4569     dec = top->childContexts().at(2)->localDeclarations().at(0);
4570     QCOMPARE(dec->identifier(), Identifier(u"a"));
4571 
4572     Declaration* closure = top->childContexts().at(2)->localDeclarations().at(1);
4573     QVERIFY(closure->identifier().isEmpty());
4574 
4575     FunctionType::Ptr funcType = closure->type<FunctionType>();
4576     QVERIFY(funcType);
4577     QVERIFY(funcType->returnType().dynamicCast<IntegralType>());
4578     QCOMPARE(funcType->returnType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeVoid));
4579 
4580     QVERIFY(dec->abstractType()->equals(closure->abstractType().constData()));
4581 
4582     dec = top->childContexts().at(2)->childContexts().at(1)->localDeclarations().at(0);
4583     QCOMPARE(dec->identifier(), Identifier(u"b"));
4584 
4585     closure = top->childContexts().at(2)->childContexts().at(1)->localDeclarations().at(1);
4586     QVERIFY(closure->identifier().isEmpty());
4587 
4588     funcType = closure->type<FunctionType>();
4589     QVERIFY(funcType);
4590     auto generatorType = funcType->returnType().dynamicCast<StructureType>();
4591     QVERIFY(generatorType);
4592     QCOMPARE(generatorType->qualifiedIdentifier(), QualifiedIdentifier(u"generator"));
4593 
4594     QVERIFY(dec->abstractType()->equals(closure->abstractType().constData()));
4595 }
4596 
4597 void TestDUChain::propertyUnionType()
4598 {
4599     //                 0         1         2         3         4         5
4600     //                 012345678901234567890123456789012345678901234567890
4601     QByteArray method("<? class A { public string|int|float|bool $foo; }");
4602 
4603     TopDUContext* top = parse(method, DumpAll);
4604     DUChainReleaser releaseTop(top);
4605 
4606     DUChainWriteLocker lock(DUChain::lock());
4607 
4608     StructureType::Ptr a = top->localDeclarations().first()->type<StructureType>();
4609     QVERIFY(a);
4610 
4611     QCOMPARE(top->childContexts().first()->localDeclarations().count(), 1);
4612 
4613     UnsureType::Ptr type = top->childContexts().first()->localDeclarations().first()->type<UnsureType>();
4614 
4615     QCOMPARE(type->typesSize(), 4u);
4616     QVERIFY(type->types()[0].abstractType().dynamicCast<IntegralType>());
4617     QVERIFY(type->types()[0].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeString);
4618     QVERIFY(type->types()[1].abstractType().dynamicCast<IntegralType>());
4619     QVERIFY(type->types()[1].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt);
4620     QVERIFY(type->types()[2].abstractType().dynamicCast<IntegralType>());
4621     QVERIFY(type->types()[2].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeFloat);
4622     QVERIFY(type->types()[3].abstractType().dynamicCast<IntegralType>());
4623     QVERIFY(type->types()[3].abstractType().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean);
4624 }
4625 
4626 #include "moc_duchain.cpp"