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"