~ubuntu-branches/ubuntu/utopic/kdevelop-php/utopic

« back to all changes in this revision

Viewing changes to duchain/typebuilder.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Fathi Boudra
  • Date: 2010-01-17 17:10:22 UTC
  • Revision ID: james.westby@ubuntu.com-20100117171022-q2xlgd9ekewo2ijx
Tags: upstream-1.0.0~beta2
ImportĀ upstreamĀ versionĀ 1.0.0~beta2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   This file is part of KDevelop                                         *
 
3
 *   Copyright 2008 Niko Sams <niko.sams@gmail.com>                        *
 
4
 *                                                                         *
 
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.                       *
 
9
 *                                                                         *
 
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.                          *
 
14
 *                                                                         *
 
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
 ***************************************************************************/
 
20
 
 
21
#include "typebuilder.h"
 
22
 
 
23
#include <ktexteditor/smartrange.h>
 
24
 
 
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"
 
34
 
 
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>
 
42
 
 
43
using namespace KDevelop;
 
44
namespace Php
 
45
{
 
46
 
 
47
TypeBuilder::TypeBuilder()
 
48
    : TypeBuilderBase()
 
49
    , m_gotTypeFromDocComment(false)
 
50
    , m_gotReturnTypeFromDocComment(false)
 
51
{
 
52
}
 
53
 
 
54
TypeBuilder::~TypeBuilder()
 
55
{
 
56
}
 
57
 
 
58
AbstractType::Ptr TypeBuilder::parseType(QString type, AstNode* node)
 
59
{
 
60
    uint iType = 0;
 
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") {
 
78
        //TODO
 
79
        iType = IntegralType::TypeMixed;
 
80
    } else if (lType == "null") {
 
81
        iType = IntegralType::TypeNull;
 
82
    } else if (lType == "void") {
 
83
        iType = IntegralType::TypeVoid;
 
84
    } else {
 
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();
 
89
        }
 
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);
 
96
                }
 
97
            }
 
98
            if (!type.isEmpty()) {
 
99
                UnsureType::Ptr ret(new UnsureType());
 
100
                foreach (const AbstractType::Ptr& t, types) {
 
101
                    ret->addType(t->indexed());
 
102
                }
 
103
                //kDebug() << type << ret->toString();
 
104
                return AbstractType::Ptr::staticCast(ret);
 
105
            }
 
106
        }
 
107
        iType = IntegralType::TypeMixed;
 
108
    }
 
109
    AbstractType::Ptr ret(new IntegralType(iType));
 
110
    //kDebug() << type << ret->toString();
 
111
    return ret;
 
112
}
 
113
 
 
114
AbstractType::Ptr TypeBuilder::injectParseType(QString type, AstNode* node)
 
115
{
 
116
    AbstractType::Ptr ret = parseType(type, node);
 
117
    injectType(ret);
 
118
    //kDebug() << type << ret->toString();
 
119
    return ret;
 
120
}
 
121
 
 
122
AbstractType::Ptr TypeBuilder::parseDocComment(AstNode* node, const QString& docCommentName)
 
123
{
 
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();
 
134
                }
 
135
            } else {
 
136
                type = injectParseType(rx.cap(1), node);
 
137
            }
 
138
            if (type) {
 
139
                m_gotTypeFromDocComment = true;
 
140
            }
 
141
            return type;
 
142
        }
 
143
    }
 
144
    return AbstractType::Ptr();
 
145
}
 
146
 
 
147
 
 
148
QList<AbstractType::Ptr> TypeBuilder::parseDocCommentParams(AstNode* node)
 
149
{
 
150
    QList<AbstractType::Ptr> ret;
 
151
    QString docComment = editor()->parseSession()->docComment(node->startToken);
 
152
    if (!docComment.isEmpty()) {
 
153
        QRegExp rx("\\*\\s+@param\\s([^\\s]*)");
 
154
        int pos = 0;
 
155
        while ((pos = rx.indexIn(docComment, pos)) != -1) {
 
156
            ret << parseType(rx.cap(1), node);
 
157
            pos += rx.matchedLength();
 
158
        }
 
159
    }
 
160
    return ret;
 
161
}
 
162
 
 
163
AbstractType::Ptr TypeBuilder::getTypeForNode(AstNode* node)
 
164
{
 
165
    AbstractType::Ptr type;
 
166
    if (node) {
 
167
        node->ducontext = currentContext();
 
168
        ExpressionParser ep;
 
169
        ep.setCreateProblems(true);
 
170
        ExpressionEvaluationResult res = ep.evaluateType(node, editor());
 
171
        type = res.type();
 
172
    }
 
173
    if (!type) {
 
174
        type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
 
175
    }
 
176
    return type;
 
177
}
 
178
 
 
179
FunctionType::Ptr TypeBuilder::openFunctionType(AstNode* node)
 
180
{
 
181
    FunctionType::Ptr functionType = FunctionType::Ptr(new FunctionType());
 
182
 
 
183
    openType(functionType);
 
184
 
 
185
    functionType->setReturnType(parseDocComment(node, "return"));
 
186
    m_gotReturnTypeFromDocComment = functionType->returnType();
 
187
    updateCurrentType();
 
188
 
 
189
    return functionType;
 
190
}
 
191
 
 
192
void TypeBuilder::visitClassDeclarationStatement(ClassDeclarationStatementAst* node)
 
193
{
 
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>());
 
197
 
 
198
    TypeBuilderBase::visitClassDeclarationStatement(node);
 
199
}
 
200
 
 
201
void TypeBuilder::visitInterfaceDeclarationStatement(InterfaceDeclarationStatementAst* node)
 
202
{
 
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>());
 
206
 
 
207
    TypeBuilderBase::visitInterfaceDeclarationStatement(node);
 
208
}
 
209
 
 
210
void TypeBuilder::visitClassStatement(ClassStatementAst *node)
 
211
{
 
212
    if (node->methodName) {
 
213
        //method declaration
 
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)));
 
219
        }
 
220
        closeType();
 
221
    } else {
 
222
        //member-variable
 
223
        parseDocComment(node, "var");
 
224
        TypeBuilderBase::visitClassStatement(node);
 
225
        if (m_gotTypeFromDocComment) {
 
226
            clearLastType();
 
227
            m_gotTypeFromDocComment = false;
 
228
        }
 
229
    }
 
230
}
 
231
 
 
232
void TypeBuilder::visitClassVariable(ClassVariableAst *node)
 
233
{
 
234
    if (!m_gotTypeFromDocComment) {
 
235
        openAbstractType(getTypeForNode(node->value));
 
236
 
 
237
        TypeBuilderBase::visitClassVariable(node);
 
238
 
 
239
        closeType();
 
240
    } else {
 
241
        TypeBuilderBase::visitClassVariable(node);
 
242
    }
 
243
}
 
244
 
 
245
void TypeBuilder::visitClassConstantDeclaration(ClassConstantDeclarationAst* node)
 
246
{
 
247
    if (!m_gotTypeFromDocComment) {
 
248
        AbstractType::Ptr type = getTypeForNode(node->scalar);
 
249
        type->setModifiers(type->modifiers() | AbstractType::ConstModifier);
 
250
        openAbstractType(type);
 
251
 
 
252
        TypeBuilderBase::visitClassConstantDeclaration(node);
 
253
 
 
254
        closeType();
 
255
    } else {
 
256
        currentAbstractType()->setModifiers(currentAbstractType()->modifiers() & AbstractType::ConstModifier);
 
257
        TypeBuilderBase::visitClassConstantDeclaration(node);
 
258
    }
 
259
}
 
260
 
 
261
void TypeBuilder::visitParameter(ParameterAst *node)
 
262
{
 
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));
 
270
    }
 
271
    if (!type) {
 
272
        if (m_currentFunctionParams.count() > currentType<FunctionType>()->arguments().count()) {
 
273
            type = m_currentFunctionParams.at(currentType<FunctionType>()->arguments().count());
 
274
        } else {
 
275
            type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
 
276
        }
 
277
    }
 
278
 
 
279
    if ( node->isRef != -1 ) {
 
280
      ReferenceType::Ptr p( new ReferenceType() );
 
281
      p->setBaseType( type );
 
282
 
 
283
      type = p.cast<AbstractType>();
 
284
    }
 
285
 
 
286
    openAbstractType(type);
 
287
    TypeBuilderBase::visitParameter(node);
 
288
    closeType();
 
289
    DUChainWriteLocker lock(DUChain::lock());
 
290
    currentType<FunctionType>()->addArgument(type);
 
291
}
 
292
 
 
293
void TypeBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node)
 
294
{
 
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>();
 
300
    Q_ASSERT(type);
 
301
 
 
302
    type->setReturnType(parseDocComment(node, "return"));
 
303
    m_gotReturnTypeFromDocComment = type->returnType();
 
304
 
 
305
    updateCurrentType();
 
306
 
 
307
    TypeBuilderBase::visitFunctionDeclarationStatement(node);
 
308
 
 
309
    if (!type->returnType()) {
 
310
        type->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
 
311
    }
 
312
}
 
313
 
 
314
void TypeBuilder::visitExpr(ExprAst *node)
 
315
{
 
316
    openAbstractType(getTypeForNode(node));
 
317
 
 
318
    TypeBuilderBase::visitExpr(node);
 
319
 
 
320
    closeType();
 
321
}
 
322
 
 
323
void TypeBuilder::visitStaticVar(StaticVarAst *node)
 
324
{
 
325
    openAbstractType(getTypeForNode(node->value));
 
326
 
 
327
    TypeBuilderBase::visitStaticVar(node);
 
328
 
 
329
    closeType();
 
330
}
 
331
 
 
332
void TypeBuilder::visitStatement(StatementAst* node)
 
333
{
 
334
    TypeBuilderBase::visitStatement(node);
 
335
    if ( !m_gotReturnTypeFromDocComment && node->returnExpr && lastType() && hasCurrentType() && currentType<FunctionType>())
 
336
    {
 
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)
 
342
            {
 
343
                //don't add TypeMixed to the list, just ignore
 
344
                ft->setReturnType(lastType());
 
345
            } else {
 
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) {
 
353
                            retT->addType(t);
 
354
                        }
 
355
                    } else {
 
356
                        //kDebug() << "add to returnType";
 
357
                        retT->addType(lastType()->indexed());
 
358
                    }
 
359
                } else {
 
360
                    if (lastType().cast<UnsureType>()) {
 
361
                        retT = lastType().cast<UnsureType>();
 
362
                    } else {
 
363
                        retT = new UnsureType();
 
364
                        retT->addType(lastType()->indexed());
 
365
                    }
 
366
                    retT->addType(ft->returnType()->indexed());
 
367
                }
 
368
                ft->setReturnType(AbstractType::Ptr::staticCast(retT));
 
369
            }
 
370
        } else {
 
371
            ft->setReturnType(lastType());
 
372
        }
 
373
        updateCurrentType();
 
374
    }
 
375
 
 
376
    AstNode *foreachNode = 0;
 
377
    if (node->foreachVar) {
 
378
        foreachNode = node->foreachVar;
 
379
    } else if (node->foreachExpr) {
 
380
        foreachNode = node->foreachExpr;
 
381
    }
 
382
    if (foreachNode) {
 
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()));
 
389
            Q_ASSERT(classDec);
 
390
            lock.unlock();
 
391
            ClassDeclaration* iteratorDecl = dynamic_cast<ClassDeclaration*>(
 
392
                findDeclarationImport(ClassDeclarationType, QualifiedIdentifier("iterator"), 0)
 
393
            );
 
394
            lock.lock();
 
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();
 
402
                }
 
403
            }
 
404
        }
 
405
    }
 
406
}
 
407
 
 
408
void TypeBuilder::visitCatchItem(Php::CatchItemAst *node)
 
409
{
 
410
    TypeBuilderBase::visitCatchItem(node);
 
411
    KDevelop::Declaration *dec = findDeclarationImport(ClassDeclarationType, node->catchClass);
 
412
    if (dec && dec->abstractType()) {
 
413
        openAbstractType(dec->abstractType());
 
414
        closeType();
 
415
    }
 
416
 
 
417
}
 
418
 
 
419
void TypeBuilder::updateCurrentType()
 
420
{
 
421
    // do nothing
 
422
}
 
423
 
 
424
}
 
425