Warning, file /kdevelop/kdev-php/duchain/tests/expressionparser.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 "expressionparser.h" 0008 0009 #include <QtTest> 0010 0011 #include <language/duchain/parsingenvironment.h> 0012 #include <language/duchain/problem.h> 0013 #include <language/duchain/duchainlock.h> 0014 #include <language/duchain/topducontext.h> 0015 #include <language/duchain/types/functiontype.h> 0016 #include <language/duchain/types/integraltype.h> 0017 #include <language/duchain/declaration.h> 0018 0019 #include "../types/structuretype.h" 0020 #include "../expressionparser.h" 0021 0022 using namespace KDevelop; 0023 0024 QTEST_MAIN(Php::TestExpressionParser) 0025 0026 namespace Php 0027 { 0028 0029 TestExpressionParser::TestExpressionParser() 0030 { 0031 } 0032 0033 void TestExpressionParser::newClass_data() 0034 { 0035 QTest::addColumn<QString>("code"); 0036 0037 QTest::newRow("normalSyntax") << "<? class A { function __construct($a){} function foo() {} } $i = new A(1);"; 0038 0039 QTest::newRow("trailingCommaInConstructor") << "<? class A { function __construct($a,){} function foo() {} } $i = new A(1);"; 0040 0041 QTest::newRow("trailingCommaInInstantiation") << "<? class A { function __construct($a,){} function foo() {} } $i = new A(1,);"; 0042 } 0043 0044 void TestExpressionParser::newClass() 0045 { 0046 QFETCH(QString, code); 0047 0048 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0049 DUChainReleaser releaseTop(top); 0050 DUChainWriteLocker lock(DUChain::lock()); 0051 0052 QVERIFY(top->problems().isEmpty()); 0053 0054 ExpressionParser p(true); 0055 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i"), DUContextPointer(top), CursorInRevision(1, 0)); 0056 QVERIFY(res.type()); 0057 QCOMPARE(res.type().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a")); 0058 } 0059 0060 void TestExpressionParser::newSelf() 0061 { 0062 // 0 1 2 3 4 5 6 7 0063 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0064 QByteArray method("<? class A { function self() { } }"); 0065 0066 TopDUContext* top = parse(method, DumpNone); 0067 DUChainReleaser releaseTop(top); 0068 DUChainWriteLocker lock(DUChain::lock()); 0069 0070 ExpressionParser p(true); 0071 ExpressionEvaluationResult res = p.evaluateType( QByteArray("new self()"), 0072 DUContextPointer(top->childContexts().first()->childContexts().last()), 0073 CursorInRevision(0, 30)); 0074 QVERIFY(res.type()); 0075 QCOMPARE(res.type().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a")); 0076 } 0077 0078 void TestExpressionParser::newStatic() 0079 { 0080 // 0 1 2 3 4 5 6 7 0081 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0082 QByteArray method("<? class A { function self() { } }"); 0083 0084 TopDUContext* top = parse(method, DumpNone); 0085 DUChainReleaser releaseTop(top); 0086 DUChainWriteLocker lock; 0087 0088 ExpressionParser p(true); 0089 ExpressionEvaluationResult res = p.evaluateType( QByteArray("new static()"), 0090 DUContextPointer(top->childContexts().first()->childContexts().last()), 0091 CursorInRevision(0, 30)); 0092 QVERIFY(res.type().dynamicCast<StructureType>()); 0093 QCOMPARE(res.type().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a")); 0094 } 0095 0096 void TestExpressionParser::memberVariable() 0097 { 0098 // 0 1 2 3 4 5 6 7 0099 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0100 QByteArray method("<? class A { /** @var A **/ public $foo; } $i = new A();"); 0101 0102 TopDUContext* top = parse(method, DumpNone); 0103 DUChainReleaser releaseTop(top); 0104 DUChainWriteLocker lock(DUChain::lock()); 0105 0106 ExpressionParser p(true); 0107 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i->foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0108 QVERIFY(res.type()); 0109 QCOMPARE(res.allDeclarations().count(), 1); 0110 QCOMPARE(res.allDeclarations().first().data(), top->childContexts().first()->localDeclarations().first()); 0111 QCOMPARE(res.type().staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier(u"a")); 0112 } 0113 void TestExpressionParser::memberFunction() 0114 { 0115 // 0 1 2 3 4 5 6 7 0116 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0117 QByteArray method("<? class A { public function foo() {} } $i = new A();"); 0118 0119 TopDUContext* top = parse(method, DumpAll); 0120 DUChainReleaser releaseTop(top); 0121 DUChainWriteLocker lock(DUChain::lock()); 0122 0123 ExpressionParser p(true); 0124 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i->foo()"), DUContextPointer(top), CursorInRevision(1, 0)); 0125 QVERIFY(res.type()); 0126 QVERIFY(res.type().dynamicCast<IntegralType>()); 0127 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeVoid); 0128 QCOMPARE(res.allDeclarations().size(), 1); 0129 QCOMPARE(res.allDeclarations().first().data(), top->childContexts().first()->localDeclarations().first()); 0130 } 0131 0132 void TestExpressionParser::newTrait() 0133 { 0134 // 0 1 2 3 4 5 6 7 0135 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0136 QByteArray method("<? trait A { function foo() {} }"); 0137 0138 TopDUContext* top = parse(method, DumpNone); 0139 DUChainReleaser releaseTop(top); 0140 DUChainWriteLocker lock(DUChain::lock()); 0141 0142 QVERIFY(top->problems().isEmpty()); 0143 0144 QCOMPARE(top->childContexts().size(), 1); 0145 QVERIFY(top->childContexts().at(0)->type() == DUContext::Class); 0146 } 0147 0148 void TestExpressionParser::newTraitWithAbstractMethod() 0149 { 0150 // 0 1 2 3 4 5 6 7 0151 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0152 QByteArray method("<? trait A { public abstract function foo(); }"); 0153 0154 TopDUContext* top = parse(method, DumpNone); 0155 DUChainReleaser releaseTop(top); 0156 DUChainWriteLocker lock(DUChain::lock()); 0157 0158 QVERIFY(top->problems().isEmpty()); 0159 0160 QCOMPARE(top->childContexts().size(), 1); 0161 QVERIFY(top->childContexts().at(0)->type() == DUContext::Class); 0162 } 0163 0164 void TestExpressionParser::invalidTrait_data() 0165 { 0166 QTest::addColumn<QString>("code"); 0167 0168 QTest::newRow("constant") << "<? trait A { const FOO = ''; }\n"; 0169 } 0170 0171 void TestExpressionParser::invalidTrait() 0172 { 0173 QFETCH(QString, code); 0174 0175 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0176 DUChainReleaser releaseTop(top); 0177 DUChainWriteLocker lock; 0178 0179 QVERIFY(!top->problems().isEmpty()); 0180 } 0181 0182 void TestExpressionParser::invalidTraitUse_data() 0183 { 0184 QTest::addColumn<QString>("code"); 0185 0186 QTest::newRow("abstractModifier") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as abstract bla; } }\n"; 0187 0188 QTest::newRow("staticModifier") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as static bla; } }\n"; 0189 0190 QTest::newRow("finalModifier") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as final bla; } }\n"; 0191 0192 QTest::newRow("abstract") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as abstract; } }\n"; 0193 0194 QTest::newRow("static") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as static; } }\n"; 0195 0196 QTest::newRow("final") << "<? trait A { public function foo(){} } class Foo { use A { A::foo as final; } }\n"; 0197 0198 QTest::newRow("traitMethodCollision") << "<? trait A { public function foo(){} } trait B { public function foo(){} } class Foo { use A,B; }\n"; 0199 0200 QTest::newRow("propertyCollision") << "<? trait A { public $arg; } class Foo { use A; public $arg; }\n"; 0201 } 0202 0203 void TestExpressionParser::invalidTraitUse() 0204 { 0205 QFETCH(QString, code); 0206 0207 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0208 DUChainReleaser releaseTop(top); 0209 DUChainWriteLocker lock; 0210 0211 QVERIFY(!top->problems().isEmpty()); 0212 } 0213 0214 void TestExpressionParser::namespaceUseNameConflict() 0215 { 0216 QByteArray alias("<?php namespace Bar { class Foo {} use Foo; }\n"); 0217 0218 TopDUContext* top = parse(alias, DumpNone); 0219 DUChainReleaser releaseTop(top); 0220 DUChainWriteLocker lock; 0221 0222 QVERIFY(!top->problems().isEmpty()); 0223 } 0224 0225 void TestExpressionParser::globalFunction() 0226 { 0227 // 0 1 2 3 4 5 6 7 0228 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0229 QByteArray method("<? function foo() {}"); 0230 0231 TopDUContext* top = parse(method, DumpNone); 0232 DUChainReleaser releaseTop(top); 0233 DUChainWriteLocker lock(DUChain::lock()); 0234 0235 ExpressionParser p(true); 0236 ExpressionEvaluationResult res = p.evaluateType(QByteArray("foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0237 QVERIFY(res.type()); 0238 QVERIFY(res.type().dynamicCast<FunctionType>()); 0239 QCOMPARE(res.allDeclarations().count(), 1); 0240 QCOMPARE(res.allDeclarations().first().data(), top->localDeclarations().first()); 0241 } 0242 0243 void TestExpressionParser::globalFunctionCall_data() 0244 { 0245 QTest::addColumn<QString>("code"); 0246 0247 QTest::newRow("normalSyntax") << "<? function foo($a) {} foo(1);"; 0248 0249 QTest::newRow("trailingComma") << "<? function foo($a) {} foo(1,);"; 0250 } 0251 0252 void TestExpressionParser::globalFunctionCall() 0253 { 0254 // 0 1 2 3 4 5 6 7 0255 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0256 QFETCH(QString, code); 0257 0258 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0259 DUChainReleaser releaseTop(top); 0260 DUChainWriteLocker lock(DUChain::lock()); 0261 0262 QVERIFY(top->problems().isEmpty()); 0263 0264 ExpressionParser p(true); 0265 ExpressionEvaluationResult res = p.evaluateType(QByteArray("foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0266 QVERIFY(res.type()); 0267 QVERIFY(res.type().dynamicCast<FunctionType>()); 0268 QCOMPARE(res.allDeclarations().count(), 1); 0269 QCOMPARE(res.allDeclarations().first().data(), top->localDeclarations().first()); 0270 } 0271 0272 void TestExpressionParser::chainCall() 0273 { 0274 // 0 1 2 3 4 5 6 7 0275 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0276 QByteArray method("<? class A { function foo() { return $this; } } $a = new A();"); 0277 0278 TopDUContext* top = parse(method, DumpAll); 0279 DUChainReleaser releaseTop(top); 0280 DUChainWriteLocker lock(DUChain::lock()); 0281 0282 FunctionType::Ptr fn = top->childContexts().first()->localDeclarations().first()->type<FunctionType>(); 0283 QVERIFY(fn); 0284 QVERIFY(fn->returnType()->equals(top->localDeclarations().first()->abstractType().data())); 0285 0286 ExpressionParser p(true); 0287 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$a->foo()"), DUContextPointer(top), CursorInRevision(1, 0)); 0288 QVERIFY(res.type()); 0289 QVERIFY(res.type()->equals(top->localDeclarations().first()->abstractType().data())); 0290 0291 res = p.evaluateType(QByteArray("$a->foo()->foo()->foo()"), DUContextPointer(top), CursorInRevision(1, 0)); 0292 QVERIFY(res.type()); 0293 QVERIFY(res.type()->equals(top->localDeclarations().first()->abstractType().data())); 0294 } 0295 void TestExpressionParser::thisObject() 0296 { 0297 // 0 1 2 3 4 5 6 7 0298 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0299 QByteArray method("<? class A { public function foo() {} }"); 0300 0301 TopDUContext* top = parse(method, DumpNone); 0302 DUChainReleaser releaseTop(top); 0303 DUChainWriteLocker lock(DUChain::lock()); 0304 0305 DUContext* funContext = top->childContexts().first()->localDeclarations().first()->internalContext(); 0306 ExpressionParser p(true); 0307 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$this"), DUContextPointer(funContext), CursorInRevision(1, 0)); 0308 QCOMPARE(res.allDeclarations().count(), 1); 0309 QCOMPARE(res.allDeclarations().first().data(), top->localDeclarations().first()); 0310 QVERIFY(res.type()); 0311 QVERIFY(res.type().dynamicCast<StructureType>()); 0312 QCOMPARE(res.type().staticCast<StructureType>()->declaration(top), top->localDeclarations().first()); 0313 } 0314 0315 void TestExpressionParser::integralTypes() 0316 { 0317 // 0 1 2 3 4 5 6 7 0318 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0319 QByteArray method("<? $foo=1;"); 0320 0321 TopDUContext* top = parse(method, DumpNone); 0322 DUChainReleaser releaseTop(top); 0323 DUChainWriteLocker lock(DUChain::lock()); 0324 0325 ExpressionParser p(true); 0326 0327 ExpressionEvaluationResult res = p.evaluateType(QByteArray("123"), DUContextPointer(top), CursorInRevision(1, 0)); 0328 QVERIFY(res.type().dynamicCast<IntegralType>()); 0329 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeInt)); 0330 0331 res = p.evaluateType(QByteArray("123.1"), DUContextPointer(top), CursorInRevision(1, 0)); 0332 QVERIFY(res.type().dynamicCast<IntegralType>()); 0333 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeFloat)); 0334 0335 res = p.evaluateType(QByteArray("\"asdf\""), DUContextPointer(top), CursorInRevision(1, 0)); 0336 QVERIFY(res.type().dynamicCast<IntegralType>()); 0337 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0338 0339 res = p.evaluateType(QByteArray("\"as $foo df\""), DUContextPointer(top), CursorInRevision(1, 0)); 0340 QVERIFY(res.type().dynamicCast<IntegralType>()); 0341 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0342 0343 res = p.evaluateType(QByteArray("'asdf'"), DUContextPointer(top), CursorInRevision(1, 0)); 0344 QVERIFY(res.type().dynamicCast<IntegralType>()); 0345 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0346 0347 res = p.evaluateType(QByteArray("true"), DUContextPointer(top), CursorInRevision(1, 0)); 0348 QVERIFY(res.type().dynamicCast<IntegralType>()); 0349 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeBoolean)); 0350 0351 res = p.evaluateType(QByteArray("TRUE"), DUContextPointer(top), CursorInRevision(1, 0)); 0352 QVERIFY(res.type().dynamicCast<IntegralType>()); 0353 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeBoolean)); 0354 0355 res = p.evaluateType(QByteArray("null"), DUContextPointer(top), CursorInRevision(1, 0)); 0356 QVERIFY(res.type().dynamicCast<IntegralType>()); 0357 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeNull)); 0358 0359 res = p.evaluateType(QByteArray("NULL"), DUContextPointer(top), CursorInRevision(1, 0)); 0360 QVERIFY(res.type().dynamicCast<IntegralType>()); 0361 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeNull)); 0362 } 0363 0364 void TestExpressionParser::newObject() 0365 { 0366 // 0 1 2 3 4 5 6 7 0367 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0368 QByteArray method("<? class A {} "); 0369 0370 TopDUContext* top = parse(method, DumpNone); 0371 DUChainReleaser releaseTop(top); 0372 DUChainWriteLocker lock(DUChain::lock()); 0373 0374 ExpressionParser p(true); 0375 0376 ExpressionEvaluationResult res = p.evaluateType(QByteArray("new A();"), DUContextPointer(top), CursorInRevision(1, 0)); 0377 QVERIFY(res.type().dynamicCast<StructureType>()); 0378 QCOMPARE(res.type().staticCast<StructureType>()->declaration(top), top->localDeclarations().first()); 0379 } 0380 0381 void TestExpressionParser::cast() 0382 { 0383 // 0 1 2 3 4 5 6 7 0384 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0385 QByteArray method("<? $foo = 1; "); 0386 0387 TopDUContext* top = parse(method, DumpNone); 0388 DUChainReleaser releaseTop(top); 0389 DUChainWriteLocker lock(DUChain::lock()); 0390 0391 ExpressionParser p(true); 0392 0393 ExpressionEvaluationResult res = p.evaluateType(QByteArray("(string)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0394 QVERIFY(res.type().dynamicCast<IntegralType>()); 0395 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeString); 0396 0397 res = p.evaluateType(QByteArray("(int)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0398 QVERIFY(res.type().dynamicCast<IntegralType>()); 0399 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0400 0401 res = p.evaluateType(QByteArray("(double)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0402 QVERIFY(res.type().dynamicCast<IntegralType>()); 0403 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeFloat); 0404 0405 res = p.evaluateType(QByteArray("(bool)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0406 QVERIFY(res.type().dynamicCast<IntegralType>()); 0407 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean); 0408 0409 res = p.evaluateType(QByteArray("(array)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0410 QVERIFY(res.type().dynamicCast<IntegralType>()); 0411 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeArray); 0412 0413 res = p.evaluateType(QByteArray("(object)$foo"), DUContextPointer(top), CursorInRevision(1, 0)); 0414 QVERIFY(res.type().dynamicCast<StructureType>()); 0415 QVERIFY(res.type().staticCast<StructureType>()->qualifiedIdentifier() == QualifiedIdentifier(u"stdclass")); 0416 } 0417 0418 void TestExpressionParser::operations() 0419 { 0420 // 0 1 2 3 4 5 6 7 0421 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0422 QByteArray method("<? $foo = 1; "); 0423 0424 TopDUContext* top = parse(method, DumpNone); 0425 DUChainReleaser releaseTop(top); 0426 DUChainWriteLocker lock(DUChain::lock()); 0427 0428 ExpressionParser p(true); 0429 0430 ExpressionEvaluationResult res = p.evaluateType(QByteArray("'1' . '1'"), DUContextPointer(top), CursorInRevision(1, 0)); 0431 QVERIFY(res.type().dynamicCast<IntegralType>()); 0432 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeString); 0433 0434 res = p.evaluateType(QByteArray("1 . 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0435 QVERIFY(res.type().dynamicCast<IntegralType>()); 0436 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeString); 0437 0438 res = p.evaluateType(QByteArray("1 + 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0439 QVERIFY(res.type().dynamicCast<IntegralType>()); 0440 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0441 0442 res = p.evaluateType(QByteArray("1 ** 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0443 QVERIFY(res.type().dynamicCast<IntegralType>()); 0444 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0445 0446 res = p.evaluateType(QByteArray("'1' + '1'"), DUContextPointer(top), CursorInRevision(1, 0)); 0447 QVERIFY(res.type().dynamicCast<IntegralType>()); 0448 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0449 0450 res = p.evaluateType(QByteArray("$foo .= '1'"), DUContextPointer(top), CursorInRevision(1, 0)); 0451 QVERIFY(res.type().dynamicCast<IntegralType>()); 0452 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeString); 0453 0454 res = p.evaluateType(QByteArray("$foo .= 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0455 QVERIFY(res.type().dynamicCast<IntegralType>()); 0456 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeString); 0457 0458 res = p.evaluateType(QByteArray("$foo += 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0459 QVERIFY(res.type().dynamicCast<IntegralType>()); 0460 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0461 0462 res = p.evaluateType(QByteArray("$foo += '1'"), DUContextPointer(top), CursorInRevision(1, 0)); 0463 QVERIFY(res.type().dynamicCast<IntegralType>()); 0464 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0465 0466 res = p.evaluateType(QByteArray("$foo *= 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0467 QVERIFY(res.type().dynamicCast<IntegralType>()); 0468 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0469 0470 res = p.evaluateType(QByteArray("$foo *= '1'"), DUContextPointer(top), CursorInRevision(1, 0)); 0471 QVERIFY(res.type().dynamicCast<IntegralType>()); 0472 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0473 0474 res = p.evaluateType(QByteArray("$foo **= 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0475 QVERIFY(res.type().dynamicCast<IntegralType>()); 0476 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0477 0478 res = p.evaluateType(QByteArray("$foo = 3 > 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0479 QVERIFY(res.type().dynamicCast<IntegralType>()); 0480 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean); 0481 0482 res = p.evaluateType(QByteArray("$foo = 3 != 1"), DUContextPointer(top), CursorInRevision(1, 0)); 0483 QVERIFY(res.type().dynamicCast<IntegralType>()); 0484 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean); 0485 0486 res = p.evaluateType(QByteArray("$foo = $a instanceof A"), DUContextPointer(top), CursorInRevision(1, 0)); 0487 QVERIFY(res.type().dynamicCast<IntegralType>()); 0488 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeBoolean); 0489 0490 res = p.evaluateType(QByteArray("$foo = 3 <=> 2"), DUContextPointer(top), CursorInRevision(1, 0)); 0491 QVERIFY(res.type().dynamicCast<IntegralType>()); 0492 QVERIFY(res.type().staticCast<IntegralType>()->dataType() == IntegralType::TypeInt); 0493 } 0494 0495 void TestExpressionParser::findArg_data() 0496 { 0497 QTest::addColumn<QString>("code"); 0498 0499 QTest::newRow("normalSyntax") << "<? class A{} function foo($arg, &$bar, A &$a) { } "; 0500 0501 QTest::newRow("trailingComma") << "<? class A{} function foo($arg, &$bar, A &$a,) { } "; 0502 } 0503 0504 void TestExpressionParser::findArg() 0505 { 0506 // 0 1 2 3 4 5 6 7 0507 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0508 QFETCH(QString, code); 0509 0510 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0511 DUChainReleaser releaseTop(top); 0512 DUChainWriteLocker lock(DUChain::lock()); 0513 0514 QVERIFY(top->problems().isEmpty()); 0515 0516 ExpressionParser p(true); 0517 0518 QCOMPARE(top->childContexts().size(), 3); 0519 QVERIFY(top->childContexts().at(0)->type() == DUContext::Class); 0520 QVERIFY(top->childContexts().at(1)->type() == DUContext::Function); 0521 QVERIFY(top->childContexts().at(2)->type() != DUContext::Function); 0522 0523 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$arg"), DUContextPointer(top->childContexts().last()), 0524 CursorInRevision(0, 47)); 0525 QVERIFY(res.type().dynamicCast<IntegralType>()); 0526 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0527 0528 res = p.evaluateType(QByteArray("$bar"), DUContextPointer(top->childContexts().last()), 0529 CursorInRevision(0, 47)); 0530 auto type = res.type().dynamicCast<ReferenceType>(); 0531 QVERIFY(type); 0532 QVERIFY(type->baseType().dynamicCast<IntegralType>()); 0533 QCOMPARE(type->baseType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0534 0535 res = p.evaluateType(QByteArray("$a"), DUContextPointer(top->childContexts().last()), 0536 CursorInRevision(0, 47)); 0537 type = res.type().dynamicCast<ReferenceType>(); 0538 QVERIFY(type); 0539 QVERIFY(type->baseType().dynamicCast<StructureType>()); 0540 QCOMPARE(type->baseType().staticCast<StructureType>()->declaration(top), top->localDeclarations().first()); 0541 } 0542 0543 void TestExpressionParser::array_data() 0544 { 0545 QTest::addColumn<QString>("code"); 0546 0547 QTest::newRow("normalSyntax") << "<? $a = array(1,2,3);\n"; 0548 0549 QTest::newRow("shortSyntax") << "<? $a = [1,2,3];\n"; 0550 0551 QTest::newRow("staticNormalSyntax") << "<? static $a = array(1,2,3);\n"; 0552 0553 QTest::newRow("staticShortSyntax") << "<? static $a = [1,2,3];\n"; 0554 } 0555 0556 void TestExpressionParser::array() 0557 { 0558 // see bug https://bugs.kde.org/show_bug.cgi?id=237110 0559 0560 // 0 1 2 3 4 5 6 7 0561 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0562 QFETCH(QString, code); 0563 0564 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0565 DUChainReleaser releaseTop(top); 0566 DUChainWriteLocker lock; 0567 0568 QVERIFY(top->problems().isEmpty()); 0569 0570 ExpressionParser p(true); 0571 QCOMPARE(top->localDeclarations().first()->abstractType().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeArray)); 0572 0573 ExpressionEvaluationResult res = p.evaluateType("$b = $a[0]", DUContextPointer(top), CursorInRevision(0, 22)); 0574 QVERIFY(res.type().dynamicCast<IntegralType>()); 0575 QEXPECT_FAIL("", "we'd need advanced array support to know that [0] returns a string...", Continue); 0576 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0577 // fallback 0578 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0579 } 0580 0581 void TestExpressionParser::arrayFunctionDereferencing_data() 0582 { 0583 QTest::addColumn<QString>("code"); 0584 0585 QTest::newRow("globalFunction") << "<? function foo() { return [1, 2]; }\n" 0586 "$a = foo()[0];\n"; 0587 0588 QTest::newRow("classMethod") << "<? class foo{ function bar() { return [1, 2]; } }\n" 0589 "$obj = new foo(); $a = $obj->bar()[0];\n"; 0590 0591 QTest::newRow("staticClassMethod") << "<? class foo{ static function bar() { return [1, 2]; } }\n" 0592 "$a = foo::bar()[0];\n"; 0593 } 0594 0595 void TestExpressionParser::arrayFunctionDereferencing() 0596 { 0597 // see bug https://bugs.kde.org/show_bug.cgi?id=305779 0598 QFETCH(QString, code); 0599 /* 0600 QByteArray method("<? function foo() { return [1, 2]; }\n" 0601 "class obj { static function bar() { return [1, 2]; } }\n" 0602 "$myObj = new obj();\n" 0603 // actual test stuff: 0604 "$a = foo()[0];\n" 0605 "$b = $myObj->bar()[0];\n" 0606 "$c = obj::bar()[0];\n");*/ 0607 0608 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0609 DUChainReleaser releaseTop(top); 0610 DUChainWriteLocker lock; 0611 0612 QVERIFY(top->problems().isEmpty()); 0613 0614 Declaration* decl = top->localDeclarations().last(); 0615 auto type = decl->abstractType().dynamicCast<IntegralType>(); 0616 QVERIFY(type); 0617 QEXPECT_FAIL("", "we'd need advanced array support to know that [0] returns a int...", Continue); 0618 QCOMPARE(type->dataType(), static_cast<uint>(IntegralType::TypeInt)); 0619 // fallback 0620 QCOMPARE(type->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0621 } 0622 0623 void TestExpressionParser::arrayLiteralDereferencing_data() 0624 { 0625 QTest::addColumn<QString>("code"); 0626 0627 QTest::newRow("normalSyntax") << "<? $a = array(1,2,3)[1];\n"; 0628 0629 QTest::newRow("shortSyntax") << "<? $a = [1,2,3][1];\n"; 0630 } 0631 0632 void TestExpressionParser::arrayLiteralDereferencing() 0633 { 0634 QFETCH(QString, code); 0635 0636 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0637 DUChainReleaser releaseTop(top); 0638 DUChainWriteLocker lock; 0639 0640 QVERIFY(top->problems().isEmpty()); 0641 0642 Declaration* decl = top->localDeclarations().last(); 0643 auto type = decl->abstractType().dynamicCast<IntegralType>(); 0644 QVERIFY(type); 0645 QEXPECT_FAIL("", "we'd need advanced array support to know that [0] returns a int...", Continue); 0646 QCOMPARE(type->dataType(), static_cast<uint>(IntegralType::TypeInt)); 0647 // fallback 0648 QCOMPARE(type->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0649 } 0650 0651 void TestExpressionParser::stringAsArray_data() 0652 { 0653 QTest::addColumn<QString>("code"); 0654 0655 QTest::newRow("constantEncapsedString") << "<? $a = 'string'[1];\n"; 0656 0657 QTest::newRow("dynamicString") << "<? $string = 'Hello';\n" 0658 "$a = \"$string World\"[1];\n"; 0659 } 0660 0661 void TestExpressionParser::stringAsArray() 0662 { 0663 QFETCH(QString, code); 0664 0665 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0666 DUChainReleaser releaseTop(top); 0667 DUChainWriteLocker lock; 0668 0669 QVERIFY(top->problems().isEmpty()); 0670 0671 Declaration* decl = top->localDeclarations().last(); 0672 auto type = decl->abstractType().dynamicCast<IntegralType>(); 0673 QVERIFY(type); 0674 QCOMPARE(type->dataType(), static_cast<uint>(IntegralType::TypeString)); 0675 } 0676 0677 void TestExpressionParser::classMemberOnInstantiation() 0678 { 0679 // 0 1 2 3 4 5 6 7 0680 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0681 QByteArray method("<? class A{ function foo(){ return 'a'; } } $a = (new A())->foo();"); 0682 0683 TopDUContext* top = parse(method, DumpNone); 0684 DUChainReleaser releaseTop(top); 0685 DUChainWriteLocker lock; 0686 0687 QVERIFY(top->problems().isEmpty()); 0688 0689 ExpressionParser p(true); 0690 0691 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$a"), DUContextPointer(top), CursorInRevision(1, 0)); 0692 QVERIFY(res.type()); 0693 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0694 } 0695 0696 void TestExpressionParser::classNameConstant_data() 0697 { 0698 QTest::addColumn<QString>("NSconst"); 0699 0700 QTest::newRow("fullNamespace") << "\\NS\\ClassName::class"; 0701 QTest::newRow("normalNamespace") << "NS\\ClassName::class"; 0702 QTest::newRow("inNamespace") << "$n"; 0703 } 0704 0705 void TestExpressionParser::classNameConstant() 0706 { 0707 QFETCH(QString, NSconst); 0708 0709 // 0 1 2 3 4 5 6 7 0710 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0711 QByteArray method("<? namespace NS { class ClassName { } $n=ClassName::class; }"); 0712 0713 TopDUContext* top = parse(method, DumpNone); 0714 DUChainReleaser releaseTop(top); 0715 DUChainWriteLocker lock; 0716 0717 QVERIFY(top->problems().isEmpty()); 0718 0719 ExpressionParser p(true); 0720 0721 ExpressionEvaluationResult res = p.evaluateType(NSconst.toUtf8(), DUContextPointer(top), CursorInRevision(1, 0)); 0722 QVERIFY(res.type()); 0723 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0724 } 0725 0726 void TestExpressionParser::invalidVariadicFunction_data() 0727 { 0728 QTest::addColumn<QString>("code"); 0729 0730 QTest::newRow("defaultValue") << "<? function foo(...$i=NULL) { } \n"; 0731 0732 QTest::newRow("multipleVariadics") << "<? function foo(...$i, ...$j) { } \n"; 0733 } 0734 0735 void TestExpressionParser::invalidVariadicFunction() 0736 { 0737 QFETCH(QString, code); 0738 0739 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0740 DUChainReleaser releaseTop(top); 0741 DUChainWriteLocker lock; 0742 0743 QVERIFY(!top->problems().isEmpty()); 0744 } 0745 0746 void TestExpressionParser::invalidArgumentUnpacking() 0747 { 0748 // 0 1 2 3 4 5 6 7 0749 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0750 QByteArray method("<? function foo($a, $b, $c){}; $d = [ 1, 2 ]; foo(...$d, $e);"); 0751 0752 TopDUContext* top = parse(method, DumpNone); 0753 DUChainReleaser releaseTop(top); 0754 DUChainWriteLocker lock; 0755 0756 QVERIFY(!top->problems().isEmpty()); 0757 } 0758 0759 void TestExpressionParser::closure_data() 0760 { 0761 QTest::addColumn<QString>("code"); 0762 0763 QTest::newRow("normalSyntax") << "<? $f = function($a, $b) { return $a + $b; }; \n"; 0764 0765 QTest::newRow("trailingComma") << "<? $f = function($a, $b,) { return $a + $b; }; \n"; 0766 } 0767 0768 void TestExpressionParser::closure() 0769 { 0770 QFETCH(QString, code); 0771 0772 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0773 DUChainReleaser releaseTop(top); 0774 DUChainWriteLocker lock; 0775 0776 QVERIFY(top->problems().isEmpty()); 0777 0778 ExpressionParser p(true); 0779 0780 QCOMPARE(top->childContexts().size(), 2); 0781 QVERIFY(top->childContexts().at(0)->type() == DUContext::Function); 0782 QVERIFY(top->childContexts().at(1)->type() == DUContext::Other); 0783 0784 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$a"), DUContextPointer(top->childContexts().at(0)), 0785 CursorInRevision(0, 20)); 0786 QVERIFY(res.type().dynamicCast<IntegralType>()); 0787 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0788 0789 res = p.evaluateType(QByteArray("$b"), DUContextPointer(top->childContexts().at(0)), 0790 CursorInRevision(0, 20)); 0791 QVERIFY(res.type().dynamicCast<IntegralType>()); 0792 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0793 } 0794 0795 void TestExpressionParser::closureUse_data() 0796 { 0797 QTest::addColumn<QString>("code"); 0798 0799 QTest::newRow("normalSyntax") << "<? $c = 'foo'; $f = function($a, $b) use ($c) { return $c . ':' . $a . '/' . $b; }; \n"; 0800 0801 QTest::newRow("trailingCommaInParameterList") << "<? $c = 'foo'; $f = function($a, $b,) use ($c) { return $c . ':' . $a . '/' . $b; }; \n"; 0802 0803 QTest::newRow("trailingCommaInUseList") << "<? $c = 'foo'; $f = function($a, $b) use ($c,) { return $c . ':' . $a . '/' . $b; }; \n"; 0804 } 0805 0806 void TestExpressionParser::closureUse() 0807 { 0808 QFETCH(QString, code); 0809 0810 TopDUContext* top = parse(code.toUtf8(), DumpNone); 0811 DUChainReleaser releaseTop(top); 0812 DUChainWriteLocker lock; 0813 0814 QVERIFY(top->problems().isEmpty()); 0815 0816 ExpressionParser p(true); 0817 0818 QCOMPARE(top->childContexts().size(), 3); 0819 QVERIFY(top->childContexts().at(0)->type() == DUContext::Function); 0820 QVERIFY(top->childContexts().at(1)->type() == DUContext::Other); 0821 QVERIFY(top->childContexts().at(2)->type() == DUContext::Other); 0822 0823 ExpressionEvaluationResult res = p.evaluateType(QByteArray("$c"), DUContextPointer(top->childContexts().at(1)), 0824 CursorInRevision(0, 50)); 0825 QVERIFY(res.type().dynamicCast<IntegralType>()); 0826 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeString)); 0827 0828 res = p.evaluateType(QByteArray("$a"), DUContextPointer(top->childContexts().at(0)), 0829 CursorInRevision(0, 34)); 0830 QVERIFY(res.type().dynamicCast<IntegralType>()); 0831 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0832 0833 res = p.evaluateType(QByteArray("$b"), DUContextPointer(top->childContexts().at(0)), 0834 CursorInRevision(0, 34)); 0835 QVERIFY(res.type().dynamicCast<IntegralType>()); 0836 QCOMPARE(res.type().staticCast<IntegralType>()->dataType(), static_cast<uint>(IntegralType::TypeMixed)); 0837 } 0838 0839 void TestExpressionParser::closureInvalidUse() 0840 { 0841 // 0 1 2 3 4 5 6 7 0842 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 0843 QByteArray method("<? $c = 'foo'; $f = function($a, $b) use () { return $c . ':' . $a . '/' . $b; }; \n"); 0844 0845 TopDUContext* top = parse(method, DumpNone); 0846 DUChainReleaser releaseTop(top); 0847 DUChainWriteLocker lock; 0848 0849 QVERIFY(!top->problems().isEmpty()); 0850 } 0851 0852 } 0853 0854 #include "moc_expressionparser.cpp"