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 }