File indexing completed on 2024-04-14 14:47:47
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 "helper.h" 0008 0009 #include <KParts/MainWindow> 0010 #include <KLocalizedString> 0011 0012 #include <language/duchain/ducontext.h> 0013 #include <language/duchain/duchainlock.h> 0014 #include <language/duchain/persistentsymboltable.h> 0015 #include <language/duchain/duchain.h> 0016 #include <language/duchain/stringhelpers.h> 0017 #include <language/duchain/parsingenvironment.h> 0018 #include <language/duchain/types/unsuretype.h> 0019 #include <language/duchain/types/integraltype.h> 0020 #include <language/duchain/types/arraytype.h> 0021 #include <interfaces/icore.h> 0022 #include <interfaces/iprojectcontroller.h> 0023 #include <interfaces/iuicontroller.h> 0024 #include <interfaces/iproject.h> 0025 #include <project/projectmodel.h> 0026 #include <util/path.h> 0027 0028 #include "editorintegrator.h" 0029 #include "../parser/parsesession.h" 0030 #include "phpast.h" 0031 #include "phpdefaultvisitor.h" 0032 #include "declarations/classdeclaration.h" 0033 #include "declarations/classmethoddeclaration.h" 0034 #include "declarations/functiondeclaration.h" 0035 #include "types/integraltypeextended.h" 0036 #include "expressionparser.h" 0037 #include "expressionvisitor.h" 0038 0039 #include "duchaindebug.h" 0040 0041 #define ifDebug(x) 0042 0043 using namespace KDevelop; 0044 0045 namespace Php 0046 { 0047 0048 bool isMatch(Declaration* declaration, DeclarationType declarationType) 0049 { 0050 if (declarationType == ClassDeclarationType 0051 && dynamic_cast<ClassDeclaration*>(declaration) 0052 ) { 0053 return true; 0054 } else if (declarationType == FunctionDeclarationType 0055 && dynamic_cast<FunctionDeclaration*>(declaration) 0056 ) { 0057 return true; 0058 } else if (declarationType == ConstantDeclarationType 0059 && declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier 0060 && (!declaration->context() || declaration->context()->type() != DUContext::Class) 0061 ) { 0062 return true; 0063 } else if (declarationType == GlobalVariableDeclarationType 0064 && declaration->kind() == Declaration::Instance 0065 && !(declaration->abstractType() && declaration->abstractType()->modifiers() & AbstractType::ConstModifier) 0066 ) { 0067 return true; 0068 } else if (declarationType == NamespaceDeclarationType 0069 && (declaration->kind() == Declaration::Namespace || declaration->kind() == Declaration::NamespaceAlias || dynamic_cast<ClassDeclaration*>(declaration)) ) 0070 { 0071 return true; 0072 } 0073 return false; 0074 } 0075 0076 bool isGenericClassTypehint(NamespacedIdentifierAst* node, EditorIntegrator *editor) 0077 { 0078 const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); 0079 QString typehint = editor->parseSession()->symbol(it->element); 0080 0081 if (typehint.compare(QLatin1String("bool"), Qt::CaseInsensitive) == 0) { 0082 return false; 0083 } else if (typehint.compare(QLatin1String("float"), Qt::CaseInsensitive) == 0) { 0084 return false; 0085 } else if (typehint.compare(QLatin1String("int"), Qt::CaseInsensitive) == 0) { 0086 return false; 0087 } else if (typehint.compare(QLatin1String("string"), Qt::CaseInsensitive) == 0) { 0088 return false; 0089 } else if (typehint.compare(QLatin1String("iterable"), Qt::CaseInsensitive) == 0) { 0090 return false; 0091 } else if (typehint.compare(QLatin1String("object"), Qt::CaseInsensitive) == 0) { 0092 return false; 0093 } else if (typehint.compare(QLatin1String("mixed"), Qt::CaseInsensitive) == 0) { 0094 return false; 0095 } else { 0096 return true; 0097 } 0098 } 0099 0100 template <class T> 0101 bool isClassTypehint(T* typeHint, EditorIntegrator *editor) 0102 { 0103 Q_ASSERT(typeHint); 0104 0105 if (typeHint->callableType != -1) { 0106 return false; 0107 } else if (typeHint->voidType != -1) { 0108 return false; 0109 } else if (typeHint->genericType) { 0110 if (typeHint->genericType->arrayType != -1) { 0111 return false; 0112 } else { 0113 return isGenericClassTypehint(typeHint->genericType->genericType, editor); 0114 } 0115 } else { 0116 return false; 0117 } 0118 } 0119 0120 bool hasClassTypehint(UnionParameterTypeAst* unionType, EditorIntegrator *editor) 0121 { 0122 Q_ASSERT(unionType); 0123 0124 const KDevPG::ListNode< ParameterTypeHintAst* >* it = unionType->unionTypeSequence->front(); 0125 0126 do { 0127 if (isClassTypehint(it->element, editor)) { 0128 return true; 0129 } 0130 } while(it->hasNext() && (it = it->next)); 0131 0132 return false; 0133 } 0134 0135 bool hasClassTypehint(UnionPropertyTypeAst* unionType, EditorIntegrator *editor) 0136 { 0137 Q_ASSERT(unionType); 0138 0139 const KDevPG::ListNode< PropertyTypeHintAst* >* it = unionType->unionTypeSequence->front(); 0140 0141 do { 0142 if (isClassTypehint(it->element, editor)) { 0143 return true; 0144 } 0145 } while(it->hasNext() && (it = it->next)); 0146 0147 return false; 0148 } 0149 0150 bool hasClassTypehint(UnionReturnTypeAst* unionType, EditorIntegrator *editor) 0151 { 0152 Q_ASSERT(unionType); 0153 0154 const KDevPG::ListNode< ReturnTypeHintAst* >* it = unionType->unionTypeSequence->front(); 0155 0156 do { 0157 if (isClassTypehint(it->element, editor)) { 0158 return true; 0159 } 0160 } while(it->hasNext() && (it = it->next)); 0161 0162 return false; 0163 } 0164 0165 DeclarationPointer findDeclarationImportHelper(DUContext* currentContext, const QualifiedIdentifier& id, 0166 DeclarationType declarationType) 0167 { 0168 /// Qualified identifier for 'self' 0169 static const QualifiedIdentifier selfQId(QStringLiteral("self")); 0170 /// Qualified identifier for 'parent' 0171 static const QualifiedIdentifier parentQId(QStringLiteral("parent")); 0172 /// Qualified identifier for 'static' 0173 static const QualifiedIdentifier staticQId(QStringLiteral("static")); 0174 0175 QualifiedIdentifier lookup; 0176 0177 if (id.explicitlyGlobal()) { 0178 ifDebug(qCDebug(DUCHAIN) << id.toString() << declarationType;) 0179 0180 lookup = id; 0181 lookup.setExplicitlyGlobal(false); 0182 } else { 0183 lookup = identifierWithNamespace(id, currentContext); 0184 0185 ifDebug(qCDebug(DUCHAIN) << lookup.toString() << declarationType;) 0186 } 0187 0188 if (declarationType == ClassDeclarationType && id == selfQId) { 0189 DUChainReadLocker lock(DUChain::lock()); 0190 if (currentContext->type() == DUContext::Class) { 0191 return DeclarationPointer(currentContext->owner()); 0192 } else if (currentContext->parentContext() && currentContext->parentContext()->type() == DUContext::Class) { 0193 return DeclarationPointer(currentContext->parentContext()->owner()); 0194 } else { 0195 return DeclarationPointer(); 0196 } 0197 } else if (declarationType == ClassDeclarationType && id == staticQId) { 0198 DUChainReadLocker lock; 0199 if (currentContext->type() == DUContext::Class) { 0200 return DeclarationPointer(currentContext->owner()); 0201 } else if (currentContext->parentContext() && currentContext->parentContext()->type() == DUContext::Class) { 0202 return DeclarationPointer(currentContext->parentContext()->owner()); 0203 } else { 0204 return DeclarationPointer(); 0205 } 0206 } else if (declarationType == ClassDeclarationType && id == parentQId) { 0207 //there can be just one Class-Context imported 0208 DUChainReadLocker lock; 0209 DUContext* classCtx = nullptr; 0210 if (currentContext->type() == DUContext::Class) { 0211 classCtx = currentContext; 0212 } else if (currentContext->parentContext() && currentContext->parentContext()->type() == DUContext::Class) { 0213 classCtx = currentContext->parentContext(); 0214 } 0215 if (classCtx) { 0216 foreach(const DUContext::Import &i, classCtx->importedParentContexts()) { 0217 DUContext* ctx = i.context(classCtx->topContext()); 0218 if (ctx && ctx->type() == DUContext::Class) { 0219 return DeclarationPointer(ctx->owner()); 0220 } 0221 } 0222 } 0223 return DeclarationPointer(); 0224 } else { 0225 DUChainReadLocker lock; 0226 QList<Declaration*> foundDeclarations = currentContext->topContext()->findDeclarations(lookup); 0227 0228 foreach(Declaration *declaration, foundDeclarations) { 0229 if (isMatch(declaration, declarationType)) { 0230 return DeclarationPointer(declaration); 0231 } 0232 } 0233 if ( currentContext->url() == internalFunctionFile() ) { 0234 // when compiling php internal functions, we don't need to ask the persistent symbol table for anything 0235 return DeclarationPointer(); 0236 } 0237 0238 lock.unlock(); 0239 0240 if (declarationType != GlobalVariableDeclarationType) { 0241 ifDebug(qCDebug(DUCHAIN) << "No declarations found with findDeclarations, trying through PersistentSymbolTable";) 0242 DeclarationPointer decl; 0243 0244 decl = findDeclarationInPST(currentContext, lookup, declarationType); 0245 0246 if (decl) { 0247 ifDebug(qCDebug(DUCHAIN) << "PST declaration exists";) 0248 } else { 0249 ifDebug(qCDebug(DUCHAIN) << "PST declaration does not exist";) 0250 } 0251 return decl; 0252 } 0253 } 0254 0255 ifDebug(qCDebug(DUCHAIN) << "returning 0";) 0256 return DeclarationPointer(); 0257 } 0258 0259 DeclarationPointer findDeclarationInPST(DUContext* currentContext, QualifiedIdentifier id, DeclarationType declarationType) 0260 { 0261 DeclarationPointer ret; 0262 0263 ifDebug(qCDebug(DUCHAIN) << "PST: " << id.toString() << declarationType;) 0264 DUChainWriteLocker wlock; 0265 0266 /// Indexed string for 'Php', identifies environment files from this language plugin 0267 static const IndexedString phpLangString("Php"); 0268 auto visitor = [&](const IndexedDeclaration &indexedDeclaration) { 0269 ParsingEnvironmentFilePointer env = DUChain::self()->environmentFileForDocument(indexedDeclaration.indexedTopContext()); 0270 if(!env) { 0271 ifDebug(qCDebug(DUCHAIN) << "skipping declaration, missing meta-data";) 0272 return PersistentSymbolTable::VisitorState::Continue; 0273 } 0274 if(env->language() != phpLangString) { 0275 ifDebug(qCDebug(DUCHAIN) << "skipping declaration, invalid language" << env->language().str();) 0276 return PersistentSymbolTable::VisitorState::Continue; 0277 } 0278 0279 const auto declaration = indexedDeclaration.declaration(); 0280 if (!declaration) { 0281 ifDebug(qCDebug(DUCHAIN) << "skipping declaration, doesn't have declaration";) 0282 return PersistentSymbolTable::VisitorState::Continue; 0283 } else if (!isMatch(declaration, declarationType)) { 0284 ifDebug(qCDebug(DUCHAIN) << "skipping declaration, doesn't match with declarationType";) 0285 return PersistentSymbolTable::VisitorState::Continue; 0286 } 0287 0288 TopDUContext* top = declaration->context()->topContext(); 0289 currentContext->topContext()->addImportedParentContext(top); 0290 currentContext->topContext()->parsingEnvironmentFile() 0291 ->addModificationRevisions(top->parsingEnvironmentFile()->allModificationRevisions()); 0292 currentContext->topContext()->updateImportsCache(); 0293 ifDebug(qCDebug(DUCHAIN) << "using" << declarations[i].declaration()->toString() << top->url();) 0294 ret = declaration; 0295 return PersistentSymbolTable::VisitorState::Break; 0296 }; 0297 PersistentSymbolTable::self().visitDeclarations(id, visitor); 0298 0299 return ret; 0300 } 0301 0302 QByteArray formatComment(AstNode* node, EditorIntegrator* editor) 0303 { 0304 return KDevelop::formatComment(editor->parseSession()->docComment(node->startToken).toUtf8()); 0305 } 0306 0307 //Helper visitor to extract a commonScalar node 0308 //used to get the value of an function call argument 0309 class ScalarExpressionVisitor : public DefaultVisitor 0310 { 0311 public: 0312 ScalarExpressionVisitor() : m_node(nullptr) {} 0313 CommonScalarAst* node() const { 0314 return m_node; 0315 } 0316 private: 0317 void visitCommonScalar(CommonScalarAst* node) override { 0318 m_node = node; 0319 } 0320 CommonScalarAst* m_node; 0321 }; 0322 0323 CommonScalarAst* findCommonScalar(AstNode* node) 0324 { 0325 ScalarExpressionVisitor visitor; 0326 visitor.visitNode(node); 0327 return visitor.node(); 0328 } 0329 0330 static bool includeExists(const Path &include) 0331 { 0332 const QString path = include.pathOrUrl(); 0333 { 0334 DUChainReadLocker lock; 0335 if (DUChain::self()->chainForDocument(IndexedString(path))) { 0336 return true; 0337 } 0338 } 0339 if ( include.isLocalFile() ) { 0340 return QFile::exists(path); 0341 } else { 0342 return false; 0343 } 0344 } 0345 0346 static IndexedString findIncludeFile(const QString &includePath, const IndexedString ¤tDocument) 0347 { 0348 if ( includePath.isEmpty() ) { 0349 return IndexedString(); 0350 } 0351 0352 // check remote files 0353 if ( includePath.startsWith(QLatin1String("http://"), Qt::CaseInsensitive) 0354 || includePath.startsWith(QLatin1String("ftp://"), Qt::CaseInsensitive) ) { 0355 // always expect remote includes to exist 0356 return IndexedString(includePath); 0357 } 0358 0359 const Path currentPath(currentDocument.str()); 0360 0361 // look for file relative to current url 0362 Path include = Path(currentPath.parent(), includePath); 0363 if ( includeExists(include) ) { 0364 return IndexedString(include.pathOrUrl()); 0365 } 0366 0367 // in the first round look for a project that is a parent of the current document 0368 // in the next round look for any project 0369 for (int i = 0; i < 2; ++i) { 0370 foreach(IProject* project, ICore::self()->projectController()->projects()) { 0371 if ( !i && !project->path().isParentOf(currentPath)) { 0372 continue; 0373 } 0374 include = Path(project->path(), includePath); 0375 if ( includeExists(include) ) { 0376 return IndexedString(include.pathOrUrl()); 0377 } 0378 } 0379 } 0380 0381 //TODO configurable include paths 0382 0383 return IndexedString(); 0384 } 0385 0386 IndexedString getIncludeFileForNode(UnaryExpressionAst* node, EditorIntegrator* editor) { 0387 if ( node->includeExpression ) { 0388 //find name of the constant (first argument of the function call) 0389 CommonScalarAst* scalar = findCommonScalar(node->includeExpression); 0390 if (scalar && scalar->string != -1) { 0391 QString str = editor->parseSession()->symbol(scalar->string); 0392 str = str.mid(1, str.length() - 2); 0393 if ( str == QLatin1String(".") || str == QLatin1String("..") || str.endsWith('/') ) { 0394 return IndexedString(); 0395 } 0396 return findIncludeFile(str, editor->parseSession()->currentDocument()); 0397 } 0398 } 0399 0400 return IndexedString(); 0401 } 0402 0403 QString prettyName(Declaration* dec) { 0404 if (!dec) { 0405 return {}; 0406 } else if ( dec->context() && dec->context()->type() == DUContext::Class && dec->isFunctionDeclaration() ) { 0407 ClassMethodDeclaration* classMember = dynamic_cast<ClassMethodDeclaration*>(dec); 0408 Q_ASSERT(classMember); 0409 return classMember->prettyName().str(); 0410 } else if ( dec->isFunctionDeclaration() ) { 0411 FunctionDeclaration* func = dynamic_cast<FunctionDeclaration*>(dec); 0412 Q_ASSERT(func); 0413 return func->prettyName().str(); 0414 } else if ( dec->internalContext() && dec->internalContext()->type() == DUContext::Class ) { 0415 ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(dec); 0416 Q_ASSERT(classDec); 0417 return classDec->prettyName().str(); 0418 } else { 0419 return dec->identifier().toString(); 0420 } 0421 } 0422 0423 const KDevelop::IndexedString& internalFunctionFile() 0424 { 0425 static const KDevelop::IndexedString internalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevphpsupport/phpfunctions.php"))); 0426 return internalFile; 0427 } 0428 0429 const KDevelop::IndexedString& phpLanguageString() 0430 { 0431 static const KDevelop::IndexedString phpLangString("Php"); 0432 return phpLangString; 0433 } 0434 0435 const IndexedString& internalTestFile() 0436 { 0437 static const KDevelop::IndexedString phpUnitFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevphpsupport/phpunitdeclarations.php"))); 0438 return phpUnitFile; 0439 } 0440 0441 QualifiedIdentifier identifierForNamespace(NamespacedIdentifierAst* node, EditorIntegrator* editor, bool lastIsConstIdentifier) 0442 { 0443 QualifiedIdentifier id; 0444 if (node->isGlobal != -1) { 0445 id.setExplicitlyGlobal(true); 0446 } 0447 const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); 0448 do { 0449 if (lastIsConstIdentifier && !it->hasNext()) { 0450 id.push(Identifier(editor->parseSession()->symbol(it->element))); 0451 } else { 0452 id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); 0453 } 0454 } while (it->hasNext() && (it = it->next)); 0455 return id; 0456 } 0457 0458 QualifiedIdentifier identifierForNamespace(NamespacedIdentifierBeforeGroupedNamespaceAst* node, EditorIntegrator* editor, bool lastIsConstIdentifier) 0459 { 0460 QualifiedIdentifier id; 0461 if (node->isGlobal != -1) { 0462 id.setExplicitlyGlobal(true); 0463 } 0464 const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); 0465 do { 0466 if (lastIsConstIdentifier && !it->hasNext()) { 0467 id.push(Identifier(editor->parseSession()->symbol(it->element))); 0468 } else { 0469 id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); 0470 } 0471 } while (it->hasNext() && (it = it->next)); 0472 return id; 0473 } 0474 0475 QualifiedIdentifier identifierForNamespace( 0476 NamespacedIdentifierBeforeGroupedNamespaceAst* prefixNode, 0477 InnerUseNamespaceAst* node, 0478 EditorIntegrator* editor, 0479 bool lastIsConstIdentifier) 0480 { 0481 QualifiedIdentifier id; 0482 if (prefixNode->isGlobal != -1) { 0483 id.setExplicitlyGlobal(true); 0484 } 0485 const KDevPG::ListNode< IdentifierAst* >* it; 0486 0487 it = prefixNode->namespaceNameSequence->front(); 0488 do { 0489 id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); 0490 } 0491 while (it->hasNext() && (it = it->next)); 0492 it = node->namespaceNameSequence->front(); 0493 do { 0494 if (lastIsConstIdentifier && !it->hasNext()) { 0495 id.push(Identifier(editor->parseSession()->symbol(it->element))); 0496 } else { 0497 id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); 0498 } 0499 } while (it->hasNext() && (it = it->next)); 0500 return id; 0501 } 0502 0503 QualifiedIdentifier identifierWithNamespace(const QualifiedIdentifier& base, DUContext* context) 0504 { 0505 DUChainReadLocker lock; 0506 auto scope = context; 0507 while (scope && scope->type() != DUContext::Namespace) { 0508 scope = scope->parentContext(); 0509 } 0510 0511 if (scope) { 0512 return scope->scopeIdentifier() + base; 0513 } else { 0514 return base; 0515 } 0516 } 0517 0518 AbstractType::Ptr determineGenericTypeHint(const GenericTypeHintAst* genericType, EditorIntegrator *editor, DUContext* currentContext) 0519 { 0520 Q_ASSERT(genericType); 0521 AbstractType::Ptr type; 0522 0523 if (genericType->arrayType != -1) { 0524 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeArray)); 0525 } else if (genericType->genericType) { 0526 NamespacedIdentifierAst* node = genericType->genericType; 0527 const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); 0528 QString typehint = editor->parseSession()->symbol(it->element); 0529 0530 if (typehint.compare(QLatin1String("bool"), Qt::CaseInsensitive) == 0) { 0531 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)); 0532 } else if (typehint.compare(QLatin1String("float"), Qt::CaseInsensitive) == 0) { 0533 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeFloat)); 0534 } else if (typehint.compare(QLatin1String("int"), Qt::CaseInsensitive) == 0) { 0535 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)); 0536 } else if (typehint.compare(QLatin1String("string"), Qt::CaseInsensitive) == 0) { 0537 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeString)); 0538 } else if (typehint.compare(QLatin1String("object"), Qt::CaseInsensitive) == 0) { 0539 type = AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeObject)); 0540 } else if (typehint.compare(QLatin1String("mixed"), Qt::CaseInsensitive) == 0) { 0541 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); 0542 } else if (typehint.compare(QLatin1String("iterable"), Qt::CaseInsensitive) == 0) { 0543 DeclarationPointer traversableDecl = findDeclarationImportHelper(currentContext, QualifiedIdentifier(u"traversable"), ClassDeclarationType); 0544 0545 if (traversableDecl) { 0546 UnsureType::Ptr unsure(new UnsureType()); 0547 AbstractType::Ptr arrayType = AbstractType::Ptr(new IntegralType(IntegralType::TypeArray)); 0548 unsure->addType(arrayType->indexed()); 0549 unsure->addType(traversableDecl->abstractType()->indexed()); 0550 0551 type = AbstractType::Ptr(unsure); 0552 } 0553 } else { 0554 //don't use openTypeFromName as it uses cursor for findDeclarations 0555 DeclarationPointer decl = findDeclarationImportHelper(currentContext, 0556 identifierForNamespace(genericType->genericType, editor), ClassDeclarationType); 0557 if (decl) { 0558 type = decl->abstractType(); 0559 } 0560 } 0561 } 0562 0563 return type; 0564 } 0565 0566 template <class T> 0567 AbstractType::Ptr determineTypehintFromList(const T* list, EditorIntegrator *editor, DUContext* currentContext) 0568 { 0569 Q_ASSERT(list); 0570 AbstractType::Ptr type; 0571 0572 if (list->element->callableType != -1) { 0573 type = AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeCallable)); 0574 } else if (list->element->voidType != -1) { 0575 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)); 0576 } else if (list->element->genericType) { 0577 type = determineGenericTypeHint(list->element->genericType, editor, currentContext); 0578 } 0579 0580 if (list->count() > 1) { 0581 auto unsure = type.dynamicCast<UnsureType>(); 0582 if (!unsure) { 0583 unsure = UnsureType::Ptr(new UnsureType()); 0584 unsure->addType(type->indexed()); 0585 } 0586 0587 while(list->hasNext() && (list = list->next)) { 0588 if (list->element->callableType != -1) { 0589 unsure->addType(AbstractType::Ptr(new IntegralTypeExtended(IntegralTypeExtended::TypeCallable))->indexed()); 0590 } else if (list->element->voidType != -1) { 0591 unsure->addType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid))->indexed()); 0592 } else if (list->element->genericType) { 0593 unsure->addType(determineGenericTypeHint(list->element->genericType, editor, currentContext)->indexed()); 0594 } 0595 0596 if (list->element->isNullable != -1) { 0597 AbstractType::Ptr nullType = AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)); 0598 unsure->addType(nullType->indexed()); 0599 } 0600 } 0601 0602 type = unsure; 0603 } else if (type && list->element->isNullable != -1) { 0604 AbstractType::Ptr nullType = AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)); 0605 auto unsure = type.dynamicCast<UnsureType>(); 0606 if (unsure) { 0607 unsure->addType(nullType->indexed()); 0608 } else { 0609 unsure = UnsureType::Ptr(new UnsureType()); 0610 unsure->addType(type->indexed()); 0611 unsure->addType(nullType->indexed()); 0612 0613 type = unsure; 0614 } 0615 } 0616 0617 return type; 0618 } 0619 0620 template <class T> 0621 AbstractType::Ptr determineTypehint(const T* unionType, EditorIntegrator *editor, DUContext* currentContext) 0622 { 0623 Q_ASSERT(unionType); 0624 AbstractType::Ptr type; 0625 0626 type = determineTypehintFromList(unionType->unionTypeSequence->front(), editor, currentContext); 0627 0628 return type; 0629 } 0630 0631 AbstractType::Ptr parameterType(const ParameterAst* node, AbstractType::Ptr phpDocTypehint, EditorIntegrator* editor, DUContext* currentContext) 0632 { 0633 AbstractType::Ptr type; 0634 if (node->parameterType && node->parameterType->typehint) { 0635 type = determineTypehint(node->parameterType->typehint, editor, currentContext); 0636 } 0637 if (node->defaultValue) { 0638 ExpressionVisitor v(editor); 0639 node->defaultValue->ducontext = currentContext; 0640 v.visitNode(node->defaultValue); 0641 AbstractType::Ptr defaultValueType = v.result().type(); 0642 0643 /* 0644 * If a typehint is already set, default values can only be the same type or `null` in PHP 0645 * If it's the same type, the type is already correctly set, 0646 * so we can ignore this case. 0647 * If it's null (which cannot be a typehint), add it as UnsureType 0648 */ 0649 if (type && defaultValueType.dynamicCast<IntegralType>() && defaultValueType.staticCast<IntegralType>()->dataType() == IntegralType::TypeNull) { 0650 auto unsure = type.dynamicCast<UnsureType>(); 0651 if (unsure) { 0652 AbstractType::Ptr nullType = AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)); 0653 unsure->addType(defaultValueType->indexed()); 0654 } else { 0655 unsure = UnsureType::Ptr(new UnsureType()); 0656 unsure->addType(type->indexed()); 0657 unsure->addType(defaultValueType->indexed()); 0658 0659 type = unsure; 0660 } 0661 } else if (!type) { 0662 //Otherwise, let the default value dictate the parameter type 0663 type = defaultValueType; 0664 } 0665 } 0666 if (!type) { 0667 if (phpDocTypehint) { 0668 type = phpDocTypehint; 0669 } else { 0670 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); 0671 } 0672 } 0673 0674 if ( node->isRef != -1 ) { 0675 ReferenceType::Ptr p( new ReferenceType() ); 0676 p->setBaseType( type ); 0677 0678 type = p; 0679 } 0680 0681 if (node->isVariadic != -1) { 0682 auto *container = new KDevelop::ArrayType(); 0683 container->setElementType(type); 0684 type = AbstractType::Ptr(container); 0685 } 0686 0687 Q_ASSERT(type); 0688 return type; 0689 } 0690 0691 AbstractType::Ptr propertyType(const ClassStatementAst* node, AbstractType::Ptr phpDocTypehint, EditorIntegrator* editor, DUContext* currentContext) 0692 { 0693 AbstractType::Ptr type; 0694 if (node->propertyType && node->propertyType->typehint) { 0695 type = determineTypehint(node->propertyType->typehint, editor, currentContext); 0696 } 0697 0698 if (!type) { 0699 if (phpDocTypehint) { 0700 type = phpDocTypehint; 0701 } else { 0702 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); 0703 } 0704 } 0705 0706 Q_ASSERT(type); 0707 return type; 0708 } 0709 0710 AbstractType::Ptr returnType(const ReturnTypeAst* node, AbstractType::Ptr phpDocTypehint, EditorIntegrator* editor, DUContext* currentContext) { 0711 AbstractType::Ptr type; 0712 if (node && node->typehint) { 0713 type = determineTypehint(node->typehint, editor, currentContext); 0714 } 0715 if (!type) { 0716 type = phpDocTypehint; 0717 } 0718 return type; 0719 } 0720 0721 }