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