File indexing completed on 2024-05-05 16:41:07

0001 /*
0002     SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "typebuilder.h"
0008 
0009 #include <language/duchain/identifier.h>
0010 #include <language/duchain/duchain.h>
0011 #include <language/duchain/duchainlock.h>
0012 #include <language/duchain/ducontext.h>
0013 #include <language/duchain/declaration.h>
0014 #include <language/duchain/types/integraltype.h>
0015 #include <language/duchain/types/arraytype.h>
0016 #include "../declarations/classdeclaration.h"
0017 #include "../types/integraltypeextended.h"
0018 #include "../types/structuretype.h"
0019 #include <duchaindebug.h>
0020 
0021 #include "editorintegrator.h"
0022 #include "parsesession.h"
0023 #include "phpdebugvisitor.h"
0024 #include "expressionparser.h"
0025 #include "expressionvisitor.h"
0026 #include "../declarations/classmethoddeclaration.h"
0027 #include <language/duchain/types/unsuretype.h>
0028 
0029 using namespace KDevelop;
0030 namespace Php
0031 {
0032 
0033 TypeBuilder::TypeBuilder()
0034     : TypeBuilderBase()
0035     , m_gotTypeFromDocComment(false)
0036     , m_gotTypeFromTypeHint(false)
0037     , m_gotReturnTypeFromDocComment(false)
0038 {
0039 }
0040 
0041 TypeBuilder::~TypeBuilder()
0042 {
0043 }
0044 
0045 AbstractType::Ptr TypeBuilder::parseType(QString type, AstNode* node)
0046 {
0047     type = type.trimmed();
0048 
0049     if (type.contains('|')) {
0050         QList<AbstractType::Ptr> types;
0051         foreach (const QString& t, type.split('|')) {
0052             AbstractType::Ptr subType = parseType(t, node);
0053             if (!(subType.dynamicCast<IntegralType>() && subType.staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed)) {
0054                 types << parseType(t, node);
0055             }
0056         }
0057         UnsureType::Ptr ret(new UnsureType());
0058         foreach (const AbstractType::Ptr& t, types) {
0059             ret->addType(t->indexed());
0060         }
0061         return ret;
0062     }
0063 
0064     if (type.endsWith(QLatin1String("[]"))) {
0065         KDevelop::ArrayType* a_type = new KDevelop::ArrayType();
0066         a_type->setElementType(parseSimpleType(type.left(type.length() - 2), node));
0067         return AbstractType::Ptr(a_type);
0068     } else {
0069         return parseSimpleType(type, node);
0070     }
0071 }
0072 
0073 AbstractType::Ptr TypeBuilder::parseSimpleType(QString type, AstNode* node)
0074 {
0075     Q_UNUSED(node)
0076     uint iType = 0;
0077     if (!type.compare(QLatin1String("int"), Qt::CaseInsensitive) || !type.compare(QLatin1String("integer"), Qt::CaseInsensitive)) {
0078         iType = IntegralType::TypeInt;
0079     } else if (!type.compare(QLatin1String("float"), Qt::CaseInsensitive) || !type.compare(QLatin1String("double"), Qt::CaseInsensitive)) {
0080         iType = IntegralType::TypeFloat;
0081     } else if (!type.compare(QLatin1String("bool"), Qt::CaseInsensitive) || !type.compare(QLatin1String("boolean"), Qt::CaseInsensitive)
0082             || !type.compare(QLatin1String("false"), Qt::CaseInsensitive) || !type.compare(QLatin1String("true"), Qt::CaseInsensitive)) {
0083         iType = IntegralType::TypeBoolean;
0084     } else if (!type.compare(QLatin1String("string"), Qt::CaseInsensitive)) {
0085         iType = IntegralType::TypeString;
0086     } else if (!type.compare(QLatin1String("mixed"), Qt::CaseInsensitive)) {
0087         iType = IntegralType::TypeMixed;
0088     } else if (!type.compare(QLatin1String("array"), Qt::CaseInsensitive)) {
0089         iType = IntegralType::TypeArray;
0090     } else if (!type.compare(QLatin1String("resource"), Qt::CaseInsensitive)) {
0091         return AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeResource));
0092     } else if (!type.compare(QLatin1String("null"), Qt::CaseInsensitive)) {
0093         iType = IntegralType::TypeNull;
0094     } else if (!type.compare(QLatin1String("void"), Qt::CaseInsensitive)) {
0095         iType = IntegralType::TypeVoid;
0096     } else if (!type.compare(QLatin1String("self"), Qt::CaseInsensitive)
0097             || !type.compare(QLatin1String("this"), Qt::CaseInsensitive) || !type.compare(QLatin1String("static"), Qt::CaseInsensitive)) {
0098         DUChainReadLocker lock(DUChain::lock());
0099         if ( currentContext()->type() == DUContext::Class && currentContext()->owner() ) {
0100             return currentContext()->owner()->abstractType();
0101         }
0102     } else {
0103         if (!type.compare(QLatin1String("object"), Qt::CaseInsensitive)) {
0104             return AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeObject));
0105         }
0106 
0107         QualifiedIdentifier typehint = QualifiedIdentifier(type.toLower().replace(QLatin1Literal("\\"), QLatin1Literal("::")));
0108 
0109         if (typehint.toString().startsWith(QLatin1Literal("::"))) {
0110             typehint.setExplicitlyGlobal(true);
0111         }
0112 
0113         //don't use openTypeFromName as it uses cursor for findDeclarations
0114         DeclarationPointer decl = findDeclarationImport(ClassDeclarationType, typehint);
0115 
0116         if (decl && decl->abstractType()) {
0117             return decl->abstractType();
0118         }
0119         iType = IntegralType::TypeMixed;
0120     }
0121     AbstractType::Ptr ret(new IntegralType(iType));
0122     //qCDebug(DUCHAIN) << type << ret->toString();
0123     return ret;
0124 }
0125 
0126 AbstractType::Ptr TypeBuilder::injectParseType(QString type, AstNode* node)
0127 {
0128     AbstractType::Ptr ret = parseType(type, node);
0129     injectType(ret);
0130     //qCDebug(DUCHAIN) << type << ret->toString();
0131     return ret;
0132 }
0133 
0134 /**
0135  * Find all (or only one - see @p docCommentName) values for a given needle
0136  * in a doc-comment. Needle has to start a line in the doccomment,
0137  * i.e.:
0138  *
0139  *  * @docCommentName value
0140  *
0141  * or
0142  *
0143  *  /// @docCommentName value
0144  */
0145 QStringList findInDocComment(const QString &docComment, const QString &docCommentName, const bool onlyOne)
0146 {
0147     QStringList matches;
0148     // optimization that does not require potentially slow regexps
0149     // old code was something like this:
0150     /*
0151     if (!docComment.isEmpty()) {
0152         QRegExp rx("\\*\\s+@param\\s([^\\s]*)");
0153         int pos = 0;
0154         while ((pos = rx.indexIn(docComment, pos)) != -1) {
0155             ret << parseType(rx.cap(1), node);
0156             pos += rx.matchedLength();
0157         }
0158     }
0159     */
0160 
0161     for ( int i = 0, size = docComment.size(); i < size; ++i ) {
0162         if ( docComment[i].isSpace() || docComment[i] == '*' || docComment[i] == '/' ) {
0163             // skip whitespace and comment-marker at beginning of line
0164             continue;
0165         } else if ( docComment[i] == '@' && docComment.midRef(i + 1, docCommentName.size()) == docCommentName ) {
0166             // find @return or similar
0167             i += docCommentName.size() + 1;
0168             // skip whitespace (at least one is required)
0169             if ( i >= size || !docComment[i].isSpace() ) {
0170                 // skip to next line
0171                 i = docComment.indexOf('\n', i);
0172                 if ( i == -1 ) {
0173                     break;
0174                 }
0175                 continue;
0176             } else if ( docComment[i] == '\n' ) {
0177                 continue;
0178             }
0179             ++i; // at least one whitespace
0180             while ( i < size && docComment[i].isSpace() ) {
0181                 ++i;
0182             }
0183             // finally get the typename
0184             int pos = i;
0185             while ( pos < size && !docComment[pos].isSpace() ) {
0186                 ++pos;
0187             }
0188             if ( pos > i ) {
0189                 matches << docComment.mid(i, pos - i);
0190                 if ( onlyOne ) {
0191                     break;
0192                 } else {
0193                     i = pos;
0194                 }
0195             }
0196         }
0197         // skip to next line
0198         i = docComment.indexOf('\n', i);
0199         if ( i == -1 ) {
0200             break;
0201         }
0202     }
0203 
0204     return matches;
0205 }
0206 
0207 AbstractType::Ptr TypeBuilder::parseDocComment(AstNode* node, const QString& docCommentName)
0208 {
0209     m_gotTypeFromDocComment = false;
0210     const QString& docComment = editor()->parseSession()->docComment(node->startToken);
0211 
0212     if ( !docComment.isEmpty() ) {
0213         const QStringList& matches = findInDocComment(docComment, docCommentName, true);
0214         if ( !matches.isEmpty() ) {
0215             AbstractType::Ptr type;
0216             if (matches.first() == QLatin1String("$this")) {
0217                 DUChainReadLocker lock(DUChain::lock());
0218                 if (currentContext()->owner()) {
0219                     type = currentContext()->owner()->abstractType();
0220                 }
0221             } else {
0222                 type = injectParseType(matches.first(), node);
0223             }
0224             if (type) {
0225                 m_gotTypeFromDocComment = true;
0226             }
0227             return type;
0228         }
0229     }
0230     return AbstractType::Ptr();
0231 }
0232 
0233 QList<AbstractType::Ptr> TypeBuilder::parseDocCommentParams(AstNode* node)
0234 {
0235     QList<AbstractType::Ptr> ret;
0236     QString docComment = editor()->parseSession()->docComment(node->startToken);
0237     if ( !docComment.isEmpty() ) {
0238         const QStringList& matches = findInDocComment(docComment, QStringLiteral("param"), false);
0239         if ( !matches.isEmpty() ) {
0240             ret.reserve(matches.size());
0241             foreach ( const QString& type, matches ) {
0242                 ret << parseType(type, node);
0243             }
0244         }
0245     }
0246     return ret;
0247 }
0248 
0249 AbstractType::Ptr TypeBuilder::getTypeForNode(AstNode* node)
0250 {
0251 
0252     AbstractType::Ptr type;
0253     if (node) {
0254         type = parseDocComment(node, QStringLiteral("var")); //we fully trust in @var typehint and don't try to evaluate ourself
0255         if (!type) {
0256             node->ducontext = currentContext();
0257             ExpressionParser ep;
0258             ep.setCreateProblems(true);
0259             ExpressionEvaluationResult res = ep.evaluateType(node, editor());
0260             if (res.hadUnresolvedIdentifiers()) {
0261                 m_hadUnresolvedIdentifiers = true;
0262             }
0263             type = res.type();
0264         }
0265     }
0266     if (!type) {
0267         type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
0268     }
0269     return type;
0270 }
0271 
0272 void TypeBuilder::visitClassDeclarationStatement(ClassDeclarationStatementAst* node)
0273 {
0274     // the predeclaration builder should have set up a type already
0275     // and the declarationbuilder should have set that as current type
0276     Q_ASSERT(hasCurrentType() && currentType<StructureType>());
0277 
0278     TypeBuilderBase::visitClassDeclarationStatement(node);
0279 }
0280 
0281 void TypeBuilder::visitInterfaceDeclarationStatement(InterfaceDeclarationStatementAst* node)
0282 {
0283     // the predeclaration builder should have set up a type already
0284     // and the declarationbuilder should have set that as current type
0285     Q_ASSERT(hasCurrentType() && currentType<StructureType>());
0286 
0287     TypeBuilderBase::visitInterfaceDeclarationStatement(node);
0288 }
0289 
0290 void TypeBuilder::visitTraitDeclarationStatement(TraitDeclarationStatementAst* node)
0291 {
0292     // the predeclaration builder should have set up a type already
0293     // and the declarationbuilder should have set that as current type
0294     Q_ASSERT(hasCurrentType() && currentType<StructureType>());
0295 
0296     TypeBuilderBase::visitTraitDeclarationStatement(node);
0297 }
0298 
0299 void TypeBuilder::visitClassStatement(ClassStatementAst *node)
0300 {
0301     if (node->methodName) {
0302         //method declaration
0303         m_currentFunctionParams = parseDocCommentParams(node);
0304 
0305         FunctionType::Ptr functionType = FunctionType::Ptr(new FunctionType());
0306         openType(functionType);
0307         openContextType(functionType);
0308 
0309         AbstractType::Ptr phpdocReturnType = parseDocComment(node, QStringLiteral("return"));
0310         functionType->setReturnType(returnType(node->returnType, phpdocReturnType, editor(), currentContext()));
0311         m_gotReturnTypeFromDocComment = functionType->returnType();
0312         updateCurrentType();
0313 
0314         TypeBuilderBase::visitClassStatement(node);
0315         if (currentType<FunctionType>() && !currentType<FunctionType>()->returnType()) {
0316             currentType<FunctionType>()->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
0317         }
0318         closeContextType();
0319         closeType();
0320     } else if (node->constsSequence) {
0321         //class constant
0322         TypeBuilderBase::visitClassStatement(node);
0323     } else if (node->propertyType) {
0324         m_gotTypeFromTypeHint = true;
0325         AbstractType::Ptr phpDocTypehint = parseDocComment(node, QStringLiteral("var"));
0326         AbstractType::Ptr type = propertyType(node, phpDocTypehint, editor(), currentContext());
0327 
0328         injectType(type);
0329         TypeBuilderBase::visitClassStatement(node);
0330         clearLastType();
0331         m_gotTypeFromTypeHint = false;
0332 
0333         if (m_gotTypeFromDocComment) {
0334             clearLastType();
0335             m_gotTypeFromDocComment = false;
0336         }
0337     } else {
0338         //member-variable
0339         parseDocComment(node, QStringLiteral("var"));
0340         TypeBuilderBase::visitClassStatement(node);
0341         if (m_gotTypeFromDocComment) {
0342             clearLastType();
0343             m_gotTypeFromDocComment = false;
0344         }
0345     }
0346 }
0347 
0348 void TypeBuilder::visitClassVariable(ClassVariableAst *node)
0349 {
0350     if (!m_gotTypeFromDocComment && !m_gotTypeFromTypeHint) {
0351         if (node->value) {
0352             openAbstractType(getTypeForNode(node->value));
0353         } else {
0354             openAbstractType(AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)));
0355         }
0356 
0357         TypeBuilderBase::visitClassVariable(node);
0358 
0359         closeType();
0360     } else {
0361         TypeBuilderBase::visitClassVariable(node);
0362     }
0363 }
0364 
0365 void TypeBuilder::visitConstantDeclaration(ConstantDeclarationAst* node)
0366 {
0367     if (!m_gotTypeFromDocComment || !currentAbstractType()) {
0368         AbstractType::Ptr type = getTypeForNode(node->scalar);
0369         type->setModifiers(type->modifiers() | AbstractType::ConstModifier);
0370         openAbstractType(type);
0371 
0372         TypeBuilderBase::visitConstantDeclaration(node);
0373 
0374         closeType();
0375     } else {
0376         currentAbstractType()->setModifiers(currentAbstractType()->modifiers() & AbstractType::ConstModifier);
0377         TypeBuilderBase::visitConstantDeclaration(node);
0378     }
0379 }
0380 
0381 void TypeBuilder::visitClassConstantDeclaration(ClassConstantDeclarationAst* node)
0382 {
0383     if (!m_gotTypeFromDocComment || !currentAbstractType()) {
0384         AbstractType::Ptr type = getTypeForNode(node->scalar);
0385         type->setModifiers(type->modifiers() | AbstractType::ConstModifier);
0386         openAbstractType(type);
0387 
0388         TypeBuilderBase::visitClassConstantDeclaration(node);
0389 
0390         closeType();
0391     } else {
0392         currentAbstractType()->setModifiers(currentAbstractType()->modifiers() & AbstractType::ConstModifier);
0393         TypeBuilderBase::visitClassConstantDeclaration(node);
0394     }
0395 }
0396 
0397 void TypeBuilder::visitParameter(ParameterAst *node)
0398 {
0399     AbstractType::Ptr phpDocTypehint;
0400     if (m_currentFunctionParams.count() > currentType<FunctionType>()->arguments().count()) {
0401         phpDocTypehint = m_currentFunctionParams.at(currentType<FunctionType>()->arguments().count());
0402     }
0403 
0404     AbstractType::Ptr type = parameterType(node, phpDocTypehint, editor(), currentContext());
0405 
0406     if (node->defaultValue) {
0407         QString symbol = m_editor->parseSession()->symbol(node->defaultValue);
0408 
0409         if (node->parameterType && node->parameterType->typehint &&
0410                 symbol.compare(QLatin1String("null"), Qt::CaseInsensitive) != 0 &&
0411                 type->contains(new IntegralTypeExtended(IntegralTypeExtended::TypeObject))) {
0412             reportError(i18n("Default value for parameters with an object type can only be NULL."), node->defaultValue);
0413         }
0414     }
0415 
0416     openAbstractType(type);
0417     TypeBuilderBase::visitParameter(node);
0418     closeType();
0419     DUChainWriteLocker lock(DUChain::lock());
0420     currentType<FunctionType>()->addArgument(type);
0421 }
0422 
0423 void TypeBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node)
0424 {
0425     m_currentFunctionParams = parseDocCommentParams(node);
0426     // the predeclarationbuilder should have already built the type
0427     // and the declarationbuilder should have set it to open
0428     Q_ASSERT(hasCurrentType());
0429     FunctionType::Ptr type = currentType<FunctionType>();
0430     Q_ASSERT(type);
0431 
0432     openContextType(type);
0433 
0434     AbstractType::Ptr phpdocReturnType = parseDocComment(node, QStringLiteral("return"));
0435     type->setReturnType(returnType(node->returnType, phpdocReturnType, editor(), currentContext()));
0436     m_gotReturnTypeFromDocComment = type->returnType();
0437 
0438     updateCurrentType();
0439 
0440     TypeBuilderBase::visitFunctionDeclarationStatement(node);
0441 
0442     if (!type->returnType()) {
0443         type->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
0444     }
0445 
0446     closeContextType();
0447 }
0448 
0449 void TypeBuilder::visitClosure(ClosureAst* node)
0450 {
0451     m_currentFunctionParams = parseDocCommentParams(node);
0452     FunctionType::Ptr type = FunctionType::Ptr(new FunctionType());
0453     openType(type);
0454     openContextType(type);
0455 
0456     AbstractType::Ptr phpdocReturnType = parseDocComment(node, QStringLiteral("return"));
0457     type->setReturnType(returnType(node->returnType, phpdocReturnType, editor(), currentContext()));
0458     m_gotReturnTypeFromDocComment = type->returnType();
0459 
0460     updateCurrentType();
0461 
0462     TypeBuilderBase::visitClosure(node);
0463 
0464     if (!type->returnType()) {
0465         type->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
0466     }
0467     closeContextType();
0468     closeType();
0469 }
0470 
0471 void TypeBuilder::visitAssignmentExpression(AssignmentExpressionAst* node)
0472 {
0473     // performance: only try to find type when we are actually in an assignment expr
0474     if (node->assignmentExpression || node->assignmentExpressionEqual) {
0475         openAbstractType(getTypeForNode(node));
0476     }
0477 
0478     TypeBuilderBase::visitAssignmentExpression(node);
0479 
0480     if (node->assignmentExpression || node->assignmentExpressionEqual) {
0481         closeType();
0482     }
0483 }
0484 
0485 void TypeBuilder::visitStaticVar(StaticVarAst *node)
0486 {
0487     openAbstractType(getTypeForNode(node->value));
0488 
0489     TypeBuilderBase::visitStaticVar(node);
0490 
0491     closeType();
0492 }
0493 
0494 void TypeBuilder::visitStatement(StatementAst* node)
0495 {
0496     TypeBuilderBase::visitStatement(node);
0497     if ( !m_gotReturnTypeFromDocComment && node->returnExpr && hasCurrentType() && currentType<FunctionType>())
0498     {
0499         FunctionType::Ptr ft = currentType<FunctionType>();
0500         // qCDebug(DUCHAIN) << "return" << (ft->returnType() ? ft->returnType()->toString() : "none") << lastType()->toString();
0501         AbstractType::Ptr type = getTypeForNode(node->returnExpr);
0502         if (type) {
0503             // ignore references for return values, PHP does so as well
0504             if ( auto rType = type.dynamicCast<ReferenceType>() ) {
0505                 type = rType->baseType();
0506             }
0507             if (ft->returnType() && !ft->returnType()->equals(type.data())) {
0508                 bool existingTypeIsCallable = ft->returnType().dynamicCast<IntegralTypeExtended>() &&
0509                     ft->returnType().staticCast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable;
0510                 bool newTypeIsCallable = type.dynamicCast<IntegralTypeExtended>() &&
0511                     type.staticCast<IntegralTypeExtended>()->dataType() == IntegralTypeExtended::TypeCallable;
0512                 if (ft->returnType().dynamicCast<IntegralType>()
0513                     && ft->returnType().staticCast<IntegralType>()->dataType() == IntegralType::TypeMixed)
0514                 {
0515                     //don't add TypeMixed to the list, just ignore
0516                     ft->setReturnType(type);
0517                 } else if ((existingTypeIsCallable && type.dynamicCast<FunctionType>()) || (newTypeIsCallable && ft->returnType().dynamicCast<FunctionType>())) {
0518                     //If one type is "callable" and the other a real function, the result is just a "callable".
0519                     ft->setReturnType(AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeCallable)));
0520                 } else {
0521                     UnsureType::Ptr retT;
0522                     if (ft->returnType().dynamicCast<UnsureType>()) {
0523                         //qCDebug(DUCHAIN) << "we already have an unsure type";
0524                         retT = ft->returnType().staticCast<UnsureType>();
0525                         if (type.dynamicCast<UnsureType>()) {
0526                             //qCDebug(DUCHAIN) << "add multiple to returnType";
0527                             FOREACH_FUNCTION(const IndexedType& t, type.staticCast<UnsureType>()->types) {
0528                                 retT->addType(t);
0529                             }
0530                         } else {
0531                             //qCDebug(DUCHAIN) << "add to returnType";
0532                             retT->addType(type->indexed());
0533                         }
0534                     } else {
0535                         if (type.dynamicCast<UnsureType>()) {
0536                             retT = type.staticCast<UnsureType>();
0537                         } else {
0538                             retT = new UnsureType();
0539                             retT->addType(type->indexed());
0540                         }
0541                         retT->addType(ft->returnType()->indexed());
0542                     }
0543                     ft->setReturnType(retT);
0544                 }
0545             } else {
0546                 ft->setReturnType(type);
0547             }
0548             updateCurrentType();
0549         }
0550     }
0551 
0552     AstNode *foreachNode = nullptr;
0553     if (node->foreachVar) {
0554         foreachNode = node->foreachVar;
0555     } else if (node->foreachExpr) {
0556         foreachNode = node->foreachExpr;
0557     } else if (node->foreachExprAsVar) {
0558         foreachNode = node->foreachExprAsVar;
0559     }
0560     if (foreachNode) {
0561         ExpressionVisitor v(editor());
0562         foreachNode->ducontext = currentContext();
0563         v.visitNode(foreachNode);
0564         DUChainReadLocker lock(DUChain::lock());
0565         bool foundType = false;
0566         if (auto type = v.result().type().dynamicCast<StructureType>()) {
0567             ClassDeclaration *classDec = dynamic_cast<ClassDeclaration*>(type->declaration(currentContext()->topContext()));
0568             if (!classDec) {
0569                 ///FIXME: this is just a hack for https://bugs.kde.org/show_bug.cgi?id=269369
0570                 ///       a proper fix needs full fledged two-pass, i.e. get rid of PreDeclarationBuilder
0571                 // 0 == global lookup and the declaration is found again...
0572                 classDec = dynamic_cast<ClassDeclaration*>(type->declaration(nullptr));
0573             }
0574             if (classDec) {
0575                 /// Qualified identifier for 'iterator'
0576                 static QualifiedIdentifier iteratorQId(QStringLiteral("iterator"));
0577                 iteratorQId.setExplicitlyGlobal(true);
0578                 ClassDeclaration* iteratorDecl = dynamic_cast<ClassDeclaration*>(
0579                     findDeclarationImport(ClassDeclarationType, iteratorQId).data()
0580                 );
0581                 Q_ASSERT(iteratorDecl);
0582                 if (classDec->isPublicBaseClass(iteratorDecl, currentContext()->topContext())) {
0583                     /// Qualified identifier for 'current'
0584                     static const QualifiedIdentifier currentQId(QStringLiteral("current"));
0585                     auto classContext = classDec->internalContext();
0586                     if (classContext) {
0587                         foreach (Declaration *d, classContext->findDeclarations(currentQId)) {
0588                             if (!dynamic_cast<ClassMethodDeclaration*>(d)) continue;
0589                             Q_ASSERT(d->type<FunctionType>());
0590                             injectType(d->type<FunctionType>()->returnType());
0591                             foundType = true;
0592                             // qCDebug(DUCHAIN) << "that's it: " << d->type<FunctionType>()->returnType()->toString();
0593                         }
0594                     }
0595                 }
0596             }
0597         } else if ( auto a_type = v.result().type().dynamicCast<ArrayType>() ) {
0598             injectType(a_type->elementType());
0599             foundType = true;
0600         }
0601 
0602         if (!foundType) {
0603             injectType(AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)));
0604         }
0605     }
0606 }
0607 
0608 void TypeBuilder::visitCatchItem(Php::CatchItemAst *node)
0609 {
0610     TypeBuilderBase::visitCatchItem(node);
0611 
0612     const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->catchClassSequence->front();
0613 
0614     if (node->catchClassSequence->count() == 1) {
0615         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType,
0616                                                        identifierForNamespace(it->element, m_editor));
0617 
0618         if (dec && dec->abstractType()) {
0619             openAbstractType(dec->abstractType());
0620             closeType();
0621         }
0622     } else {
0623         UnsureType::Ptr decs(new UnsureType());
0624 
0625         forever {
0626             DeclarationPointer dec = findDeclarationImport(ClassDeclarationType,
0627                                                            identifierForNamespace(it->element, m_editor));
0628 
0629             if (dec && dec->abstractType()) {
0630                 decs->addType(dec->abstractType()->indexed());
0631             }
0632 
0633             if ( it->hasNext() ) {
0634                 it = it->next;
0635             } else {
0636                 break;
0637             }
0638         }
0639 
0640         if (decs) {
0641             openAbstractType(decs);
0642             closeType();
0643         }
0644     }
0645 }
0646 
0647 void TypeBuilder::visitVarExpression(Php::VarExpressionAst *node)
0648 {
0649     if (hasCurrentContextType() && node->isGenerator != -1 && !m_gotReturnTypeFromDocComment) {
0650         auto ft = currentContextType().dynamicCast<FunctionType>();
0651         static QualifiedIdentifier generatorQId(QStringLiteral("generator"));
0652         generatorQId.setExplicitlyGlobal(true);
0653         DeclarationPointer generatorDecl = findDeclarationImport(ClassDeclarationType, generatorQId);
0654 
0655         if (ft && generatorDecl) {
0656             AbstractType::Ptr generatorType = generatorDecl->abstractType();
0657 
0658             if (generatorType) {
0659                 ft->setReturnType(generatorType);
0660             }
0661         }
0662 
0663         updateCurrentType();
0664     }
0665 
0666     TypeBuilderBase::visitVarExpression(node);
0667 }
0668 
0669 void TypeBuilder::updateCurrentType()
0670 {
0671     // do nothing
0672 }
0673 
0674 }
0675