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 &currentDocument)
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 }