1
/***************************************************************************
2
* This file is part of KDevelop *
3
* Copyright 2008 Niko Sams <niko.sams@gmail.com> *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU Library General Public License as *
7
* published by the Free Software Foundation; either version 2 of the *
8
* License, or (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU Library General Public *
16
* License along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19
***************************************************************************/
21
#include "typebuilder.h"
23
#include <ktexteditor/smartrange.h>
25
#include <language/duchain/identifier.h>
26
#include <language/duchain/duchain.h>
27
#include <language/duchain/duchainlock.h>
28
#include <language/duchain/ducontext.h>
29
#include <language/duchain/declaration.h>
30
#include <language/duchain/types/integraltype.h>
31
#include "classdeclaration.h"
32
#include "integraltypeextended.h"
33
#include "structuretype.h"
35
#include "editorintegrator.h"
36
#include "parsesession.h"
37
#include "phpdebugvisitor.h"
38
#include "expressionparser.h"
39
#include "expressionvisitor.h"
40
#include "classmethoddeclaration.h"
41
#include <language/duchain/types/unsuretype.h>
43
using namespace KDevelop;
47
TypeBuilder::TypeBuilder()
49
, m_gotTypeFromDocComment(false)
50
, m_gotReturnTypeFromDocComment(false)
54
TypeBuilder::~TypeBuilder()
58
AbstractType::Ptr TypeBuilder::parseType(QString type, AstNode* node)
61
type = type.trimmed();
62
QString lType = type.toLower();
63
if (lType == "int" || lType == "integer") {
64
iType = IntegralType::TypeInt;
65
} else if (lType == "float" || lType == "double") {
66
iType = IntegralType::TypeFloat;
67
} else if (lType == "bool" || lType == "boolean" || lType == "false" || lType == "true") {
68
iType = IntegralType::TypeBoolean;
69
} else if (lType == "string") {
70
iType = IntegralType::TypeString;
71
} else if (lType == "mixed") {
72
iType = IntegralType::TypeMixed;
73
} else if (lType == "array") {
74
iType = IntegralType::TypeArray;
75
} else if (lType == "resource") {
76
return AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeResource));
77
} else if (lType == "object") {
79
iType = IntegralType::TypeMixed;
80
} else if (lType == "null") {
81
iType = IntegralType::TypeNull;
82
} else if (lType == "void") {
83
iType = IntegralType::TypeVoid;
85
//don't use openTypeFromName as it uses cursor for findDeclarations
86
Declaration* decl = findDeclarationImport(ClassDeclarationType, QualifiedIdentifier(type.toLower()), node);
87
if (decl && decl->abstractType()) {
88
return decl->abstractType();
90
if (type.contains('|')) {
91
QList<AbstractType::Ptr> types;
92
foreach (const QString& t, type.split('|')) {
93
AbstractType::Ptr subType = parseType(t, node);
94
if (!(IntegralType::Ptr::dynamicCast(subType) && IntegralType::Ptr::staticCast(subType)->dataType() == IntegralType::TypeMixed)) {
95
types << parseType(t, node);
98
if (!type.isEmpty()) {
99
UnsureType::Ptr ret(new UnsureType());
100
foreach (const AbstractType::Ptr& t, types) {
101
ret->addType(t->indexed());
103
//kDebug() << type << ret->toString();
104
return AbstractType::Ptr::staticCast(ret);
107
iType = IntegralType::TypeMixed;
109
AbstractType::Ptr ret(new IntegralType(iType));
110
//kDebug() << type << ret->toString();
114
AbstractType::Ptr TypeBuilder::injectParseType(QString type, AstNode* node)
116
AbstractType::Ptr ret = parseType(type, node);
118
//kDebug() << type << ret->toString();
122
AbstractType::Ptr TypeBuilder::parseDocComment(AstNode* node, const QString& docCommentName)
124
m_gotTypeFromDocComment = false;
125
QString docComment = editor()->parseSession()->docComment(node->startToken);
126
if (!docComment.isEmpty()) {
127
QRegExp rx("(?:\\*|///)\\s+@" + QRegExp::escape(docCommentName) + "\\s+([^\\s]*)");
128
if (rx.indexIn(docComment) != -1) {
129
AbstractType::Ptr type;
130
if (rx.cap(1) == "$this") {
131
DUChainReadLocker lock(DUChain::lock());
132
if (currentContext()->owner()) {
133
type = currentContext()->owner()->abstractType();
136
type = injectParseType(rx.cap(1), node);
139
m_gotTypeFromDocComment = true;
144
return AbstractType::Ptr();
148
QList<AbstractType::Ptr> TypeBuilder::parseDocCommentParams(AstNode* node)
150
QList<AbstractType::Ptr> ret;
151
QString docComment = editor()->parseSession()->docComment(node->startToken);
152
if (!docComment.isEmpty()) {
153
QRegExp rx("\\*\\s+@param\\s([^\\s]*)");
155
while ((pos = rx.indexIn(docComment, pos)) != -1) {
156
ret << parseType(rx.cap(1), node);
157
pos += rx.matchedLength();
163
AbstractType::Ptr TypeBuilder::getTypeForNode(AstNode* node)
165
AbstractType::Ptr type;
167
node->ducontext = currentContext();
169
ep.setCreateProblems(true);
170
ExpressionEvaluationResult res = ep.evaluateType(node, editor());
174
type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
179
FunctionType::Ptr TypeBuilder::openFunctionType(AstNode* node)
181
FunctionType::Ptr functionType = FunctionType::Ptr(new FunctionType());
183
openType(functionType);
185
functionType->setReturnType(parseDocComment(node, "return"));
186
m_gotReturnTypeFromDocComment = functionType->returnType();
192
void TypeBuilder::visitClassDeclarationStatement(ClassDeclarationStatementAst* node)
194
// the predeclaration builder should have set up a type already
195
// and the declarationbuilder should have set that as current type
196
Q_ASSERT(hasCurrentType() && currentType<StructureType>());
198
TypeBuilderBase::visitClassDeclarationStatement(node);
201
void TypeBuilder::visitInterfaceDeclarationStatement(InterfaceDeclarationStatementAst* node)
203
// the predeclaration builder should have set up a type already
204
// and the declarationbuilder should have set that as current type
205
Q_ASSERT(hasCurrentType() && currentType<StructureType>());
207
TypeBuilderBase::visitInterfaceDeclarationStatement(node);
210
void TypeBuilder::visitClassStatement(ClassStatementAst *node)
212
if (node->methodName) {
214
m_currentFunctionParams = parseDocCommentParams(node);
215
openFunctionType(node);
216
TypeBuilderBase::visitClassStatement(node);
217
if (currentType<FunctionType>() && !currentType<FunctionType>()->returnType()) {
218
currentType<FunctionType>()->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
223
parseDocComment(node, "var");
224
TypeBuilderBase::visitClassStatement(node);
225
if (m_gotTypeFromDocComment) {
227
m_gotTypeFromDocComment = false;
232
void TypeBuilder::visitClassVariable(ClassVariableAst *node)
234
if (!m_gotTypeFromDocComment) {
235
openAbstractType(getTypeForNode(node->value));
237
TypeBuilderBase::visitClassVariable(node);
241
TypeBuilderBase::visitClassVariable(node);
245
void TypeBuilder::visitClassConstantDeclaration(ClassConstantDeclarationAst* node)
247
if (!m_gotTypeFromDocComment) {
248
AbstractType::Ptr type = getTypeForNode(node->scalar);
249
type->setModifiers(type->modifiers() | AbstractType::ConstModifier);
250
openAbstractType(type);
252
TypeBuilderBase::visitClassConstantDeclaration(node);
256
currentAbstractType()->setModifiers(currentAbstractType()->modifiers() & AbstractType::ConstModifier);
257
TypeBuilderBase::visitClassConstantDeclaration(node);
261
void TypeBuilder::visitParameter(ParameterAst *node)
263
AbstractType::Ptr type;
264
if (node->parameterType) {
265
//don't use openTypeFromName as it uses cursor for findDeclarations
266
Declaration* decl = findDeclarationImport(ClassDeclarationType, node->parameterType);
267
if (decl) type = decl->abstractType();
268
} else if (node->arrayType != -1) {
269
type = AbstractType::Ptr(new IntegralType(IntegralType::TypeArray));
272
if (m_currentFunctionParams.count() > currentType<FunctionType>()->arguments().count()) {
273
type = m_currentFunctionParams.at(currentType<FunctionType>()->arguments().count());
275
type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
279
if ( node->isRef != -1 ) {
280
ReferenceType::Ptr p( new ReferenceType() );
281
p->setBaseType( type );
283
type = p.cast<AbstractType>();
286
openAbstractType(type);
287
TypeBuilderBase::visitParameter(node);
289
DUChainWriteLocker lock(DUChain::lock());
290
currentType<FunctionType>()->addArgument(type);
293
void TypeBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node)
295
m_currentFunctionParams = parseDocCommentParams(node);
296
// the predeclarationbuilder should have already built the type
297
// and the declarationbuilder should have set it to open
298
Q_ASSERT(hasCurrentType());
299
FunctionType::Ptr type = currentType<FunctionType>();
302
type->setReturnType(parseDocComment(node, "return"));
303
m_gotReturnTypeFromDocComment = type->returnType();
307
TypeBuilderBase::visitFunctionDeclarationStatement(node);
309
if (!type->returnType()) {
310
type->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
314
void TypeBuilder::visitExpr(ExprAst *node)
316
openAbstractType(getTypeForNode(node));
318
TypeBuilderBase::visitExpr(node);
323
void TypeBuilder::visitStaticVar(StaticVarAst *node)
325
openAbstractType(getTypeForNode(node->value));
327
TypeBuilderBase::visitStaticVar(node);
332
void TypeBuilder::visitStatement(StatementAst* node)
334
TypeBuilderBase::visitStatement(node);
335
if ( !m_gotReturnTypeFromDocComment && node->returnExpr && lastType() && hasCurrentType() && currentType<FunctionType>())
337
FunctionType::Ptr ft = currentType<FunctionType>();
338
// kDebug() << "return" << (ft->returnType() ? ft->returnType()->toString() : "none") << lastType()->toString();
339
if (ft->returnType()) {
340
if (ft->returnType().cast<IntegralType>()
341
&& ft->returnType().cast<IntegralType>()->dataType() == IntegralType::TypeMixed)
343
//don't add TypeMixed to the list, just ignore
344
ft->setReturnType(lastType());
346
UnsureType::Ptr retT;
347
if (ft->returnType().cast<UnsureType>()) {
348
//kDebug() << "we already have an unsure type";
349
retT = ft->returnType().cast<UnsureType>();
350
if (lastType().cast<UnsureType>()) {
351
//kDebug() << "add multiple to returnType";
352
FOREACH_FUNCTION(const IndexedType& t, lastType().cast<UnsureType>()->types) {
356
//kDebug() << "add to returnType";
357
retT->addType(lastType()->indexed());
360
if (lastType().cast<UnsureType>()) {
361
retT = lastType().cast<UnsureType>();
363
retT = new UnsureType();
364
retT->addType(lastType()->indexed());
366
retT->addType(ft->returnType()->indexed());
368
ft->setReturnType(AbstractType::Ptr::staticCast(retT));
371
ft->setReturnType(lastType());
376
AstNode *foreachNode = 0;
377
if (node->foreachVar) {
378
foreachNode = node->foreachVar;
379
} else if (node->foreachExpr) {
380
foreachNode = node->foreachExpr;
383
ExpressionVisitor v(editor());
384
foreachNode->ducontext = currentContext();
385
v.visitNode(foreachNode);
386
DUChainReadLocker lock(DUChain::lock());
387
if (StructureType::Ptr type = StructureType::Ptr::dynamicCast(v.result().type())) {
388
ClassDeclaration *classDec = dynamic_cast<ClassDeclaration*>(type->declaration(currentContext()->topContext()));
391
ClassDeclaration* iteratorDecl = dynamic_cast<ClassDeclaration*>(
392
findDeclarationImport(ClassDeclarationType, QualifiedIdentifier("iterator"), 0)
395
Q_ASSERT(iteratorDecl);
396
if (classDec->isPublicBaseClass(iteratorDecl, currentContext()->topContext())) {
397
foreach (Declaration *d, classDec->internalContext()->findDeclarations(QualifiedIdentifier("current"))) {
398
if (!dynamic_cast<ClassMethodDeclaration*>(d)) continue;
399
Q_ASSERT(d->type<FunctionType>());
400
injectType(d->type<FunctionType>()->returnType());
401
// kDebug() << "that's it: " << d->type<FunctionType>()->returnType()->toString();
408
void TypeBuilder::visitCatchItem(Php::CatchItemAst *node)
410
TypeBuilderBase::visitCatchItem(node);
411
KDevelop::Declaration *dec = findDeclarationImport(ClassDeclarationType, node->catchClass);
412
if (dec && dec->abstractType()) {
413
openAbstractType(dec->abstractType());
419
void TypeBuilder::updateCurrentType()