adapt to kdevplatform changes, use DeclarationPointer when the duchain might get...
[kdevelop:php.git] / duchain / expressionvisitor.cpp
1 /***************************************************************************
2  *   This file is part of KDevelop                                         *
3  *   Copyright 2008 Niko Sams <niko.sams@gmail.com>                        *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Library General Public License as       *
7  *   published by the Free Software Foundation; either version 2 of the    *
8  *   License, or (at your option) any later version.                       *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU Library General Public     *
16  *   License along with this program; if not, write to the                 *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 #include "expressionvisitor.h"
21 #include "parsesession.h"
22 #include "editorintegrator.h"
23 #include "helper.h"
24 #include "declarations/variabledeclaration.h"
25 #include "declarations/classdeclaration.h"
26
27 #include <language/duchain/topducontext.h>
28 #include <language/duchain/duchain.h>
29 #include <language/duchain/duchainlock.h>
30 #include <language/duchain/persistentsymboltable.h>
31 #include <language/duchain/types/functiontype.h>
32 #include <language/duchain/types/integraltype.h>
33
34 #define ifDebug(x)
35
36 using namespace KDevelop;
37
38 namespace Php
39 {
40
41 ExpressionVisitor::ExpressionVisitor(EditorIntegrator* editor)
42         : m_editor(editor), m_createProblems(false),
43           m_offset(CursorInRevision::invalid()), m_currentContext(0),
44           m_isAssignmentExpressionEqual(false)
45 {
46 }
47
48 DeclarationPointer ExpressionVisitor::processVariable(VariableIdentifierAst* variable)
49 {
50     Q_ASSERT(m_currentContext);
51
52     CursorInRevision position = m_editor->findPosition(variable->variable, EditorIntegrator::BackEdge);
53
54     if ( m_offset.isValid() ) {
55         position.line += m_offset.line;
56         position.column += m_offset.column;
57     }
58
59     DeclarationPointer ret;
60     Identifier identifier = identifierForNode(variable).last();
61
62     ifDebug(kDebug() << "processing variable" << identifier.toString() << position.textCursor();)
63
64     if (identifier.nameEquals(Identifier("this"))) {
65         DUChainReadLocker lock(DUChain::lock());
66         if (m_currentContext->parentContext()
67                 && m_currentContext->parentContext()->type() == DUContext::Class
68                 && m_currentContext->parentContext()->owner()) {
69             ret = m_currentContext->parentContext()->owner();
70         }
71     } else {
72         DUChainReadLocker lock(DUChain::lock());
73         //DontSearchInParent-flag because (1) in Php global variables aren't available in function
74         //context and (2) a function body consists of a single context (so this is no problem)
75         QList<Declaration*> decls = m_currentContext->findDeclarations(identifier, position,
76                                                             0, DUContext::DontSearchInParent);
77         for (int i = decls.count() - 1; i >= 0; i--) {
78             Declaration *dec = decls.at(i);
79             if (dec->kind() == Declaration::Instance && dynamic_cast<VariableDeclaration*>(dec)) {
80                 ret = dec;
81                 break;
82             }
83         }
84     }
85     if (!ret) {
86         //look for a function argument
87         DUChainReadLocker lock(DUChain::lock());
88         ///TODO: why doesn't m_currentContext->findDeclarations() work?
89         ///      evaluate if the stuff below is fast enough (faster?) than findDeclarations()
90         ///see r1028306
91         foreach(const DUContext::Import &import, m_currentContext->importedParentContexts() ) {
92             if ( !import.isDirect() || import.position > position ) {
93                 continue;
94             }
95             DUContext* ctx = import.context(m_currentContext->topContext());
96             if ( ctx->type() == DUContext::Function ) {
97                 QList<Declaration*> args = ctx->findLocalDeclarations(identifier);
98                 if ( !args.isEmpty() ) {
99                     ret = args.first();
100                     break;
101                 }
102             }
103         }
104     }
105     if (!ret) {
106         //look for a superglobal variable
107         DUChainReadLocker lock(DUChain::lock());
108         foreach(Declaration* dec, m_currentContext->topContext()->findDeclarations(identifier, position)) {
109             VariableDeclaration* varDec = dynamic_cast<VariableDeclaration*>(dec);
110             if (varDec && varDec->isSuperglobal()) {
111                 ret = dec;
112                 break;
113             }
114         }
115     }
116     if ( !m_isAssignmentExpressionEqual || identifier.nameEquals( Identifier("this") )
117          // might be something like $s = $s . $s; in which case we have to add a use for the first $s
118          || (ret && ret->range().end < position) ) {
119         usingDeclaration(variable, ret);
120     }
121     ifDebug(kDebug() << "found declaration:" << (ret ? ret->toString() : QString("no declaration found"));)
122     return ret;
123 }
124
125 void ExpressionVisitor::visitNode(AstNode *node)
126 {
127     if (node && node->ducontext) {
128         m_currentContext = node->ducontext;
129     }
130     Q_ASSERT(m_currentContext);
131     DefaultVisitor::visitNode(node);
132 }
133
134 void ExpressionVisitor::visitAssignmentExpression(AssignmentExpressionAst *node)
135 {
136     if (node->assignmentExpressionEqual) {
137         m_isAssignmentExpressionEqual = true;
138     }
139     visitNode(node->expression);
140     m_isAssignmentExpressionEqual = false;
141
142     visitNode(node->assignmentExpressionEqual);
143     visitNode(node->assignmentExpression);
144
145     if (node->operation == OperationPlus || node->operation == OperationMinus || node->operation == OperationMul || node->operation == OperationDiv) {
146         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
147     } else if (node->operation == OperationConcat) {
148         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
149     }
150 }
151
152 void ExpressionVisitor::visitBaseVariable(BaseVariableAst* node)
153 {
154     DefaultVisitor::visitBaseVariable(node);
155
156     if (node->offsetItemsSequence) {
157         // it's an array item but we don't support it really, so just assign type mixed and be done
158         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)));
159     }
160 }
161
162 void ExpressionVisitor::visitCompoundVariableWithSimpleIndirectReference(CompoundVariableWithSimpleIndirectReferenceAst *node)
163 {
164     if (node->variable) {
165         m_result.setDeclaration(processVariable(node->variable));
166     }
167     DefaultVisitor::visitCompoundVariableWithSimpleIndirectReference(node);
168 }
169 void ExpressionVisitor::visitVariable(VariableAst* node)
170 {
171     if ( node->variablePropertiesSequence &&
172          node->variablePropertiesSequence->front() &&
173          node->variablePropertiesSequence->front()->element &&
174          node->variablePropertiesSequence->front()->element->objectProperty ) {
175         // make sure we mark $foo as a use in $foo->...
176         bool isAssignmentExpressionEqual = m_isAssignmentExpressionEqual;
177         m_isAssignmentExpressionEqual = false;
178         DefaultVisitor::visitVariable(node);
179         m_isAssignmentExpressionEqual = isAssignmentExpressionEqual;
180     } else {
181         DefaultVisitor::visitVariable(node);
182     }
183 }
184
185 void ExpressionVisitor::visitVarExpressionNewObject(VarExpressionNewObjectAst *node)
186 {
187     DefaultVisitor::visitVarExpressionNewObject(node);
188     if (node->className->identifier) {
189         const QualifiedIdentifier id = identifierForNamespace(node->className->identifier, m_editor);
190         DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, node->className->identifier,
191                                                  id);
192         usingDeclaration(node->className->identifier->namespaceNameSequence->back()->element, dec);
193         buildNamespaceUses(node->className->identifier, id);
194         m_result.setDeclaration(dec);
195     }
196 }
197
198 void ExpressionVisitor::visitVarExpressionNormal(VarExpressionNormalAst *node)
199 {
200     DefaultVisitor::visitVarExpressionNormal(node);
201     if (node->array != -1) {
202         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeArray)));
203     }
204 }
205
206 void ExpressionVisitor::visitClosure(ClosureAst* node)
207 {
208     FunctionType* closureType = new FunctionType;
209     m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid)));
210     visitInnerStatementList(node->functionBody);
211     closureType->setReturnType(m_result.type());
212
213     if (node->parameters->parametersSequence) {
214         const KDevPG::ListNode< ParameterAst* >* it = node->parameters->parametersSequence->front();
215         forever {
216             AbstractType::Ptr type;
217             if (it->element->parameterType) {
218                 //don't use openTypeFromName as it uses cursor for findDeclarations
219                 DeclarationPointer decl = findDeclarationImport(ClassDeclarationType,
220                                                             it->element->parameterType,
221                                                             identifierForNamespace(it->element->parameterType, m_editor));
222                 if (decl) {
223                     type = decl->abstractType();
224                 }
225             } else if (it->element->arrayType != -1) {
226                 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeArray));
227             } else if (it->element->defaultValue) {
228                 ExpressionVisitor v(m_editor);
229                 it->element->defaultValue->ducontext = m_currentContext;
230                 v.visitNode(it->element->defaultValue);
231                 type = v.result().type();
232             }
233             if (!type) {
234                 type = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed));
235             }
236
237             if ( it->element->isRef != -1 ) {
238                 ReferenceType::Ptr p( new ReferenceType() );
239                 p->setBaseType( type );
240
241                 type = p.cast<AbstractType>();
242             }
243             closureType->addArgument(type);
244             if ( it->hasNext() ) {
245                 it = it->next;
246             } else {
247                 break;
248             }
249         }
250     }
251
252     if (node->lexicalVars) {
253         const KDevPG::ListNode< LexicalVarAst* >* it = node->lexicalVars->lexicalVarsSequence->front();
254         DUChainWriteLocker lock;
255         forever {
256             DeclarationPointer found;
257             foreach(Declaration* dec, m_currentContext->findDeclarations(identifierForNode(it->element->variable))) {
258                 if (dec->kind() == Declaration::Instance) {
259                     found = dec;
260                     break;
261                 }
262             }
263             usingDeclaration(it->element->variable, found);
264             if ( it->hasNext() ) {
265                 it = it->next;
266             } else {
267                 break;
268             }
269         }
270     }
271
272     m_result.setType(AbstractType::Ptr(closureType));
273 }
274
275 void ExpressionVisitor::visitFunctionCallParameterList( FunctionCallParameterListAst* node )
276 {
277     QList<DeclarationPointer> decs = m_result.allDeclarations();
278     AbstractType::Ptr type = m_result.type();
279
280     DefaultVisitor::visitFunctionCallParameterList( node );
281
282     m_result.setDeclarations(decs);
283     m_result.setType(type);
284 }
285
286 void ExpressionVisitor::visitFunctionCall(FunctionCallAst* node)
287 {
288     DefaultVisitor::visitFunctionCall(node);
289     if (node->stringFunctionNameOrClass) {
290         if (node->stringFunctionName) {
291             //static function call foo::bar()
292             DUContext* context = findClassContext(node->stringFunctionNameOrClass);
293             if (context) {
294                 DUChainReadLocker lock(DUChain::lock());
295                 QualifiedIdentifier methodName(stringForNode(node->stringFunctionName).toLower());
296                 m_result.setDeclarations(context->findDeclarations(methodName));
297                 lock.unlock();
298                 if (!m_result.allDeclarations().isEmpty()) {
299                     usingDeclaration(node->stringFunctionName, m_result.allDeclarations().last());
300                     FunctionType::Ptr function = m_result.allDeclarations().last()->type<FunctionType>();
301                     if (function) {
302                         m_result.setType(function->returnType());
303                     } else {
304                         m_result.setType(AbstractType::Ptr());
305                     }
306                 }
307             } else {
308                 m_result.setHadUnresolvedIdentifiers(true);
309                 usingDeclaration(node->stringFunctionName, DeclarationPointer());
310                 m_result.setType(AbstractType::Ptr());
311             }
312         } else if (node->varFunctionName) {
313             //static function call foo::$bar()
314         } else {
315             //global function call foo();
316             const QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor);
317             DeclarationPointer dec = findDeclarationImport(FunctionDeclarationType,
318                                                            node->stringFunctionNameOrClass, id);
319             ifDebug(kDebug() << "function call of" << (dec ? dec->toString() : QString("function not found"));)
320             m_result.setDeclaration(dec);
321             usingDeclaration(node->stringFunctionNameOrClass->namespaceNameSequence->back()->element, dec);
322             buildNamespaceUses(node->stringFunctionNameOrClass, id);
323             if (dec) {
324                 FunctionType::Ptr function = dec->type<FunctionType>();
325                 if (function) {
326                     m_result.setType(function->returnType());
327                 } else {
328                     m_result.setType(AbstractType::Ptr());
329                 }
330             } else {
331                 m_result.setHadUnresolvedIdentifiers(true);
332             }
333         }
334     }
335 }
336 ///TODO: DUContext pointer?
337 DUContext* ExpressionVisitor::findClassContext(IdentifierAst* className)
338 {
339     DUContext* context = 0;
340     DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, className);
341     usingDeclaration(className, declaration);
342     if (declaration) {
343         DUChainReadLocker lock(DUChain::lock());
344         context = declaration->internalContext();
345         if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
346             //className is currentClass (internalContext is not yet set)
347             context = m_currentContext->parentContext();
348         }
349     }
350     return context;
351 }
352 ///TODO: DUContext pointer?
353 DUContext* ExpressionVisitor::findClassContext(NamespacedIdentifierAst* className)
354 {
355     DUContext* context = 0;
356     const QualifiedIdentifier id = identifierForNamespace(className, m_editor);
357     static const QualifiedIdentifier staticQId("static");
358     if (id.count() == 1 && id == staticQId) {
359         return m_currentContext->parentContext();
360     }
361     DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, className, id);
362     usingDeclaration(className->namespaceNameSequence->back()->element, declaration);
363     buildNamespaceUses(className, id);
364     if (declaration) {
365         DUChainReadLocker lock(DUChain::lock());
366         context = declaration->internalContext();
367         if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
368             //className is currentClass (internalContext is not yet set)
369             context = m_currentContext->parentContext();
370         }
371     }
372     return context;
373 }
374
375 void ExpressionVisitor::visitConstantOrClassConst(ConstantOrClassConstAst *node)
376 {
377     DefaultVisitor::visitConstantOrClassConst(node);
378
379     if (node->classConstant) {
380         //class constant Foo::BAR
381         DUContext* context = findClassContext(node->constant);
382         if (context) {
383             DUChainReadLocker lock(DUChain::lock());
384             m_result.setDeclarations(context->findDeclarations(Identifier(m_editor->parseSession()->symbol(node->classConstant))));
385             lock.unlock();
386             if (!m_result.allDeclarations().isEmpty()) {
387                 usingDeclaration(node->classConstant, m_result.allDeclarations().last());
388             } else {
389                 usingDeclaration(node->classConstant, DeclarationPointer());
390             }
391         } else {
392             m_result.setType(AbstractType::Ptr());
393         }
394     } else {
395         QString str(stringForNode(node->constant).toLower());
396         if (str == "true" || str == "false") {
397             m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)));
398         } else if (str == "null") {
399             m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeNull)));
400         } else {
401             //constant (created with declare('foo', 'bar')) or const Foo = 1;
402             QualifiedIdentifier id = identifierForNamespace(node->constant, m_editor, true);
403             DeclarationPointer declaration = findDeclarationImport(ConstantDeclarationType, node->constant, id);
404             if (!declaration) {
405                 ///TODO: is this really wanted?
406                 //it could also be a global function call, without ()
407                 declaration = findDeclarationImport(FunctionDeclarationType, node->constant, id);
408             }
409             m_result.setDeclaration(declaration);
410             usingDeclaration(node->constant->namespaceNameSequence->back()->element, declaration);
411             buildNamespaceUses(node->constant, id);
412         }
413     }
414 }
415
416 void ExpressionVisitor::visitScalar(ScalarAst *node)
417 {
418     DefaultVisitor::visitScalar(node);
419
420     if (node->commonScalar) {
421         uint type = IntegralType::TypeVoid;
422         switch (node->commonScalar->scalarType) {
423         case ScalarTypeInt:
424             type = IntegralType::TypeInt;
425             break;
426         case ScalarTypeFloat:
427             type = IntegralType::TypeFloat;
428             break;
429         case ScalarTypeString:
430             type = IntegralType::TypeString;
431             break;
432         }
433         m_result.setType(AbstractType::Ptr(new IntegralType(type)));
434     } else if (node->varname != -1) {
435         //STRING_VARNAME-Token, probably the type of varname should be used
436         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
437     } else if (node->encapsList) {
438         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
439     }
440 }
441
442 void ExpressionVisitor::visitStaticScalar(StaticScalarAst *node)
443 {
444     if (node->ducontext) {
445         m_currentContext = node->ducontext;
446     }
447     Q_ASSERT(m_currentContext);
448
449     DefaultVisitor::visitStaticScalar(node);
450
451     uint type = IntegralType::TypeVoid;
452     if (node->value) {
453         switch (node->value->scalarType) {
454         case ScalarTypeInt:
455             type = IntegralType::TypeInt;
456             break;
457         case ScalarTypeFloat:
458             type = IntegralType::TypeFloat;
459             break;
460         case ScalarTypeString:
461             type = IntegralType::TypeString;
462             break;
463         }
464     } else if (node->plusValue || node->minusValue) {
465         type = IntegralType::TypeInt;
466     } else if (node->array != -1) {
467         type = IntegralType::TypeArray;
468     }
469     if (type != IntegralType::TypeVoid) {
470         m_result.setType(AbstractType::Ptr(new IntegralType(type)));
471     }
472 }
473
474 void ExpressionVisitor::visitEncapsVar(EncapsVarAst *node)
475 {
476     DefaultVisitor::visitEncapsVar(node);
477     if (node->variable) {
478         // handle $foo
479         DeclarationPointer dec = processVariable(node->variable);
480         if (dec && node->propertyIdentifier) {
481             // handle property in $foo->bar
482             DeclarationPointer foundDec;
483             DUChainReadLocker lock(DUChain::lock());
484             if ( StructureType::Ptr structType = dec->type<StructureType>() ) {
485                 if ( ClassDeclaration* cdec = dynamic_cast<ClassDeclaration*>(structType->declaration(m_currentContext->topContext())) ) {
486                     foreach( Declaration* pdec, cdec->internalContext()->findDeclarations(identifierForNode(node->propertyIdentifier)) ) {
487                         if ( !pdec->isFunctionDeclaration() ) {
488                             foundDec = pdec;
489                             break;
490                         }
491                     }
492                 }
493             }
494             lock.unlock();
495             usingDeclaration(node->propertyIdentifier, foundDec);
496         }
497     }
498 }
499
500 void ExpressionVisitor::visitVariableProperty(VariablePropertyAst *node)
501 {
502     ifDebug(kDebug() << "node:" << m_editor->parseSession()->symbol(node)
503         << (node->isFunctionCall != -1 ? QString("is function call") : QString("is no function call"));)
504     if (node->objectProperty && node->objectProperty->objectDimList) {
505         //handle $foo->bar() and $foo->baz, $foo is m_result.type()
506
507         if (m_result.type() && StructureType::Ptr::dynamicCast(m_result.type())) {
508             DUChainReadLocker lock(DUChain::lock());
509             Declaration* declaration = StructureType::Ptr::staticCast(m_result.type())->declaration(m_currentContext->topContext());
510             if (declaration) {
511                 ifDebug(kDebug() << "parent:" << declaration->toString();)
512                 DUContext* context = declaration->internalContext();
513                 if (!context && m_currentContext->parentContext()) {
514                     if (m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) {
515                         //class is currentClass (internalContext is not yet set)
516                         context = m_currentContext->parentContext();
517                     }
518                 }
519                 if (context) {
520                     QualifiedIdentifier propertyId;
521                     if ( node->isFunctionCall != -1 ) {
522                         propertyId = QualifiedIdentifier(stringForNode(node->objectProperty->objectDimList->variableName->name).toLower());
523                     } else {
524                         propertyId = identifierForNode(node->objectProperty->objectDimList->variableName->name);
525                     }
526                     ifDebug(kDebug() << "property id:" << propertyId.toString();)
527
528                     QList<Declaration*> decs;
529                     foreach ( Declaration* dec, context->findDeclarations(propertyId) ) {
530                         if ( node->isFunctionCall != -1 ) {
531                             if ( dec->isFunctionDeclaration() ) {
532                                 decs << dec;
533                                 ifDebug(kDebug() << "found:" << dec->toString();)
534                             }
535                         } else {
536                             if ( !dec->isFunctionDeclaration() ) {
537                                 decs << dec;
538                                 ifDebug(kDebug() << "found:" << dec->toString();)
539                             }
540                         }
541                     }
542                     m_result.setDeclarations(decs);
543                     lock.unlock();
544                     if (!m_result.allDeclarations().isEmpty()) {
545                         if ( !m_isAssignmentExpressionEqual ) {
546                             usingDeclaration(node->objectProperty->objectDimList->variableName, m_result.allDeclarations().last());
547                         }
548                         if (node->isFunctionCall != -1) {
549                             FunctionType::Ptr function = m_result.allDeclarations().last()->type<FunctionType>();
550                             if (function) {
551                                 m_result.setType(function->returnType());
552                             } else {
553                                 m_result.setType(AbstractType::Ptr());
554                             }
555                         }
556                     } else {
557                         if ( !m_isAssignmentExpressionEqual ) {
558                             usingDeclaration(node->objectProperty->objectDimList->variableName,
559                                              DeclarationPointer());
560                         }
561                         m_result.setType(AbstractType::Ptr());
562                     }
563                 } else {
564                     m_result.setType(AbstractType::Ptr());
565                 }
566             } else {
567                 m_result.setType(AbstractType::Ptr());
568             }
569         }
570     }
571     DefaultVisitor::visitVariableProperty(node);
572 }
573
574 void ExpressionVisitor::visitStaticMember(StaticMemberAst* node)
575 {
576     //don't call DefaultVisitor::visitStaticMember(node);
577     //because we would end up in visitCompoundVariableWithSimpleIndirectReference
578     if (node->variable->variable->variable) {
579         DUContext* context = findClassContext(node->className);
580         if (context) {
581             DUChainReadLocker lock(DUChain::lock());
582             m_result.setDeclarations(context->findDeclarations(identifierForNode(node->variable->variable->variable)));
583             lock.unlock();
584             if (!m_result.allDeclarations().isEmpty()) {
585                 usingDeclaration(node->variable->variable->variable, m_result.allDeclarations().last());
586             } else {
587                 usingDeclaration(node->variable->variable->variable, DeclarationPointer());
588             }
589         } else {
590             usingDeclaration(node->className, DeclarationPointer());
591             m_result.setType(AbstractType::Ptr());
592         }
593         if (node->variable->offsetItemsSequence) {
594             const KDevPG::ListNode< DimListItemAst* >* it = node->variable->offsetItemsSequence->front();
595             do {
596                 visitDimListItem(it->element);
597             } while(it->hasNext() && (it = it->next));
598         }
599     }
600 }
601
602 void ExpressionVisitor::visitUnaryExpression(UnaryExpressionAst* node)
603 {
604     DefaultVisitor::visitUnaryExpression(node);
605     if (node->castType) {
606         uint type = 0;
607         switch (node->castType) {
608         case CastInt:
609             type = IntegralType::TypeInt;
610             break;
611         case CastDouble:
612             type = IntegralType::TypeFloat;
613             break;
614         case CastString:
615             type = IntegralType::TypeString;
616             break;
617         case CastArray:
618             type = IntegralType::TypeArray;
619             break;
620         case CastObject: {
621             /// Qualified identifier for 'stdclass'
622             static const QualifiedIdentifier stdclassQId("stdclass");
623             DUChainReadLocker lock(DUChain::lock());
624             m_result.setDeclarations(m_currentContext->findDeclarations(stdclassQId));
625             break;
626         }
627         case CastBool:
628             type = IntegralType::TypeBoolean;
629             break;
630         case CastUnset:
631             //TODO
632             break;
633         }
634         if (type) {
635             m_result.setType(AbstractType::Ptr(new IntegralType(type)));
636         }
637     }
638 }
639
640 void ExpressionVisitor::visitAdditiveExpressionRest(AdditiveExpressionRestAst* node)
641 {
642     DefaultVisitor::visitAdditiveExpressionRest(node);
643     if (node->operation == OperationPlus || node->operation == OperationMinus) {
644         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)));
645     } else if (node->operation == OperationConcat) {
646         m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString)));
647     }
648 }
649
650 QString ExpressionVisitor::stringForNode(AstNode* id)
651 {
652     if (!id)
653         return QString();
654
655     return m_editor->parseSession()->symbol(id);
656 }
657
658 QualifiedIdentifier ExpressionVisitor::identifierForNode(IdentifierAst* id)
659 {
660     if (!id)
661         return QualifiedIdentifier();
662
663     return QualifiedIdentifier(stringForNode(id));
664 }
665
666 QString ExpressionVisitor::stringForNode(VariableIdentifierAst* id)
667 {
668     if (!id)
669         return QString();
670     QString ret(m_editor->parseSession()->symbol(id->variable));
671     ret = ret.mid(1); //cut off $
672     return ret;
673 }
674
675 QualifiedIdentifier ExpressionVisitor::identifierForNode(VariableIdentifierAst* id)
676 {
677     if (!id)
678         return QualifiedIdentifier();
679
680     return QualifiedIdentifier(stringForNode(id));
681 }
682
683 void ExpressionVisitor::setCreateProblems(bool v)
684 {
685     m_createProblems = v;
686 }
687
688 void ExpressionVisitor::setOffset(const CursorInRevision& offset)
689 {
690     m_offset = offset;
691 }
692
693 void ExpressionVisitor::buildNamespaceUses(NamespacedIdentifierAst* namespaces, const QualifiedIdentifier& identifier)
694 {
695     QualifiedIdentifier curId;
696     curId.setExplicitlyGlobal(identifier.explicitlyGlobal());
697     Q_ASSERT(identifier.count() == namespaces->namespaceNameSequence->count());
698     for ( int i = 0; i < identifier.count() - 1; ++i ) {
699         curId.push(identifier.at(i));
700         AstNode* node = namespaces->namespaceNameSequence->at(i)->element;
701         DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, node, curId);
702         usingDeclaration(node, dec);
703     }
704 }
705
706 DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, IdentifierAst* node)
707 {
708     // methods and class names are case insensitive
709     QualifiedIdentifier id;
710     if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) {
711         id = QualifiedIdentifier(stringForNode(node).toLower());
712     } else {
713         id = identifierForNode(node);
714     }
715     return findDeclarationImport(declarationType, node, id);
716 }
717
718 DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, VariableIdentifierAst* node)
719 {
720     return findDeclarationImport(declarationType, node, identifierForNode(node));
721 }
722
723 DeclarationPointer ExpressionVisitor::findDeclarationImport( DeclarationType declarationType, AstNode* node, const QualifiedIdentifier& identifier)
724 {
725     return findDeclarationImportHelper(m_currentContext, identifier, declarationType, node, m_editor);
726 }
727
728 }