1
/* This file is part of KDevelop
2
Copyright 2008 Niko Sams <niko.sams@gmail.com>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License version 2 as published by the Free Software Foundation.
8
This library is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
Library General Public License for more details.
13
You should have received a copy of the GNU Library General Public License
14
along with this library; see the file COPYING.LIB. If not, write to
15
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16
Boston, MA 02110-1301, USA.
19
#include "test_expressionparser.h"
21
#include <QtTest/QtTest>
23
#include <language/duchain/parsingenvironment.h>
24
#include <language/duchain/duchain.h>
25
#include <language/duchain/duchainlock.h>
26
#include <language/duchain/topducontext.h>
27
#include <language/duchain/types/functiontype.h>
28
#include <language/duchain/types/integraltype.h>
29
#include <language/duchain/declaration.h>
31
#include "structuretype.h"
32
#include "expressionparser.h"
34
using namespace KTextEditor;
35
using namespace KDevelop;
37
QTEST_MAIN(Php::TestExpressionParser)
42
TestExpressionParser::TestExpressionParser()
47
void TestExpressionParser::newClass()
50
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
51
QByteArray method("<? class A { function foo() {} } $i = new A();");
53
TopDUContext* top = parse(method, DumpNone);
54
DUChainReleaser releaseTop(top);
55
DUChainWriteLocker lock(DUChain::lock());
57
ExpressionParser p(true);
58
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i"), DUContextPointer(top), SimpleCursor(1, 0));
60
QCOMPARE(StructureType::Ptr::staticCast(res.type())->qualifiedIdentifier(), QualifiedIdentifier("a"));
62
void TestExpressionParser::memberVariable()
65
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
66
QByteArray method("<? class A { /** @var A **/ public $foo; } $i = new A();");
68
TopDUContext* top = parse(method, DumpNone);
69
DUChainReleaser releaseTop(top);
70
DUChainWriteLocker lock(DUChain::lock());
72
ExpressionParser p(true);
73
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i->foo"), DUContextPointer(top), SimpleCursor(1, 0));
75
QCOMPARE(res.allDeclarations().count(), 1);
76
QCOMPARE(res.allDeclarations().first(), top->childContexts().first()->localDeclarations().first());
77
QCOMPARE(StructureType::Ptr::staticCast(res.type())->qualifiedIdentifier(), QualifiedIdentifier("a"));
79
void TestExpressionParser::memberFunction()
82
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
83
QByteArray method("<? class A { public function foo() {} } $i = new A();");
85
TopDUContext* top = parse(method, DumpAll);
86
DUChainReleaser releaseTop(top);
87
DUChainWriteLocker lock(DUChain::lock());
89
ExpressionParser p(true);
90
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$i->foo()"), DUContextPointer(top), SimpleCursor(1, 0));
92
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
93
QVERIFY(IntegralType::Ptr::dynamicCast(res.type())->dataType() == IntegralType::TypeVoid);
94
QCOMPARE(res.allDeclarations().size(), 1);
95
QCOMPARE(res.allDeclarations().first(), top->childContexts().first()->localDeclarations().first());
97
void TestExpressionParser::globalFunction()
100
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
101
QByteArray method("<? function foo() {}");
103
TopDUContext* top = parse(method, DumpNone);
104
DUChainReleaser releaseTop(top);
105
DUChainWriteLocker lock(DUChain::lock());
107
ExpressionParser p(true);
108
ExpressionEvaluationResult res = p.evaluateType(QByteArray("foo"), DUContextPointer(top), SimpleCursor(1, 0));
110
QVERIFY(FunctionType::Ptr::dynamicCast(res.type()));
111
QCOMPARE(res.allDeclarations().count(), 1);
112
QCOMPARE(res.allDeclarations().first(), top->localDeclarations().first());
115
void TestExpressionParser::chainCall()
118
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
119
QByteArray method("<? class A { function foo() { return $this; } } $a = new A();");
121
TopDUContext* top = parse(method, DumpAll);
122
DUChainReleaser releaseTop(top);
123
DUChainWriteLocker lock(DUChain::lock());
125
FunctionType::Ptr fn = top->childContexts().first()->localDeclarations().first()->type<FunctionType>();
127
QVERIFY(fn->returnType()->equals(top->localDeclarations().first()->abstractType().unsafeData()));
129
ExpressionParser p(true);
130
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$a->foo()"), DUContextPointer(top), SimpleCursor(1, 0));
132
QVERIFY(res.type()->equals(top->localDeclarations().first()->abstractType().unsafeData()));
134
res = p.evaluateType(QByteArray("$a->foo()->foo()->foo()"), DUContextPointer(top), SimpleCursor(1, 0));
136
QVERIFY(res.type()->equals(top->localDeclarations().first()->abstractType().unsafeData()));
138
void TestExpressionParser::thisObject()
141
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
142
QByteArray method("<? class A { public function foo() {} }");
144
TopDUContext* top = parse(method, DumpNone);
145
DUChainReleaser releaseTop(top);
146
DUChainWriteLocker lock(DUChain::lock());
148
DUContext* funContext = top->childContexts().first()->localDeclarations().first()->internalContext();
149
ExpressionParser p(true);
150
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$this"), DUContextPointer(funContext), SimpleCursor(1, 0));
151
QCOMPARE(res.allDeclarations().count(), 1);
152
QCOMPARE(res.allDeclarations().first(), top->localDeclarations().first());
154
QVERIFY(StructureType::Ptr::dynamicCast(res.type()));
155
QCOMPARE(StructureType::Ptr::dynamicCast(res.type())->declaration(top), top->localDeclarations().first());
158
void TestExpressionParser::integralTypes()
161
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
162
QByteArray method("<? $foo=1;");
164
TopDUContext* top = parse(method, DumpNone);
165
DUChainReleaser releaseTop(top);
166
DUChainWriteLocker lock(DUChain::lock());
168
ExpressionParser p(true);
170
ExpressionEvaluationResult res = p.evaluateType(QByteArray("123"), DUContextPointer(top), SimpleCursor(1, 0));
171
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
172
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeInt));
174
res = p.evaluateType(QByteArray("123.1"), DUContextPointer(top), SimpleCursor(1, 0));
175
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
176
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeFloat));
178
res = p.evaluateType(QByteArray("\"asdf\""), DUContextPointer(top), SimpleCursor(1, 0));
179
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
180
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeString));
182
res = p.evaluateType(QByteArray("\"as $foo df\""), DUContextPointer(top), SimpleCursor(1, 0));
183
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
184
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeString));
186
res = p.evaluateType(QByteArray("'asdf'"), DUContextPointer(top), SimpleCursor(1, 0));
187
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
188
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeString));
190
res = p.evaluateType(QByteArray("true"), DUContextPointer(top), SimpleCursor(1, 0));
191
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
192
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeBoolean));
194
res = p.evaluateType(QByteArray("TRUE"), DUContextPointer(top), SimpleCursor(1, 0));
195
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
196
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeBoolean));
198
res = p.evaluateType(QByteArray("null"), DUContextPointer(top), SimpleCursor(1, 0));
199
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
200
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeNull));
202
res = p.evaluateType(QByteArray("NULL"), DUContextPointer(top), SimpleCursor(1, 0));
203
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
204
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeNull));
207
void TestExpressionParser::newObject()
210
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
211
QByteArray method("<? class A {} ");
213
TopDUContext* top = parse(method, DumpNone);
214
DUChainReleaser releaseTop(top);
215
DUChainWriteLocker lock(DUChain::lock());
217
ExpressionParser p(true);
219
ExpressionEvaluationResult res = p.evaluateType(QByteArray("new A();"), DUContextPointer(top), SimpleCursor(1, 0));
220
QVERIFY(StructureType::Ptr::dynamicCast(res.type()));
221
QCOMPARE(StructureType::Ptr::staticCast(res.type())->declaration(top), top->localDeclarations().first());
224
void TestExpressionParser::cast()
227
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
228
QByteArray method("<? $foo = 1; ");
230
TopDUContext* top = parse(method, DumpNone);
231
DUChainReleaser releaseTop(top);
232
DUChainWriteLocker lock(DUChain::lock());
234
ExpressionParser p(true);
236
ExpressionEvaluationResult res = p.evaluateType(QByteArray("(string)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
237
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
238
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeString);
240
res = p.evaluateType(QByteArray("(int)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
241
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
242
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
244
res = p.evaluateType(QByteArray("(double)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
245
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
246
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeFloat);
248
res = p.evaluateType(QByteArray("(bool)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
249
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
250
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeBoolean);
252
res = p.evaluateType(QByteArray("(array)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
253
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
254
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeArray);
256
res = p.evaluateType(QByteArray("(object)$foo"), DUContextPointer(top), SimpleCursor(1, 0));
257
kDebug() << res.type();
258
kDebug() << res.type()->toString();
259
QVERIFY(StructureType::Ptr::dynamicCast(res.type()));
260
QVERIFY(StructureType::Ptr::staticCast(res.type())->qualifiedIdentifier() == QualifiedIdentifier("stdclass"));
263
void TestExpressionParser::operations()
266
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
267
QByteArray method("<? $foo = 1; ");
269
TopDUContext* top = parse(method, DumpNone);
270
DUChainReleaser releaseTop(top);
271
DUChainWriteLocker lock(DUChain::lock());
273
ExpressionParser p(true);
275
ExpressionEvaluationResult res = p.evaluateType(QByteArray("'1' . '1'"), DUContextPointer(top), SimpleCursor(1, 0));
276
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
277
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeString);
279
res = p.evaluateType(QByteArray("1 . 1"), DUContextPointer(top), SimpleCursor(1, 0));
280
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
281
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeString);
283
res = p.evaluateType(QByteArray("1 + 1"), DUContextPointer(top), SimpleCursor(1, 0));
284
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
285
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
287
res = p.evaluateType(QByteArray("'1' + '1'"), DUContextPointer(top), SimpleCursor(1, 0));
288
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
289
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
291
res = p.evaluateType(QByteArray("$foo .= '1'"), DUContextPointer(top), SimpleCursor(1, 0));
292
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
293
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeString);
295
res = p.evaluateType(QByteArray("$foo .= 1"), DUContextPointer(top), SimpleCursor(1, 0));
296
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
297
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeString);
299
res = p.evaluateType(QByteArray("$foo += 1"), DUContextPointer(top), SimpleCursor(1, 0));
300
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
301
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
303
res = p.evaluateType(QByteArray("$foo += '1'"), DUContextPointer(top), SimpleCursor(1, 0));
304
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
305
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
307
res = p.evaluateType(QByteArray("$foo *= 1"), DUContextPointer(top), SimpleCursor(1, 0));
308
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
309
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
311
res = p.evaluateType(QByteArray("$foo *= '1'"), DUContextPointer(top), SimpleCursor(1, 0));
312
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
313
QVERIFY(IntegralType::Ptr::staticCast(res.type())->dataType() == IntegralType::TypeInt);
316
void TestExpressionParser::findArg()
319
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
320
QByteArray method("<? class A{} function foo($arg, &$bar, A &$a) { } ");
322
TopDUContext* top = parse(method, DumpNone);
323
DUChainReleaser releaseTop(top);
324
DUChainWriteLocker lock(DUChain::lock());
326
ExpressionParser p(true);
328
QCOMPARE(top->childContexts().size(), 3);
329
QVERIFY(top->childContexts().at(0)->type() == DUContext::Class);
330
QVERIFY(top->childContexts().at(1)->type() == DUContext::Function);
331
QVERIFY(top->childContexts().at(2)->type() != DUContext::Function);
333
ExpressionEvaluationResult res = p.evaluateType(QByteArray("$arg"), DUContextPointer(top->childContexts().last()),
334
SimpleCursor(0, 47));
335
QVERIFY(IntegralType::Ptr::dynamicCast(res.type()));
336
QCOMPARE(IntegralType::Ptr::staticCast(res.type())->dataType(), static_cast<uint>(IntegralType::TypeMixed));
338
res = p.evaluateType(QByteArray("$bar"), DUContextPointer(top->childContexts().last()),
339
SimpleCursor(0, 47));
340
ReferenceType::Ptr type = ReferenceType::Ptr::dynamicCast(res.type());
342
QVERIFY(IntegralType::Ptr::dynamicCast(type->baseType()));
343
QCOMPARE(IntegralType::Ptr::staticCast(type->baseType())->dataType(), static_cast<uint>(IntegralType::TypeMixed));
345
res = p.evaluateType(QByteArray("$a"), DUContextPointer(top->childContexts().last()),
346
SimpleCursor(0, 47));
347
type = ReferenceType::Ptr::dynamicCast(res.type());
349
QVERIFY(StructureType::Ptr::dynamicCast(type->baseType()));
350
QCOMPARE(StructureType::Ptr::staticCast(type->baseType())->declaration(top), top->localDeclarations().first());
355
#include "test_expressionparser.moc"