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"