File indexing completed on 2024-03-24 16:04:26

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 "expressionvisitor.h"
0008 #include "parsesession.h"
0009 #include "editorintegrator.h"
0010 #include "helper.h"
0011 #include "declarations/variabledeclaration.h"
0012 #include "declarations/classdeclaration.h"
0013 
0014 #include <language/duchain/topducontext.h>
0015 #include <language/duchain/duchain.h>
0016 #include <language/duchain/duchainlock.h>
0017 #include <language/duchain/persistentsymboltable.h>
0018 #include <language/duchain/types/functiontype.h>
0019 #include <language/duchain/types/integraltype.h>
0020 #include <language/duchain/types/structuretype.h>
0021 #include <language/duchain/types/unsuretype.h>
0022 
0023 #include "duchaindebug.h"
0024 
0025 #define ifDebug(x)
0026 
0027 using namespace KDevelop;
0028 
0029 namespace Php
0030 {
0031 
0032 ExpressionVisitor::ExpressionVisitor(EditorIntegrator* editor)
0033         : m_editor(editor), m_createProblems(false),
0034           m_offset(CursorInRevision::invalid()), m_currentContext(nullptr),
0035           m_isAssignmentExpressionEqual(false),
0036           m_inDefine(false)
0037 {
0038 }
0039 
0040 DeclarationPointer ExpressionVisitor::processVariable(VariableIdentifierAst* variable)
0041 {
0042     Q_ASSERT(m_currentContext);
0043 
0044     CursorInRevision position = m_editor->findPosition(variable->variable, EditorIntegrator::BackEdge);
0045 
0046     if ( m_offset.isValid() ) {
0047         position.line += m_offset.line;
0048         position.column += m_offset.column;
0049     }
0050 
0051     DeclarationPointer ret;
0052     Identifier identifier = identifierForNode(variable).last();
0053 
0054     ifDebug(qCDebug(DUCHAIN) << "processing variable" << identifier.toString() << position.castToSimpleCursor();)
0055 
0056     DUChainReadLocker lock;
0057 
0058     if (identifier.nameEquals(Identifier(QStringLiteral("this")))) {
0059         if (m_currentContext->parentContext()
0060                 && m_currentContext->parentContext()->type() == DUContext::Class
0061                 && m_currentContext->parentContext()->owner()) {
0062             ret = m_currentContext->parentContext()->owner();
0063         }
0064     } else {
0065         //DontSearchInParent-flag because (1) in Php global variables aren't available in function
0066         //context and (2) a function body consists of a single context (so this is no problem)
0067         ret = findVariableDeclaration(m_currentContext, identifier, position, DUContext::DontSearchInParent);
0068     }
0069     if (!ret && m_currentContext->type() == DUContext::Namespace)
0070     {
0071         ret = findVariableDeclaration(m_currentContext, identifier, position, DUContext::NoSearchFlags);
0072     }
0073     if (!ret) {
0074         //look for a function argument
0075         ///TODO: why doesn't m_currentContext->findDeclarations() work?
0076         ///      evaluate if the stuff below is fast enough (faster?) than findDeclarations()
0077         ///see r1028306
0078         foreach(const DUContext::Import &import, m_currentContext->importedParentContexts() ) {
0079             if ( !import.isDirect() || import.position > position ) {
0080                 continue;
0081             }
0082             DUContext* ctx = import.context(m_currentContext->topContext());
0083             if ( ctx->type() == DUContext::Function ) {
0084                 QList<Declaration*> args = ctx->findLocalDeclarations(identifier);
0085                 if ( !args.isEmpty() ) {
0086                     ret = args.first();
0087                     break;
0088                 }
0089             }
0090         }
0091     }
0092     if (!ret) {
0093         //look for a superglobal variable
0094         foreach(Declaration* dec, m_currentContext->topContext()->findDeclarations(identifier, position)) {
0095             VariableDeclaration* varDec = dynamic_cast<VariableDeclaration*>(dec);
0096             if (varDec && varDec->isSuperglobal()) {
0097                 ret = dec;
0098                 break;
0099             }
0100         }
0101     }
0102 
0103     lock.unlock();
0104 
0105     if ( !m_isAssignmentExpressionEqual || identifier.nameEquals( Identifier(QStringLiteral("this")) )
0106          // might be something like $s = $s . $s; in which case we have to add a use for the first $s
0107          || (ret && ret->range().end < position) )
0108     {
0109         // also don't report uses for the place of declaration
0110         if (!ret || ret->range().end != position) {
0111             usingDeclaration(variable, ret);
0112         }
0113     }
0114     ifDebug(qCDebug(DUCHAIN) << "found declaration:" << (ret ? ret->toString() : QString("no declaration found"));)
0115     return ret;
0116 }
0117 
0118 void ExpressionVisitor::visitNode(AstNode *node)
0119 {
0120     if (node && node->ducontext) {
0121         m_currentContext = node->ducontext;
0122     }
0123     Q_ASSERT(m_currentContext);
0124     DefaultVisitor::visitNode(node);
0125 }
0126 
0127 void ExpressionVisitor::visitAssignmentExpression(AssignmentExpressionAst *node)
0128 {
0129     if (node->assignmentExpressionEqual) {
0130         m_isAssignmentExpressionEqual = true;
0131     }
0132     visitNode(node->expression);
0133     m_isAssignmentExpressionEqual = false;
0134 
0135     visitNode(node->assignmentExpressionEqual);
0136     visitNode(node->assignmentExpression);
0137 
0138     if (node->operation == OperationPlus || node->operation == OperationMinus ||
0139             node->operation == OperationMul || node->operation == OperationDiv ||
0140             node->operation == OperationExp) {
0141         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
0142     } else if (node->operation == OperationConcat) {
0143         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
0144     }
0145 }
0146 
0147 void ExpressionVisitor::visitArrayIndexSpecifier(ArrayIndexSpecifierAst* node)
0148 {
0149     DefaultVisitor::visitArrayIndexSpecifier(node);
0150 
0151     // it's an array item but we don't support it really, so just assign type mixed and be done
0152     m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)));
0153 }
0154 
0155 void ExpressionVisitor::visitCompoundVariableWithSimpleIndirectReference(CompoundVariableWithSimpleIndirectReferenceAst *node)
0156 {
0157     if (node->variable) {
0158         m_result.setDeclaration(processVariable(node->variable));
0159     }
0160     DefaultVisitor::visitCompoundVariableWithSimpleIndirectReference(node);
0161 }
0162 
0163 void ExpressionVisitor::visitVariable(VariableAst* node)
0164 {
0165     if ( node->variablePropertiesSequence &&
0166          node->variablePropertiesSequence->front() &&
0167          node->variablePropertiesSequence->front()->element &&
0168          node->variablePropertiesSequence->front()->element->variableProperty &&
0169          node->variablePropertiesSequence->front()->element->variableProperty->objectProperty ) {
0170         // make sure we mark $foo as a use in $foo->...
0171         bool isAssignmentExpressionEqual = m_isAssignmentExpressionEqual;
0172         m_isAssignmentExpressionEqual = false;
0173         DefaultVisitor::visitVariable(node);
0174         m_isAssignmentExpressionEqual = isAssignmentExpressionEqual;
0175     } else {
0176         DefaultVisitor::visitVariable(node);
0177     }
0178 }
0179 
0180 void ExpressionVisitor::visitVarExpression(VarExpressionAst *node)
0181 {
0182     DefaultVisitor::visitVarExpression(node);
0183     if (node->isGenerator != -1) {
0184         DeclarationPointer generatorDecl = findDeclarationImport(ClassDeclarationType, QualifiedIdentifier(u"generator"));
0185 
0186         if (generatorDecl) {
0187             m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)));
0188             if (hasCurrentClosureReturnType()) {
0189                 auto closureType = currentClosureReturnType().staticCast<FunctionType>();
0190                 closureType->setReturnType(generatorDecl->abstractType());
0191             }
0192         }
0193     }
0194 }
0195 
0196 void ExpressionVisitor::visitVarExpressionNewObject(VarExpressionNewObjectAst *node)
0197 {
0198     DefaultVisitor::visitVarExpressionNewObject(node);
0199     if (node->classNameReference->className && node->classNameReference->className->staticIdentifier != -1) {
0200         static const QualifiedIdentifier id(QStringLiteral("static"));
0201         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id);
0202         usingDeclaration(node->classNameReference->className, dec);
0203         m_result.setDeclaration(dec);
0204     } else if (node->classNameReference->className && node->classNameReference->className->identifier) {
0205         const QualifiedIdentifier id = identifierForNamespace(node->classNameReference->className->identifier, m_editor);
0206         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id);
0207         usingDeclaration(node->classNameReference->className->identifier->namespaceNameSequence->back()->element, dec);
0208         buildNamespaceUses(node->classNameReference->className->identifier, id);
0209         m_result.setDeclaration(dec);
0210     }
0211 }
0212 
0213 void ExpressionVisitor::visitVarExpressionArray(VarExpressionArrayAst *node)
0214 {
0215     DefaultVisitor::visitVarExpressionArray(node);
0216     m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeArray)));
0217 }
0218 
0219 void ExpressionVisitor::visitClosure(ClosureAst* node)
0220 {
0221     auto* closureType = new FunctionType;
0222     closureType->setReturnType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
0223     openClosureReturnType(AbstractType::Ptr(closureType));
0224     if (node->functionBody) {
0225         visitInnerStatementList(node->functionBody);
0226     }
0227     if (node->returnType) {
0228         visitReturnType(node->returnType);
0229     }
0230 
0231     //Override found type with return typehint or phpdoc return typehint
0232     AbstractType::Ptr type = returnType(node->returnType, {}, m_editor, m_currentContext);
0233 
0234     if (type) {
0235         closureType->setReturnType(type);
0236     }
0237 
0238     if (node->parameters->parametersSequence) {
0239         const KDevPG::ListNode< ParameterAst* >* it = node->parameters->parametersSequence->front();
0240         forever {
0241             AbstractType::Ptr type = parameterType(it->element, {}, m_editor, m_currentContext);
0242             closureType->addArgument(type);
0243 
0244             if (it->element->parameterType) {
0245                 visitParameterType(it->element->parameterType);
0246             }
0247 
0248             if (it->element->defaultValue) {
0249                 visitExpr(it->element->defaultValue);
0250             }
0251             if ( it->hasNext() ) {
0252                 it = it->next;
0253             } else {
0254                 break;
0255             }
0256         }
0257     }
0258 
0259     if (node->lexicalVars && node->lexicalVars->lexicalVarsSequence) {
0260         const KDevPG::ListNode< LexicalVarAst* >* it = node->lexicalVars->lexicalVarsSequence->front();
0261         DUChainWriteLocker lock;
0262         forever {
0263             DeclarationPointer found;
0264             foreach(Declaration* dec, m_currentContext->findDeclarations(identifierForNode(it->element->variable))) {
0265                 if (dec->kind() == Declaration::Instance) {
0266                     found = dec;
0267                     break;
0268                 }
0269             }
0270             usingDeclaration(it->element->variable, found);
0271             if ( it->hasNext() ) {
0272                 it = it->next;
0273             } else {
0274                 break;
0275             }
0276         }
0277     }
0278 
0279     m_result.setType(AbstractType::Ptr(closureType));
0280     closeClosureReturnType();
0281 }
0282 
0283 void ExpressionVisitor::visitFunctionCallParameterList( FunctionCallParameterListAst* node )
0284 {
0285     QList<DeclarationPointer> decs = m_result.allDeclarations();
0286     AbstractType::Ptr type = m_result.type();
0287 
0288     DefaultVisitor::visitFunctionCallParameterList( node );
0289 
0290     m_result.setDeclarations(decs);
0291     m_result.setType(type);
0292 }
0293 
0294 void ExpressionVisitor::visitFunctionCallParameterListElement(FunctionCallParameterListElementAst* node)
0295 {
0296     DefaultVisitor::visitFunctionCallParameterListElement(node);
0297 
0298     if (m_inDefine) m_inDefine = false; //reset after first parameter passed, the second argument can be a class name
0299 }
0300 
0301 void ExpressionVisitor::visitFunctionCall(FunctionCallAst* node)
0302 {
0303     if (node->stringFunctionNameOrClass && !node->stringFunctionName && !node->varFunctionName) {
0304         QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor);
0305         if (id.toString(RemoveExplicitlyGlobalPrefix) == QLatin1String("define")
0306                 && node->stringParameterList && node->stringParameterList->parametersSequence
0307                 && node->stringParameterList->parametersSequence->count() > 0) {
0308             //in a define() call the first argument is the constant name. we don't want to look for a class name to build uses
0309             m_inDefine = true;
0310         }
0311     }
0312 
0313     DefaultVisitor::visitFunctionCall(node);
0314 
0315     m_inDefine = false;
0316 
0317     if (node->stringFunctionNameOrClass) {
0318         if (node->stringFunctionName) {
0319             //static function call foo::bar()
0320             DUContext* context = findClassContext(node->stringFunctionNameOrClass);
0321             if (context) {
0322                 DUChainReadLocker lock(DUChain::lock());
0323                 QualifiedIdentifier methodName(stringForNode(node->stringFunctionName).toLower());
0324                 m_result.setDeclarations(context->findDeclarations(methodName));
0325                 lock.unlock();
0326                 if (!m_result.allDeclarations().isEmpty()) {
0327                     usingDeclaration(node->stringFunctionName, m_result.allDeclarations().last());
0328                     FunctionType::Ptr function = m_result.allDeclarations().last()->type<FunctionType>();
0329                     if (function) {
0330                         m_result.setType(function->returnType());
0331                     } else {
0332                         m_result.setType(AbstractType::Ptr());
0333                     }
0334                 }
0335             } else {
0336                 m_result.setHadUnresolvedIdentifiers(true);
0337                 usingDeclaration(node->stringFunctionName, DeclarationPointer());
0338                 m_result.setType(AbstractType::Ptr());
0339             }
0340         } else if (node->varFunctionName) {
0341             //static function call foo::$bar()
0342         } else if (node->expr) {
0343             //static function call foo::{expr}()
0344             const QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor);
0345             DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id);
0346             usingDeclaration(node->stringFunctionNameOrClass->namespaceNameSequence->back()->element, dec);
0347             buildNamespaceUses(node->stringFunctionNameOrClass, id);
0348             m_result.setDeclaration(dec);
0349         } else {
0350             //global function call foo();
0351             QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor);
0352             DeclarationPointer dec = findDeclarationImport(FunctionDeclarationType, id);
0353             if (!dec) {
0354                 id.setExplicitlyGlobal(true);
0355                 dec = findDeclarationImport(FunctionDeclarationType, id);
0356             }
0357             ifDebug(qCDebug(DUCHAIN) << "function call of" << (dec ? dec->toString() : QString("function not found"));)
0358             m_result.setDeclaration(dec);
0359             usingDeclaration(node->stringFunctionNameOrClass->namespaceNameSequence->back()->element, dec);
0360             buildNamespaceUses(node->stringFunctionNameOrClass, id);
0361             if (dec) {
0362                 FunctionType::Ptr function = dec->type<FunctionType>();
0363                 if (function) {
0364                     m_result.setType(function->returnType());
0365                 } else {
0366                     m_result.setType(AbstractType::Ptr());
0367                 }
0368             } else {
0369                 m_result.setHadUnresolvedIdentifiers(true);
0370             }
0371         }
0372     }
0373 }
0374 ///TODO: DUContext pointer?
0375 DUContext* ExpressionVisitor::findClassContext(IdentifierAst* className)
0376 {
0377     DUContext* context = nullptr;
0378     DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, className);
0379     usingDeclaration(className, declaration);
0380     if (declaration) {
0381         DUChainReadLocker lock(DUChain::lock());
0382         context = declaration->internalContext();
0383         if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
0384             //className is currentClass (internalContext is not yet set)
0385             context = m_currentContext->parentContext();
0386         }
0387     }
0388     return context;
0389 }
0390 ///TODO: DUContext pointer?
0391 DUContext* ExpressionVisitor::findClassContext(NamespacedIdentifierAst* className)
0392 {
0393     DUContext* context = nullptr;
0394     const QualifiedIdentifier id = identifierForNamespace(className, m_editor);
0395     DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id);
0396     usingDeclaration(className->namespaceNameSequence->back()->element, declaration);
0397     buildNamespaceUses(className, id);
0398     if (declaration) {
0399         DUChainReadLocker lock(DUChain::lock());
0400         context = declaration->internalContext();
0401         if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
0402             //className is currentClass (internalContext is not yet set)
0403             context = m_currentContext->parentContext();
0404         }
0405     }
0406     return context;
0407 }
0408 
0409 void ExpressionVisitor::visitConstantOrClassConst(ConstantOrClassConstAst *node)
0410 {
0411     DefaultVisitor::visitConstantOrClassConst(node);
0412 
0413     if (node->classConstant) {
0414         //class constant Foo::BAR
0415         DUContext* context = findClassContext(node->constant);
0416         if (context) {
0417             DUChainReadLocker lock(DUChain::lock());
0418             m_result.setDeclarations(context->findDeclarations(Identifier(m_editor->parseSession()->symbol(node->classConstant))));
0419             lock.unlock();
0420             if (!m_result.allDeclarations().isEmpty()) {
0421                 usingDeclaration(node->classConstant, m_result.allDeclarations().last());
0422             } else {
0423                 usingDeclaration(node->classConstant, DeclarationPointer());
0424             }
0425             if (!stringForNode(node->classConstant).compare(QLatin1String("class"), Qt::CaseInsensitive)) {
0426                 m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
0427             }
0428         } else {
0429             m_result.setType(AbstractType::Ptr());
0430         }
0431     } else {
0432         QString str(stringForNode(node->constant).toLower());
0433         if (str == QLatin1String("true") || str == QLatin1String("false")) {
0434             m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)));
0435         } else if (str == QLatin1String("null")) {
0436             m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)));
0437         } else {
0438             //constant (created with declare('foo', 'bar')) or const Foo = 1;
0439             QualifiedIdentifier id = identifierForNamespace(node->constant, m_editor, true);
0440             DeclarationPointer declaration = findDeclarationImport(ConstantDeclarationType, id);
0441             if (!declaration) {
0442                 id.setExplicitlyGlobal(true);
0443                 declaration = findDeclarationImport(ConstantDeclarationType, id);
0444             }
0445             if (!declaration) {
0446                 ///TODO: is this really wanted?
0447                 //it could also be a global function call, without ()
0448                 declaration = findDeclarationImport(FunctionDeclarationType, id);
0449             }
0450             m_result.setDeclaration(declaration);
0451             usingDeclaration(node->constant->namespaceNameSequence->back()->element, declaration);
0452             buildNamespaceUses(node->constant, id);
0453         }
0454     }
0455 }
0456 
0457 void ExpressionVisitor::visitScalar(ScalarAst *node)
0458 {
0459     DefaultVisitor::visitScalar(node);
0460 
0461     if (node->commonScalar) {
0462         uint type = IntegralType::TypeVoid;
0463         switch (node->commonScalar->scalarType) {
0464         case ScalarTypeInt:
0465             type = IntegralType::TypeInt;
0466             break;
0467         case ScalarTypeFloat:
0468             type = IntegralType::TypeFloat;
0469             break;
0470         case ScalarTypeString:
0471             type = IntegralType::TypeString;
0472             break;
0473         }
0474         m_result.setType(AbstractType::Ptr(new IntegralType(type)));
0475     } else if (node->varname != -1) {
0476         //STRING_VARNAME-Token, probably the type of varname should be used
0477         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
0478     } else if (node->encapsList) {
0479         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
0480     }
0481 
0482     if (!m_inDefine && node->commonScalar && node->commonScalar->scalarType == ScalarTypeString) {
0483         QString str = m_editor->parseSession()->symbol(node->commonScalar);
0484         QRegExp exp("^['\"]([A-Za-z0-9_]+)['\"]$");
0485         if (exp.exactMatch(str)) {
0486             //that *could* be a class name
0487             QualifiedIdentifier id(exp.cap(1).toLower());
0488             DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id);
0489             if (declaration) {
0490                 usingDeclaration(node->commonScalar, declaration);
0491             } else {
0492                 m_result.setHadUnresolvedIdentifiers(true);
0493             }
0494         }
0495     }
0496 }
0497 
0498 
0499 void ExpressionVisitor::visitStaticScalar(StaticScalarAst *node)
0500 {
0501     if (node->ducontext) {
0502         m_currentContext = node->ducontext;
0503     }
0504     Q_ASSERT(m_currentContext);
0505 
0506     DefaultVisitor::visitStaticScalar(node);
0507 
0508     uint type = IntegralType::TypeVoid;
0509     if (node->value) {
0510         switch (node->value->scalarType) {
0511         case ScalarTypeInt:
0512             type = IntegralType::TypeInt;
0513             break;
0514         case ScalarTypeFloat:
0515             type = IntegralType::TypeFloat;
0516             break;
0517         case ScalarTypeString:
0518             type = IntegralType::TypeString;
0519             break;
0520         }
0521     } else if (node->plusValue || node->minusValue) {
0522         type = IntegralType::TypeInt;
0523     } else if (node->array != -1) {
0524         type = IntegralType::TypeArray;
0525     }
0526     if (type != IntegralType::TypeVoid) {
0527         m_result.setType(AbstractType::Ptr(new IntegralType(type)));
0528     }
0529 }
0530 
0531 void ExpressionVisitor::visitEncapsVar(EncapsVarAst *node)
0532 {
0533     DefaultVisitor::visitEncapsVar(node);
0534     if (node->variable) {
0535         // handle $foo
0536         DeclarationPointer dec = processVariable(node->variable);
0537         if (dec && node->propertyIdentifier) {
0538             // handle property in $foo->bar
0539             DeclarationPointer foundDec;
0540             DUChainReadLocker lock(DUChain::lock());
0541             if ( StructureType::Ptr structType = dec->type<StructureType>() ) {
0542                 if ( ClassDeclaration* cdec = dynamic_cast<ClassDeclaration*>(structType->declaration(m_currentContext->topContext())) ) {
0543                     ///TODO: share code with visitVariableProperty
0544                     DUContext* ctx = cdec->internalContext();
0545                     if (!ctx && m_currentContext->parentContext()) {
0546                         if (m_currentContext->parentContext()->localScopeIdentifier() == cdec->qualifiedIdentifier()) {
0547                             //class is currentClass (internalContext is not yet set)
0548                             ctx = m_currentContext->parentContext();
0549                         }
0550                     }
0551                     if (ctx) {
0552                         foreach( Declaration* pdec, ctx->findDeclarations(identifierForNode(node->propertyIdentifier)) ) {
0553                             if ( !pdec->isFunctionDeclaration() ) {
0554                                 foundDec = pdec;
0555                                 break;
0556                             }
0557                         }
0558                     }
0559                 }
0560             }
0561             lock.unlock();
0562             usingDeclaration(node->propertyIdentifier, foundDec);
0563         }
0564     }
0565 }
0566 
0567 void ExpressionVisitor::visitVariableProperty(VariablePropertyAst *node)
0568 {
0569     ifDebug(qCDebug(DUCHAIN) << "node:" << m_editor->parseSession()->symbol(node)
0570         << (node->isFunctionCall != -1 ? QString("is function call") : QString("is no function call"));)
0571     if (node->objectProperty && node->objectProperty->objectDimList) {
0572         //handle $foo->bar() and $foo->baz, $foo is m_result.type()
0573 
0574         AbstractType::Ptr type = m_result.type();
0575 
0576         //If the variable type is unsure, try to see if it contains a StructureType. If so, use that
0577         //    (since the other types do not allow accessing properties)
0578         if (auto unsureType = type.dynamicCast<UnsureType>()) {
0579             int numStructureType = 0;
0580             StructureType::Ptr structureType;
0581 
0582             for (unsigned int i = 0; i<unsureType->typesSize(); ++i) {
0583                 StructureType::Ptr subType = unsureType->types()[i].type<StructureType>();
0584                 if (subType) {
0585                     structureType = subType;
0586                     ++numStructureType;
0587                 }
0588             }
0589 
0590             //Only use the found structureType if there's exactly *one* such type
0591             if (numStructureType == 1) {
0592                 Q_ASSERT(structureType);
0593                 type = AbstractType::Ptr(structureType);
0594             }
0595         }
0596 
0597         if (type && type.dynamicCast<StructureType>()) {
0598             DUChainReadLocker lock(DUChain::lock());
0599             Declaration* declaration = type.staticCast<StructureType>()->declaration(m_currentContext->topContext());
0600             if (declaration) {
0601                 ifDebug(qCDebug(DUCHAIN) << "parent:" << declaration->toString();)
0602                 DUContext* context = declaration->internalContext();
0603                 if (!context && m_currentContext->parentContext()) {
0604                     if (m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
0605                         //class is currentClass (internalContext is not yet set)
0606                         context = m_currentContext->parentContext();
0607                     }
0608                 }
0609                 if (context) {
0610                     QualifiedIdentifier propertyId;
0611                     if ( node->isFunctionCall != -1 ) {
0612                         propertyId = QualifiedIdentifier(stringForNode(node->objectProperty->objectDimList->variableName->name).toLower());
0613                     } else {
0614                         propertyId = identifierForNode(node->objectProperty->objectDimList->variableName->name);
0615                     }
0616                     ifDebug(qCDebug(DUCHAIN) << "property id:" << propertyId.toString();)
0617 
0618                     QList<Declaration*> decs;
0619                     foreach ( Declaration* dec, context->findDeclarations(propertyId) ) {
0620                         if ( node->isFunctionCall != -1 ) {
0621                             if ( dec->isFunctionDeclaration() ) {
0622                                 decs << dec;
0623                                 ifDebug(qCDebug(DUCHAIN) << "found:" << dec->toString();)
0624                             }
0625                         } else {
0626                             if ( !dec->isFunctionDeclaration() ) {
0627                                 ClassMemberDeclaration *classDec = dynamic_cast<ClassMemberDeclaration*>(dec);
0628                                 if (classDec && classDec->accessPolicy() == Declaration::Private) {
0629                                     if (declaration == dec->context()->owner()) {
0630                                         decs << dec;
0631                                         ifDebug(qCDebug(DUCHAIN) << "found private:" << dec->toString();)
0632                                     }
0633                                 } else {
0634                                     decs << dec;
0635                                     ifDebug(qCDebug(DUCHAIN) << "found:" << dec->toString();)
0636                                 }
0637                             }
0638                         }
0639                     }
0640                     m_result.setDeclarations(decs);
0641                     lock.unlock();
0642                     if (!m_result.allDeclarations().isEmpty()) {
0643                         if ( !m_isAssignmentExpressionEqual ) {
0644                             usingDeclaration(node->objectProperty->objectDimList->variableName, m_result.allDeclarations().last());
0645                         }
0646                         if (node->isFunctionCall != -1) {
0647                             FunctionType::Ptr function = m_result.allDeclarations().last()->type<FunctionType>();
0648                             if (function) {
0649                                 m_result.setType(function->returnType());
0650                             } else {
0651                                 m_result.setType(AbstractType::Ptr());
0652                             }
0653                         }
0654                     } else {
0655                         if ( !m_isAssignmentExpressionEqual ) {
0656                             usingDeclaration(node->objectProperty->objectDimList->variableName,
0657                                              DeclarationPointer());
0658                         }
0659                         m_result.setType(AbstractType::Ptr());
0660                     }
0661                 } else {
0662                     m_result.setType(AbstractType::Ptr());
0663                 }
0664             } else {
0665                 m_result.setType(AbstractType::Ptr());
0666             }
0667         }
0668     }
0669     DefaultVisitor::visitVariableProperty(node);
0670 }
0671 
0672 void ExpressionVisitor::visitStaticMember(StaticMemberAst* node)
0673 {
0674     //don't call DefaultVisitor::visitStaticMember(node);
0675     //because we would end up in visitCompoundVariableWithSimpleIndirectReference
0676     if (node->staticProperty && node->staticProperty->staticProperty) {
0677         if (node->staticProperty->staticProperty->variable) {
0678             DUContext* context = findClassContext(node->className);
0679             if (context) {
0680                 useDeclaration(node->staticProperty->staticProperty->variable, context);
0681             } else {
0682                 usingDeclaration(node->className, DeclarationPointer());
0683                 m_result.setType(AbstractType::Ptr());
0684             }
0685         } else if (node->staticProperty->staticProperty->expr) {
0686             const QualifiedIdentifier id = identifierForNamespace(node->className, m_editor);
0687             DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id);
0688             usingDeclaration(node->className->namespaceNameSequence->back()->element, declaration);
0689             buildNamespaceUses(node->className, id);
0690 
0691             visitExpr(node->staticProperty->staticProperty->expr);
0692 
0693             m_result.setType(AbstractType::Ptr());
0694         }
0695     }
0696 
0697     if (node->staticProperty && node->staticProperty->offsetItemsSequence) {
0698         const KDevPG::ListNode< DimListItemAst* >* it = node->staticProperty->offsetItemsSequence->front();
0699         do {
0700             visitDimListItem(it->element);
0701         } while(it->hasNext() && (it = it->next));
0702     }
0703 }
0704 
0705 void ExpressionVisitor::visitClassNameReference(ClassNameReferenceAst* node)
0706 {
0707     if (node->staticProperty) {
0708         DUContext* context = findClassContext(node->className->identifier);
0709 
0710         if (context && node->staticProperty && node->staticProperty->staticProperty) {
0711             if (node->staticProperty->staticProperty->variable) {
0712                 // static properties (object::$property)
0713                 useDeclaration(node->staticProperty->staticProperty->variable, context);
0714             } else if (node->staticProperty->staticProperty->expr) {
0715                 // variable static properties (object::${$property})
0716                 visitExpr(node->staticProperty->staticProperty->expr);
0717                 usingDeclaration(node->className, DeclarationPointer());
0718             }
0719         }
0720 
0721         if (node->staticProperty && node->staticProperty->offsetItemsSequence) {
0722             const KDevPG::ListNode< DimListItemAst* >* dim_it = node->staticProperty->offsetItemsSequence->front();
0723             do {
0724                 visitDimListItem(dim_it->element);
0725             } while(dim_it->hasNext() && (dim_it = dim_it->next));
0726         }
0727     }
0728 
0729     if (node->baseVariable) {
0730         DefaultVisitor::visitVariableWithoutObjects(node->baseVariable);
0731     }
0732 
0733     if (node->propertiesSequence) {
0734         if (!m_result.allDeclarations().isEmpty()) {
0735             DUContext* context = nullptr;
0736             StructureType::Ptr type;
0737 
0738             Declaration *declaration = nullptr;
0739             const KDevPG::ListNode< ClassPropertyAst* >* it = node->propertiesSequence->front();
0740 
0741             do {
0742                 // first check for property names held in variables ($object->$property)
0743                 if (it->element->property && it->element->property->variableWithoutObjects
0744                         && it->element->property->variableWithoutObjects->variable->variable) {
0745                     VariableIdentifierAst *varnode = it->element->property->variableWithoutObjects->variable->variable;
0746                     useDeclaration(varnode, m_currentContext);
0747                 } else if (it->element->property && it->element->property->variableWithoutObjects
0748                         && it->element->property->variableWithoutObjects->variable->expr) {
0749                         // variable dynamic properties ($object->${$property})
0750                         visitExpr(it->element->property->variableWithoutObjects->variable->expr);
0751                 } else if (!m_result.allDeclarations().isEmpty()) {
0752                     // Handle dynamic static properties first, as they don't need a class context
0753                     if (it->element->staticProperty && it->element->staticProperty->staticProperty
0754                       && it->element->staticProperty->staticProperty->expr) {
0755                         // variable static properties ($object::${$property})
0756                         visitExpr(it->element->staticProperty->staticProperty->expr);
0757                         usingDeclaration(it->element->staticProperty, DeclarationPointer());
0758                     }
0759 
0760                     type = m_result.allDeclarations().last()->type<StructureType>();
0761 
0762                     if (!type) {
0763                         context = nullptr;
0764                         visitClassNameReferenceDimListItems(it->element);
0765                         continue;
0766                     }
0767 
0768                     DUChainReadLocker lock(DUChain::lock());
0769                     declaration = type->declaration(m_currentContext->topContext());
0770                     lock.unlock();
0771 
0772                     if (!declaration) {
0773                         context = nullptr;
0774                         visitClassNameReferenceDimListItems(it->element);
0775                         continue;
0776                     }
0777 
0778                     context = declaration->internalContext();
0779 
0780                     if (!context || context->type() != DUContext::Class) {
0781                         context = nullptr;
0782                         visitClassNameReferenceDimListItems(it->element);
0783                         continue;
0784                     }
0785 
0786                     if (it->element->staticProperty && it->element->staticProperty->staticProperty
0787                       && it->element->staticProperty->staticProperty->variable) {
0788                         // static properties ($object::$property)
0789                         VariableIdentifierAst *varnode = it->element->staticProperty->staticProperty->variable;
0790                         useDeclaration(varnode, context);
0791                     } else if (it->element->property && it->element->property->objectDimList
0792                             && it->element->property->objectDimList->variableName->name) {
0793                         // normal properties ($object->property)
0794                         IdentifierAst *varidnode = it->element->property->objectDimList->variableName->name;
0795                         useDeclaration(varidnode, context);
0796                     } else {
0797                         context = nullptr;
0798                     }
0799 
0800                     visitClassNameReferenceDimListItems(it->element);
0801                 }
0802             } while(it->hasNext() && (it = it->next));
0803         }
0804     }
0805 }
0806 
0807 void ExpressionVisitor::visitClassNameReferenceDimListItems(ClassPropertyAst* node)
0808 {
0809     // handle array indices after normal/static properties ($object->property[$index] // $object::$property[$index])
0810     if (node->property && node->property->objectDimList && node->property->objectDimList->offsetItemsSequence) {
0811         const KDevPG::ListNode< DimListItemAst* >* dim_it = node->property->objectDimList->offsetItemsSequence->front();
0812         do {
0813             visitDimListItem(dim_it->element);
0814         } while(dim_it->hasNext() && (dim_it = dim_it->next));
0815     } else if (node->staticProperty && node->staticProperty->offsetItemsSequence) {
0816         const KDevPG::ListNode< DimListItemAst* >* dim_it = node->staticProperty->offsetItemsSequence->front();
0817         do {
0818             visitDimListItem(dim_it->element);
0819         } while(dim_it->hasNext() && (dim_it = dim_it->next));
0820     }
0821 }
0822 
0823 void ExpressionVisitor::visitUnaryExpression(UnaryExpressionAst* node)
0824 {
0825     DefaultVisitor::visitUnaryExpression(node);
0826     if (node->castType) {
0827         uint type = 0;
0828         switch (node->castType) {
0829         case CastInt:
0830             type = IntegralType::TypeInt;
0831             break;
0832         case CastDouble:
0833             type = IntegralType::TypeFloat;
0834             break;
0835         case CastString:
0836             type = IntegralType::TypeString;
0837             break;
0838         case CastArray:
0839             type = IntegralType::TypeArray;
0840             break;
0841         case CastObject: {
0842             /// Qualified identifier for 'stdclass'
0843             static const QualifiedIdentifier stdclassQId(QStringLiteral("stdclass"));
0844             DUChainReadLocker lock(DUChain::lock());
0845             m_result.setDeclarations(m_currentContext->findDeclarations(stdclassQId));
0846             break;
0847         }
0848         case CastBool:
0849             type = IntegralType::TypeBoolean;
0850             break;
0851         case CastUnset:
0852             //TODO
0853             break;
0854         }
0855         if (type) {
0856             m_result.setType(AbstractType::Ptr(new IntegralType(type)));
0857         }
0858     }
0859 }
0860 
0861 void ExpressionVisitor::visitAdditiveExpressionRest(AdditiveExpressionRestAst* node)
0862 {
0863     DefaultVisitor::visitAdditiveExpressionRest(node);
0864     if (node->operation == OperationPlus || node->operation == OperationMinus) {
0865         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
0866     } else if (node->operation == OperationConcat) {
0867         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
0868     }
0869 }
0870 
0871 void ExpressionVisitor::visitRelationalExpression(RelationalExpressionAst *node)
0872 {
0873     DefaultVisitor::visitRelationalExpression(node);
0874     if (node->instanceofType && node->instanceofType->className && node->instanceofType->className->identifier) {
0875         const QualifiedIdentifier id = identifierForNamespace(node->instanceofType->className->identifier, m_editor);
0876         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id);
0877         usingDeclaration(node->instanceofType->className->identifier->namespaceNameSequence->back()->element, dec);
0878         buildNamespaceUses(node->instanceofType->className->identifier, id);
0879 
0880         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)));
0881     }
0882 }
0883 
0884 void ExpressionVisitor::visitRelationalExpressionRest(RelationalExpressionRestAst *node)
0885 {
0886     DefaultVisitor::visitRelationalExpressionRest(node);
0887 
0888     m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)));
0889 }
0890 
0891 void ExpressionVisitor::visitEqualityExpressionRest(EqualityExpressionRestAst *node)
0892 {
0893     DefaultVisitor::visitEqualityExpressionRest(node);
0894 
0895     if (node->operation && node->operation == OperationSpaceship) {
0896         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
0897     } else {
0898         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)));
0899     }
0900 }
0901 
0902 void ExpressionVisitor::visitStatement(StatementAst *node)
0903 {
0904     DefaultVisitor::visitStatement(node);
0905 
0906     if (node->returnExpr) {
0907         auto closureType = currentClosureReturnType().dynamicCast<FunctionType>();
0908 
0909         if (closureType) {
0910             closureType->setReturnType(m_result.type());
0911         }
0912     }
0913 }
0914 
0915 void ExpressionVisitor::visitGenericTypeHint(GenericTypeHintAst *node) {
0916     if (node->genericType && isGenericClassTypehint(node->genericType, m_editor)) {
0917         NamespacedIdentifierAst* objectType = node->genericType;
0918         QualifiedIdentifier id = identifierForNamespace(objectType, m_editor);
0919         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id);
0920 
0921         usingDeclaration(objectType->namespaceNameSequence->back()->element, dec);
0922         buildNamespaceUses(objectType, id);
0923     }
0924 }
0925 
0926 QString ExpressionVisitor::stringForNode(AstNode* id)
0927 {
0928     if (!id)
0929         return QString();
0930 
0931     return m_editor->parseSession()->symbol(id);
0932 }
0933 
0934 QualifiedIdentifier ExpressionVisitor::identifierForNode(IdentifierAst* id)
0935 {
0936     if (!id)
0937         return QualifiedIdentifier();
0938 
0939     return QualifiedIdentifier(stringForNode(id));
0940 }
0941 
0942 QString ExpressionVisitor::stringForNode(VariableIdentifierAst* id)
0943 {
0944     if (!id)
0945         return QString();
0946     QString ret(m_editor->parseSession()->symbol(id->variable));
0947     ret = ret.mid(1); //cut off $
0948     return ret;
0949 }
0950 
0951 QualifiedIdentifier ExpressionVisitor::identifierForNode(VariableIdentifierAst* id)
0952 {
0953     if (!id)
0954         return QualifiedIdentifier();
0955 
0956     return QualifiedIdentifier(stringForNode(id));
0957 }
0958 
0959 void ExpressionVisitor::setCreateProblems(bool v)
0960 {
0961     m_createProblems = v;
0962 }
0963 
0964 void ExpressionVisitor::setOffset(const CursorInRevision& offset)
0965 {
0966     m_offset = offset;
0967 }
0968 
0969 void ExpressionVisitor::buildNamespaceUses(NamespacedIdentifierAst* namespaces, const QualifiedIdentifier& identifier)
0970 {
0971     QualifiedIdentifier curId;
0972     curId.setExplicitlyGlobal(identifier.explicitlyGlobal());
0973     Q_ASSERT(identifier.count() == namespaces->namespaceNameSequence->count());
0974     for ( int i = 0; i < identifier.count() - 1; ++i ) {
0975         curId.push(identifier.at(i));
0976         AstNode* node = namespaces->namespaceNameSequence->at(i)->element;
0977         DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId);
0978         usingDeclaration(node, dec);
0979     }
0980 }
0981 
0982 void ExpressionVisitor::useDeclaration(VariableIdentifierAst* node, DUContext* context)
0983 {
0984     DUChainReadLocker lock(DUChain::lock());
0985     m_result.setDeclarations(context->findDeclarations(identifierForNode(node)));
0986     lock.unlock();
0987     if (!m_result.allDeclarations().isEmpty()) {
0988         usingDeclaration(node, m_result.allDeclarations().last());
0989     } else {
0990         usingDeclaration(node, DeclarationPointer());
0991     }
0992 }
0993 
0994 void ExpressionVisitor::useDeclaration(IdentifierAst* node, DUContext* context)
0995 {
0996     DUChainReadLocker lock(DUChain::lock());
0997     m_result.setDeclarations(context->findDeclarations(identifierForNode(node)));
0998     lock.unlock();
0999     if (!m_result.allDeclarations().isEmpty()) {
1000         usingDeclaration(node, m_result.allDeclarations().last());
1001     } else {
1002         usingDeclaration(node, DeclarationPointer());
1003     }
1004 }
1005 
1006 DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, IdentifierAst* node)
1007 {
1008     // methods and class names are case insensitive
1009     QualifiedIdentifier id;
1010     if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) {
1011         id = QualifiedIdentifier(stringForNode(node).toLower());
1012     } else {
1013         id = identifierForNode(node);
1014     }
1015     return findDeclarationImport(declarationType, id);
1016 }
1017 
1018 DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, VariableIdentifierAst* node)
1019 {
1020     return findDeclarationImport(declarationType, identifierForNode(node));
1021 }
1022 
1023 DeclarationPointer ExpressionVisitor::findDeclarationImport( DeclarationType declarationType,
1024                                                              const QualifiedIdentifier& identifier)
1025 {
1026     return findDeclarationImportHelper(m_currentContext, identifier, declarationType);
1027 }
1028 
1029 Declaration* ExpressionVisitor::findVariableDeclaration(DUContext* context, Identifier identifier,
1030                                                         CursorInRevision position, DUContext::SearchFlag flag)
1031 {
1032     QList<Declaration*> decls = context->findDeclarations(identifier, position,
1033                                                         nullptr, flag);
1034     for (int i = decls.count() - 1; i >= 0; i--) {
1035         Declaration *dec = decls.at(i);
1036         if (dec->kind() == Declaration::Instance && dynamic_cast<VariableDeclaration*>(dec)) {
1037             return dec;
1038         }
1039     }
1040 
1041     return nullptr;
1042 }
1043 
1044 }