diff --git a/duchain/builders/usebuilder.cpp b/duchain/builders/usebuilder.cpp index 044da88..15c1feb 100644 --- a/duchain/builders/usebuilder.cpp +++ b/duchain/builders/usebuilder.cpp @@ -1,281 +1,284 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Niko Sams * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "usebuilder.h" #include #include "editorintegrator.h" #include "expressionvisitor.h" #include "parsesession.h" #include using namespace KDevelop; namespace Php { class UseExpressionVisitor : public ExpressionVisitor { public: UseExpressionVisitor(EditorIntegrator* editor, UseBuilder* useBuilder) : ExpressionVisitor(editor), m_builder(useBuilder) { } protected: void usingDeclaration(AstNode* node, const DeclarationPointer& decl) override { m_builder->newCheckedUse(node, decl); } private: UseBuilder* m_builder; }; UseBuilder::UseBuilder( EditorIntegrator* editor ) { m_editor = editor; } ReferencedTopDUContext UseBuilder::build ( const IndexedString& url, AstNode* node, ReferencedTopDUContext updateContext ) { // just for safety purposes: running the UseBuilder on the internal function file // will lead to undefined behavior due to the amount of optimization it has received // (esp. in the contextbuilder) Q_ASSERT(url != internalFunctionFile()); return UseBuilderBase::build ( url, node, updateContext ); } void UseBuilder::visitParameter(ParameterAst *node) { if (node->parameterType && node->parameterType->objectType) { buildNamespaceUses(node->parameterType->objectType); } + if (node->defaultValue) { + visitNodeWithExprVisitor(node->defaultValue); + } } void UseBuilder::visitClassImplements(ClassImplementsAst *node) { if (node->implementsSequence) { const KDevPG::ListNode *__it = node->implementsSequence->front(), *__end = __it; do { buildNamespaceUses(__it->element); __it = __it->next; } while (__it != __end); } } void UseBuilder::visitClassExtends(ClassExtendsAst *node) { buildNamespaceUses(node->identifier); } void UseBuilder::visitClassStatement(ClassStatementAst *node) { if (!node->traitsSequence) { UseBuilderBase::visitClassStatement(node); return; } const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->traitsSequence->front(); forever { buildNamespaceUses(it->element, ClassDeclarationType); if ( it->hasNext() ) { it = it->next; } else { break; } } if (node->imports) { visitTraitAliasDeclaration(node->imports); } UseBuilderBase::visitClassStatement(node); } void UseBuilder::visitTraitAliasStatement(TraitAliasStatementAst *node) { if (node->conflictIdentifierSequence) { const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->conflictIdentifierSequence->front(); forever { buildNamespaceUses(it->element, ClassDeclarationType); if ( it->hasNext() ) { it = it->next; } else { break; } } } DUChainWriteLocker lock; DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, identifierForNamespace(node->importIdentifier->identifier, m_editor)); if (dec) { QualifiedIdentifier original = identifierPairForNode(node->importIdentifier->methodIdentifier).second; QList list = dec.data()->internalContext()->findLocalDeclarations(original.last(), dec.data()->internalContext()->range().start); if (!list.isEmpty()) { UseBuilderBase::newUse(node->importIdentifier->methodIdentifier, DeclarationPointer(list.first())); } } lock.unlock(); visitTraitAliasIdentifier(node->importIdentifier); } void UseBuilder::visitTraitAliasIdentifier(TraitAliasIdentifierAst *node) { buildNamespaceUses(node->identifier, ClassDeclarationType); } void UseBuilder::visitExpr(ExprAst* node) { visitNodeWithExprVisitor(node); } void UseBuilder::visitGlobalVar(GlobalVarAst* node) { if (node->var) { DeclarationPointer dec = findDeclarationImport(GlobalVariableDeclarationType, node->var); if (dec) { newCheckedUse(node->var, dec); } } } void UseBuilder::visitStaticScalar(StaticScalarAst* node) { if (currentContext()->type() == DUContext::Class) { visitNodeWithExprVisitor(node); } } void UseBuilder::visitStatement(StatementAst *node) { if (node->foreachVar) { visitNodeWithExprVisitor(node->foreachVar); } else if (node->unsetVariablesSequence) { visitNodeWithExprVisitor(node); } if (node->foreachExprAsVar) { visitNodeWithExprVisitor(node->foreachExprAsVar); } if (node->foreachVarAsVar) { visitNodeWithExprVisitor(node->foreachVarAsVar); } if (node->foreachVariable) { visitNodeWithExprVisitor(node->foreachVariable); } UseBuilderBase::visitStatement(node); } void UseBuilder::visitCatchItem(CatchItemAst *node) { if (node->catchClass) { buildNamespaceUses(node->catchClass, ClassDeclarationType); } UseBuilderBase::visitCatchItem(node); } void UseBuilder::newCheckedUse(AstNode* node, const DeclarationPointer& declaration, bool reportNotFound) { if ( declaration && declaration->comment().contains("@deprecated") ) { reportError(i18n("Usage of %1 is deprecated.", declaration->toString()), node, IProblem::Hint); } else if ( !declaration && reportNotFound ) { reportError(i18n("Declaration not found: %1", m_editor->parseSession()->symbol(node)), node, IProblem::Hint); } UseBuilderBase::newUse(node, declaration); } void UseBuilder::visitUnaryExpression( UnaryExpressionAst* node ) { IndexedString includeFile = getIncludeFileForNode(node, m_editor); if ( !includeFile.isEmpty() ) { QualifiedIdentifier identifier(includeFile.str()); DUChainWriteLocker lock(DUChain::lock()); foreach ( Declaration* dec, currentContext()->topContext()->findDeclarations(identifier) ) { if ( dec->kind() == Declaration::Import ) { newUse(node->includeExpression, DeclarationPointer(dec)); return; } } } } void UseBuilder::visitUseNamespace(UseNamespaceAst* node) { buildNamespaceUses(node->identifier, NamespaceDeclarationType); } void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationType lastType) { const QualifiedIdentifier identifier = identifierForNamespace(node, m_editor); QualifiedIdentifier curId; curId.setExplicitlyGlobal(identifier.explicitlyGlobal()); Q_ASSERT(identifier.count() == node->namespaceNameSequence->count()); for ( int i = 0; i < identifier.count() - 1; ++i ) { curId.push(identifier.at(i)); AstNode* n = node->namespaceNameSequence->at(i)->element; DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId); if (!dec || dec->range() != editorFindRange(n, n)) { newCheckedUse(n, dec, true); } } newCheckedUse(node->namespaceNameSequence->back()->element, findDeclarationImport(lastType, identifier), lastType == ClassDeclarationType || lastType == ConstantDeclarationType || lastType == FunctionDeclarationType || lastType == NamespaceDeclarationType); } void UseBuilder::openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const RangeInRevision& range) { if (node != parent->namespaceNameSequence->back()->element) { DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, identifier.second); if (!dec || dec->range() != editorFindRange(node, node)) { newCheckedUse(node, dec); } } UseBuilderBase::openNamespace(parent, node, identifier, range); } void UseBuilder::visitNodeWithExprVisitor(AstNode* node) { UseExpressionVisitor v(m_editor, this); node->ducontext = currentContext(); v.visitNode(node); if (v.result().hadUnresolvedIdentifiers()) { m_hadUnresolvedIdentifiers = true; } } void UseBuilder::visitReturnType(ReturnTypeAst* node) { if (node->objectType) { buildNamespaceUses(node->objectType); } } } diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp index 1853cc1..58d51f3 100644 --- a/duchain/expressionvisitor.cpp +++ b/duchain/expressionvisitor.cpp @@ -1,799 +1,819 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Niko Sams * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "expressionvisitor.h" #include "parsesession.h" #include "editorintegrator.h" #include "helper.h" #include "declarations/variabledeclaration.h" #include "declarations/classdeclaration.h" #include #include #include #include #include #include #include #include "duchaindebug.h" #define ifDebug(x) using namespace KDevelop; namespace Php { ExpressionVisitor::ExpressionVisitor(EditorIntegrator* editor) : m_editor(editor), m_createProblems(false), m_offset(CursorInRevision::invalid()), m_currentContext(nullptr), m_isAssignmentExpressionEqual(false), m_inDefine(false) { } DeclarationPointer ExpressionVisitor::processVariable(VariableIdentifierAst* variable) { Q_ASSERT(m_currentContext); CursorInRevision position = m_editor->findPosition(variable->variable, EditorIntegrator::BackEdge); if ( m_offset.isValid() ) { position.line += m_offset.line; position.column += m_offset.column; } DeclarationPointer ret; Identifier identifier = identifierForNode(variable).last(); ifDebug(qCDebug(DUCHAIN) << "processing variable" << identifier.toString() << position.castToSimpleCursor();) DUChainReadLocker lock; if (identifier.nameEquals(Identifier(QStringLiteral("this")))) { if (m_currentContext->parentContext() && m_currentContext->parentContext()->type() == DUContext::Class && m_currentContext->parentContext()->owner()) { ret = m_currentContext->parentContext()->owner(); } } else { //DontSearchInParent-flag because (1) in Php global variables aren't available in function //context and (2) a function body consists of a single context (so this is no problem) ret = findVariableDeclaration(m_currentContext, identifier, position, DUContext::DontSearchInParent); } if (!ret && m_currentContext->type() == DUContext::Namespace) { ret = findVariableDeclaration(m_currentContext, identifier, position, DUContext::NoSearchFlags); } if (!ret) { //look for a function argument ///TODO: why doesn't m_currentContext->findDeclarations() work? /// evaluate if the stuff below is fast enough (faster?) than findDeclarations() ///see r1028306 foreach(const DUContext::Import &import, m_currentContext->importedParentContexts() ) { if ( !import.isDirect() || import.position > position ) { continue; } DUContext* ctx = import.context(m_currentContext->topContext()); if ( ctx->type() == DUContext::Function ) { QList args = ctx->findLocalDeclarations(identifier); if ( !args.isEmpty() ) { ret = args.first(); break; } } } } if (!ret) { //look for a superglobal variable foreach(Declaration* dec, m_currentContext->topContext()->findDeclarations(identifier, position)) { VariableDeclaration* varDec = dynamic_cast(dec); if (varDec && varDec->isSuperglobal()) { ret = dec; break; } } } lock.unlock(); if ( !m_isAssignmentExpressionEqual || identifier.nameEquals( Identifier(QStringLiteral("this")) ) // might be something like $s = $s . $s; in which case we have to add a use for the first $s || (ret && ret->range().end < position) ) { // also don't report uses for the place of declaration if (!ret || ret->range().end != position) { usingDeclaration(variable, ret); } } ifDebug(qCDebug(DUCHAIN) << "found declaration:" << (ret ? ret->toString() : QString("no declaration found"));) return ret; } void ExpressionVisitor::visitNode(AstNode *node) { if (node && node->ducontext) { m_currentContext = node->ducontext; } Q_ASSERT(m_currentContext); DefaultVisitor::visitNode(node); } void ExpressionVisitor::visitAssignmentExpression(AssignmentExpressionAst *node) { if (node->assignmentExpressionEqual) { m_isAssignmentExpressionEqual = true; } visitNode(node->expression); m_isAssignmentExpressionEqual = false; visitNode(node->assignmentExpressionEqual); visitNode(node->assignmentExpression); if (node->operation == OperationPlus || node->operation == OperationMinus || node->operation == OperationMul || node->operation == OperationDiv || node->operation == OperationExp) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt))); } else if (node->operation == OperationConcat) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString))); } } void ExpressionVisitor::visitArrayIndexSpecifier(ArrayIndexSpecifierAst* node) { DefaultVisitor::visitArrayIndexSpecifier(node); // it's an array item but we don't support it really, so just assign type mixed and be done m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed))); } void ExpressionVisitor::visitCompoundVariableWithSimpleIndirectReference(CompoundVariableWithSimpleIndirectReferenceAst *node) { if (node->variable) { m_result.setDeclaration(processVariable(node->variable)); } DefaultVisitor::visitCompoundVariableWithSimpleIndirectReference(node); } void ExpressionVisitor::visitVariable(VariableAst* node) { if ( node->variablePropertiesSequence && node->variablePropertiesSequence->front() && node->variablePropertiesSequence->front()->element && node->variablePropertiesSequence->front()->element->variableProperty && node->variablePropertiesSequence->front()->element->variableProperty->objectProperty ) { // make sure we mark $foo as a use in $foo->... bool isAssignmentExpressionEqual = m_isAssignmentExpressionEqual; m_isAssignmentExpressionEqual = false; DefaultVisitor::visitVariable(node); m_isAssignmentExpressionEqual = isAssignmentExpressionEqual; } else { DefaultVisitor::visitVariable(node); } } void ExpressionVisitor::visitVarExpressionNewObject(VarExpressionNewObjectAst *node) { DefaultVisitor::visitVarExpressionNewObject(node); if (node->className->staticIdentifier != -1) { static const QualifiedIdentifier id(QStringLiteral("static")); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); usingDeclaration(node->className, dec); m_result.setDeclaration(dec); } else if (node->className->identifier) { const QualifiedIdentifier id = identifierForNamespace(node->className->identifier, m_editor); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); usingDeclaration(node->className->identifier->namespaceNameSequence->back()->element, dec); buildNamespaceUses(node->className->identifier, id); m_result.setDeclaration(dec); } } void ExpressionVisitor::visitVarExpressionArray(VarExpressionArrayAst *node) { DefaultVisitor::visitVarExpressionArray(node); m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeArray))); } void ExpressionVisitor::visitClosure(ClosureAst* node) { FunctionType* closureType = new FunctionType; m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeVoid))); if (node->functionBody) { visitInnerStatementList(node->functionBody); } + if (node->returnType && node->returnType->objectType) { + NamespacedIdentifierAst* objectType = node->returnType->objectType; + QualifiedIdentifier id = identifierForNamespace(objectType, m_editor); + DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); + + usingDeclaration(objectType->namespaceNameSequence->back()->element, dec); + buildNamespaceUses(objectType, id); + } //First try return typehint or phpdoc return typehint AbstractType::Ptr type = returnType(node->returnType, {}, m_editor, m_currentContext); if (!type) { //If failed, use the inferred type from return statements type = m_result.type(); } closureType->setReturnType(type); if (node->parameters->parametersSequence) { const KDevPG::ListNode< ParameterAst* >* it = node->parameters->parametersSequence->front(); forever { AbstractType::Ptr type = parameterType(it->element, {}, m_editor, m_currentContext); closureType->addArgument(type); + + if (it->element->parameterType && it->element->parameterType->objectType) { + NamespacedIdentifierAst* objectType = it->element->parameterType->objectType; + QualifiedIdentifier id = identifierForNamespace(objectType, m_editor); + DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); + + usingDeclaration(objectType->namespaceNameSequence->back()->element, dec); + buildNamespaceUses(objectType, id); + } + if (it->element->defaultValue) { + visitExpr(it->element->defaultValue); + } if ( it->hasNext() ) { it = it->next; } else { break; } } } if (node->lexicalVars && node->lexicalVars->lexicalVarsSequence) { const KDevPG::ListNode< LexicalVarAst* >* it = node->lexicalVars->lexicalVarsSequence->front(); DUChainWriteLocker lock; forever { DeclarationPointer found; foreach(Declaration* dec, m_currentContext->findDeclarations(identifierForNode(it->element->variable))) { if (dec->kind() == Declaration::Instance) { found = dec; break; } } usingDeclaration(it->element->variable, found); if ( it->hasNext() ) { it = it->next; } else { break; } } } m_result.setType(AbstractType::Ptr(closureType)); } void ExpressionVisitor::visitFunctionCallParameterList( FunctionCallParameterListAst* node ) { QList decs = m_result.allDeclarations(); AbstractType::Ptr type = m_result.type(); DefaultVisitor::visitFunctionCallParameterList( node ); m_result.setDeclarations(decs); m_result.setType(type); } void ExpressionVisitor::visitFunctionCallParameterListElement(FunctionCallParameterListElementAst* node) { DefaultVisitor::visitFunctionCallParameterListElement(node); if (m_inDefine) m_inDefine = false; //reset after first parameter passed, the second argument can be a class name } void ExpressionVisitor::visitFunctionCall(FunctionCallAst* node) { if (node->stringFunctionNameOrClass && !node->stringFunctionName && !node->varFunctionName) { QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); if (id.toString(RemoveExplicitlyGlobalPrefix) == QLatin1String("define") && node->stringParameterList && node->stringParameterList->parametersSequence && node->stringParameterList->parametersSequence->count() > 0) { //in a define() call the first argument is the constant name. we don't want to look for a class name to build uses m_inDefine = true; } } DefaultVisitor::visitFunctionCall(node); m_inDefine = false; if (node->stringFunctionNameOrClass) { if (node->stringFunctionName) { //static function call foo::bar() DUContext* context = findClassContext(node->stringFunctionNameOrClass); if (context) { DUChainReadLocker lock(DUChain::lock()); QualifiedIdentifier methodName(stringForNode(node->stringFunctionName).toLower()); m_result.setDeclarations(context->findDeclarations(methodName)); lock.unlock(); if (!m_result.allDeclarations().isEmpty()) { usingDeclaration(node->stringFunctionName, m_result.allDeclarations().last()); FunctionType::Ptr function = m_result.allDeclarations().last()->type(); if (function) { m_result.setType(function->returnType()); } else { m_result.setType(AbstractType::Ptr()); } } } else { m_result.setHadUnresolvedIdentifiers(true); usingDeclaration(node->stringFunctionName, DeclarationPointer()); m_result.setType(AbstractType::Ptr()); } } else if (node->varFunctionName) { //static function call foo::$bar() } else if (node->expr) { //static function call foo::{expr}() const QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); usingDeclaration(node->stringFunctionNameOrClass->namespaceNameSequence->back()->element, dec); buildNamespaceUses(node->stringFunctionNameOrClass, id); m_result.setDeclaration(dec); } else { //global function call foo(); const QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); DeclarationPointer dec = findDeclarationImport(FunctionDeclarationType, id); ifDebug(qCDebug(DUCHAIN) << "function call of" << (dec ? dec->toString() : QString("function not found"));) m_result.setDeclaration(dec); usingDeclaration(node->stringFunctionNameOrClass->namespaceNameSequence->back()->element, dec); buildNamespaceUses(node->stringFunctionNameOrClass, id); if (dec) { FunctionType::Ptr function = dec->type(); if (function) { m_result.setType(function->returnType()); } else { m_result.setType(AbstractType::Ptr()); } } else { m_result.setHadUnresolvedIdentifiers(true); } } } } ///TODO: DUContext pointer? DUContext* ExpressionVisitor::findClassContext(IdentifierAst* className) { DUContext* context = nullptr; DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, className); usingDeclaration(className, declaration); if (declaration) { DUChainReadLocker lock(DUChain::lock()); context = declaration->internalContext(); if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) { //className is currentClass (internalContext is not yet set) context = m_currentContext->parentContext(); } } return context; } ///TODO: DUContext pointer? DUContext* ExpressionVisitor::findClassContext(NamespacedIdentifierAst* className) { DUContext* context = nullptr; const QualifiedIdentifier id = identifierForNamespace(className, m_editor); DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id); usingDeclaration(className->namespaceNameSequence->back()->element, declaration); buildNamespaceUses(className, id); if (declaration) { DUChainReadLocker lock(DUChain::lock()); context = declaration->internalContext(); if (!context && m_currentContext->parentContext() && m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) { //className is currentClass (internalContext is not yet set) context = m_currentContext->parentContext(); } } return context; } void ExpressionVisitor::visitConstantOrClassConst(ConstantOrClassConstAst *node) { DefaultVisitor::visitConstantOrClassConst(node); if (node->classConstant) { //class constant Foo::BAR DUContext* context = findClassContext(node->constant); if (context) { DUChainReadLocker lock(DUChain::lock()); m_result.setDeclarations(context->findDeclarations(Identifier(m_editor->parseSession()->symbol(node->classConstant)))); lock.unlock(); if (!m_result.allDeclarations().isEmpty()) { usingDeclaration(node->classConstant, m_result.allDeclarations().last()); } else { usingDeclaration(node->classConstant, DeclarationPointer()); } if (!stringForNode(node->classConstant).compare(QLatin1String("class"), Qt::CaseInsensitive)) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString))); } } else { m_result.setType(AbstractType::Ptr()); } } else { QString str(stringForNode(node->constant).toLower()); if (str == QLatin1String("true") || str == QLatin1String("false")) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean))); } else if (str == QLatin1String("null")) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeNull))); } else { //constant (created with declare('foo', 'bar')) or const Foo = 1; QualifiedIdentifier id = identifierForNamespace(node->constant, m_editor, true); DeclarationPointer declaration = findDeclarationImport(ConstantDeclarationType, id); if (!declaration) { ///TODO: is this really wanted? //it could also be a global function call, without () declaration = findDeclarationImport(FunctionDeclarationType, id); } m_result.setDeclaration(declaration); usingDeclaration(node->constant->namespaceNameSequence->back()->element, declaration); buildNamespaceUses(node->constant, id); } } } void ExpressionVisitor::visitScalar(ScalarAst *node) { DefaultVisitor::visitScalar(node); if (node->commonScalar) { uint type = IntegralType::TypeVoid; switch (node->commonScalar->scalarType) { case ScalarTypeInt: type = IntegralType::TypeInt; break; case ScalarTypeFloat: type = IntegralType::TypeFloat; break; case ScalarTypeString: type = IntegralType::TypeString; break; } m_result.setType(AbstractType::Ptr(new IntegralType(type))); } else if (node->varname != -1) { //STRING_VARNAME-Token, probably the type of varname should be used m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString))); } else if (node->encapsList) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString))); } if (!m_inDefine && node->commonScalar && node->commonScalar->scalarType == ScalarTypeString) { QString str = m_editor->parseSession()->symbol(node->commonScalar); QRegExp exp("^['\"]([A-Za-z0-9_]+)['\"]$"); if (exp.exactMatch(str)) { //that *could* be a class name QualifiedIdentifier id(exp.cap(1).toLower()); DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id); if (declaration) { usingDeclaration(node->commonScalar, declaration); } else { m_result.setHadUnresolvedIdentifiers(true); } } } } void ExpressionVisitor::visitStaticScalar(StaticScalarAst *node) { if (node->ducontext) { m_currentContext = node->ducontext; } Q_ASSERT(m_currentContext); DefaultVisitor::visitStaticScalar(node); uint type = IntegralType::TypeVoid; if (node->value) { switch (node->value->scalarType) { case ScalarTypeInt: type = IntegralType::TypeInt; break; case ScalarTypeFloat: type = IntegralType::TypeFloat; break; case ScalarTypeString: type = IntegralType::TypeString; break; } } else if (node->plusValue || node->minusValue) { type = IntegralType::TypeInt; } else if (node->array != -1) { type = IntegralType::TypeArray; } if (type != IntegralType::TypeVoid) { m_result.setType(AbstractType::Ptr(new IntegralType(type))); } } void ExpressionVisitor::visitEncapsVar(EncapsVarAst *node) { DefaultVisitor::visitEncapsVar(node); if (node->variable) { // handle $foo DeclarationPointer dec = processVariable(node->variable); if (dec && node->propertyIdentifier) { // handle property in $foo->bar DeclarationPointer foundDec; DUChainReadLocker lock(DUChain::lock()); if ( StructureType::Ptr structType = dec->type() ) { if ( ClassDeclaration* cdec = dynamic_cast(structType->declaration(m_currentContext->topContext())) ) { ///TODO: share code with visitVariableProperty DUContext* ctx = cdec->internalContext(); if (!ctx && m_currentContext->parentContext()) { if (m_currentContext->parentContext()->localScopeIdentifier() == cdec->qualifiedIdentifier()) { //class is currentClass (internalContext is not yet set) ctx = m_currentContext->parentContext(); } } if (ctx) { foreach( Declaration* pdec, ctx->findDeclarations(identifierForNode(node->propertyIdentifier)) ) { if ( !pdec->isFunctionDeclaration() ) { foundDec = pdec; break; } } } } } lock.unlock(); usingDeclaration(node->propertyIdentifier, foundDec); } } } void ExpressionVisitor::visitVariableProperty(VariablePropertyAst *node) { ifDebug(qCDebug(DUCHAIN) << "node:" << m_editor->parseSession()->symbol(node) << (node->isFunctionCall != -1 ? QString("is function call") : QString("is no function call"));) if (node->objectProperty && node->objectProperty->objectDimList) { //handle $foo->bar() and $foo->baz, $foo is m_result.type() if (m_result.type() && StructureType::Ptr::dynamicCast(m_result.type())) { DUChainReadLocker lock(DUChain::lock()); Declaration* declaration = StructureType::Ptr::staticCast(m_result.type())->declaration(m_currentContext->topContext()); if (declaration) { ifDebug(qCDebug(DUCHAIN) << "parent:" << declaration->toString();) DUContext* context = declaration->internalContext(); if (!context && m_currentContext->parentContext()) { if (m_currentContext->parentContext()->localScopeIdentifier() == declaration->qualifiedIdentifier()) { //class is currentClass (internalContext is not yet set) context = m_currentContext->parentContext(); } } if (context) { QualifiedIdentifier propertyId; if ( node->isFunctionCall != -1 ) { propertyId = QualifiedIdentifier(stringForNode(node->objectProperty->objectDimList->variableName->name).toLower()); } else { propertyId = identifierForNode(node->objectProperty->objectDimList->variableName->name); } ifDebug(qCDebug(DUCHAIN) << "property id:" << propertyId.toString();) QList decs; foreach ( Declaration* dec, context->findDeclarations(propertyId) ) { if ( node->isFunctionCall != -1 ) { if ( dec->isFunctionDeclaration() ) { decs << dec; ifDebug(qCDebug(DUCHAIN) << "found:" << dec->toString();) } } else { if ( !dec->isFunctionDeclaration() ) { decs << dec; ifDebug(qCDebug(DUCHAIN) << "found:" << dec->toString();) } } } m_result.setDeclarations(decs); lock.unlock(); if (!m_result.allDeclarations().isEmpty()) { if ( !m_isAssignmentExpressionEqual ) { usingDeclaration(node->objectProperty->objectDimList->variableName, m_result.allDeclarations().last()); } if (node->isFunctionCall != -1) { FunctionType::Ptr function = m_result.allDeclarations().last()->type(); if (function) { m_result.setType(function->returnType()); } else { m_result.setType(AbstractType::Ptr()); } } } else { if ( !m_isAssignmentExpressionEqual ) { usingDeclaration(node->objectProperty->objectDimList->variableName, DeclarationPointer()); } m_result.setType(AbstractType::Ptr()); } } else { m_result.setType(AbstractType::Ptr()); } } else { m_result.setType(AbstractType::Ptr()); } } } DefaultVisitor::visitVariableProperty(node); } void ExpressionVisitor::visitStaticMember(StaticMemberAst* node) { //don't call DefaultVisitor::visitStaticMember(node); //because we would end up in visitCompoundVariableWithSimpleIndirectReference if (node->variable->variable->variable) { DUContext* context = findClassContext(node->className); if (context) { DUChainReadLocker lock(DUChain::lock()); m_result.setDeclarations(context->findDeclarations(identifierForNode(node->variable->variable->variable))); lock.unlock(); if (!m_result.allDeclarations().isEmpty()) { usingDeclaration(node->variable->variable->variable, m_result.allDeclarations().last()); } else { usingDeclaration(node->variable->variable->variable, DeclarationPointer()); } } else { usingDeclaration(node->className, DeclarationPointer()); m_result.setType(AbstractType::Ptr()); } if (node->variable->offsetItemsSequence) { const KDevPG::ListNode< DimListItemAst* >* it = node->variable->offsetItemsSequence->front(); do { visitDimListItem(it->element); } while(it->hasNext() && (it = it->next)); } } } void ExpressionVisitor::visitUnaryExpression(UnaryExpressionAst* node) { DefaultVisitor::visitUnaryExpression(node); if (node->castType) { uint type = 0; switch (node->castType) { case CastInt: type = IntegralType::TypeInt; break; case CastDouble: type = IntegralType::TypeFloat; break; case CastString: type = IntegralType::TypeString; break; case CastArray: type = IntegralType::TypeArray; break; case CastObject: { /// Qualified identifier for 'stdclass' static const QualifiedIdentifier stdclassQId(QStringLiteral("stdclass")); DUChainReadLocker lock(DUChain::lock()); m_result.setDeclarations(m_currentContext->findDeclarations(stdclassQId)); break; } case CastBool: type = IntegralType::TypeBoolean; break; case CastUnset: //TODO break; } if (type) { m_result.setType(AbstractType::Ptr(new IntegralType(type))); } } } void ExpressionVisitor::visitAdditiveExpressionRest(AdditiveExpressionRestAst* node) { DefaultVisitor::visitAdditiveExpressionRest(node); if (node->operation == OperationPlus || node->operation == OperationMinus) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt))); } else if (node->operation == OperationConcat) { m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeString))); } } void ExpressionVisitor::visitRelationalExpression(RelationalExpressionAst *node) { DefaultVisitor::visitRelationalExpression(node); if (node->instanceofType && node->instanceofType->identifier) { const QualifiedIdentifier id = identifierForNamespace(node->instanceofType->identifier, m_editor); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); usingDeclaration(node->instanceofType->identifier->namespaceNameSequence->back()->element, dec); buildNamespaceUses(node->instanceofType->identifier, id); m_result.setDeclaration(dec); } } QString ExpressionVisitor::stringForNode(AstNode* id) { if (!id) return QString(); return m_editor->parseSession()->symbol(id); } QualifiedIdentifier ExpressionVisitor::identifierForNode(IdentifierAst* id) { if (!id) return QualifiedIdentifier(); return QualifiedIdentifier(stringForNode(id)); } QString ExpressionVisitor::stringForNode(VariableIdentifierAst* id) { if (!id) return QString(); QString ret(m_editor->parseSession()->symbol(id->variable)); ret = ret.mid(1); //cut off $ return ret; } QualifiedIdentifier ExpressionVisitor::identifierForNode(VariableIdentifierAst* id) { if (!id) return QualifiedIdentifier(); return QualifiedIdentifier(stringForNode(id)); } void ExpressionVisitor::setCreateProblems(bool v) { m_createProblems = v; } void ExpressionVisitor::setOffset(const CursorInRevision& offset) { m_offset = offset; } void ExpressionVisitor::buildNamespaceUses(NamespacedIdentifierAst* namespaces, const QualifiedIdentifier& identifier) { QualifiedIdentifier curId; curId.setExplicitlyGlobal(identifier.explicitlyGlobal()); Q_ASSERT(identifier.count() == namespaces->namespaceNameSequence->count()); for ( int i = 0; i < identifier.count() - 1; ++i ) { curId.push(identifier.at(i)); AstNode* node = namespaces->namespaceNameSequence->at(i)->element; DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId); usingDeclaration(node, dec); } } DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, IdentifierAst* node) { // methods and class names are case insensitive QualifiedIdentifier id; if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) { id = QualifiedIdentifier(stringForNode(node).toLower()); } else { id = identifierForNode(node); } return findDeclarationImport(declarationType, id); } DeclarationPointer ExpressionVisitor::findDeclarationImport(DeclarationType declarationType, VariableIdentifierAst* node) { return findDeclarationImport(declarationType, identifierForNode(node)); } DeclarationPointer ExpressionVisitor::findDeclarationImport( DeclarationType declarationType, const QualifiedIdentifier& identifier) { return findDeclarationImportHelper(m_currentContext, identifier, declarationType); } Declaration* ExpressionVisitor::findVariableDeclaration(DUContext* context, Identifier identifier, CursorInRevision position, DUContext::SearchFlag flag) { QList decls = context->findDeclarations(identifier, position, nullptr, flag); for (int i = decls.count() - 1; i >= 0; i--) { Declaration *dec = decls.at(i); if (dec->kind() == Declaration::Instance && dynamic_cast(dec)) { return dec; } } return nullptr; } } diff --git a/duchain/tests/uses.cpp b/duchain/tests/uses.cpp index 0615882..c3e1538 100644 --- a/duchain/tests/uses.cpp +++ b/duchain/tests/uses.cpp @@ -1,1247 +1,1274 @@ /* This file is part of KDevelop Copyright 2008 Niko Sams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "uses.h" #include #include #include #include "../declarations/classdeclaration.h" #include "../declarations/variabledeclaration.h" #include "../declarations/traitmethodaliasdeclaration.h" #include "../declarations/traitmemberaliasdeclaration.h" using namespace KDevelop; QTEST_MAIN(Php::TestUses) namespace Php { void compareUses(Declaration* dec, QList ranges) { qDebug() << "comparing uses for" << dec->toString(); QCOMPARE(dec->uses().keys().count(), 1); QCOMPARE(dec->uses().values().count(), 1); QCOMPARE(dec->uses().values().first().count(), ranges.count()); for (int i = 0; i < ranges.count(); ++i) { qDebug() << dec->uses().values().first().at(i) << ranges.at(i); QCOMPARE(dec->uses().values().first().at(i), ranges.at(i)); } } void compareUses(Declaration* dec, RangeInRevision range) { QList r; r << range; compareUses(dec, r); } TestUses::TestUses() { } void TestUses::newObject() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 25, 0, 28)); QCOMPARE(top->localDeclarations().first()->uses().keys().first(), IndexedString(QUrl("file:///internal/usestest/newObject.php"))); } void TestUses::functionCall() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(); compareUses(fun, RangeInRevision(0, 21, 0, 24)); QCOMPARE(fun->uses().keys().first(), IndexedString(QUrl("file:///internal/usestest/functionCall.php"))); } void TestUses::memberFunctionCall() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo(); "); TopDUContext* top = parse(method, DumpNone, QUrl(QStringLiteral("file:///internal/usestest/memberFunctionCall.php"))); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); Declaration* fun = top->childContexts().first()->localDeclarations().first(); compareUses(fun, RangeInRevision(0, 51, 0, 54)); QCOMPARE(fun->uses().keys().first(), IndexedString(QUrl("file:///internal/usestest/memberFunctionCall.php"))); } void TestUses::memberVariable() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo; "); TopDUContext* top = parse(method, DumpNone, QUrl(QStringLiteral("file:///internal/usestest/memberVariable.php"))); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); Declaration* var = top->childContexts().first()->localDeclarations().first(); compareUses(var, RangeInRevision(0, 46, 0, 49)); QCOMPARE(var->uses().keys().first(), IndexedString(QUrl("file:///internal/usestest/memberVariable.php"))); } void TestUses::variable() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo; foo($a); "); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(1, 42 - 3, 1, 44 - 3) << RangeInRevision(1, 46 - 3, 1, 48 - 3) << RangeInRevision(1, 59 - 3, 1, 61 - 3); compareUses(top->localDeclarations().at(1), ranges); } void TestUses::varInString() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" ranges; ranges << RangeInRevision(0, 13, 0, 15) << RangeInRevision(0, 17, 0, 19); compareUses(top->localDeclarations().at(0), ranges); } void TestUses::variableInNamespace() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo; foo($a); };"); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(1, 55, 1, 57) << RangeInRevision(1, 59, 1, 61) << RangeInRevision(1, 72, 1, 74); compareUses(top->localDeclarations().at(2), ranges); } void TestUses::globalVariableInNamespace() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo; foo($a); };"); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(1, 55, 1, 57) << RangeInRevision(1, 59, 1, 61) << RangeInRevision(1, 72, 1, 74); compareUses(top->localDeclarations().at(1), ranges); } void TestUses::variableInOtherNamespace() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("foo; foo($a); };"); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(1, 73, 1, 75) << RangeInRevision(1, 77, 1, 79) << RangeInRevision(1, 90, 1, 92); compareUses(top->localDeclarations().at(2), ranges); } void TestUses::memberVarInString() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789 01234567890123 4567890123456789 QByteArray method("v; \"$a->v {$a->v}\"; "); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(0, 43, 0, 45) << RangeInRevision(0, 51, 0, 53) << RangeInRevision(0, 58, 0, 60); compareUses(top->localDeclarations().at(1), ranges); ranges.clear(); ranges << RangeInRevision(0, 47, 0, 48) << RangeInRevision(0, 55, 0, 56) << RangeInRevision(0, 62, 0, 63); compareUses(top->childContexts().first()->localDeclarations().first(), ranges); } void TestUses::memberFunctionInString() { // 0 1 2 3 4 5 6 7 // 012345678901234567890123456789012345678901234567 890123456789 01234567890123456789 QByteArray method("foo()}\"; "); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); //$a compareUses(top->localDeclarations().at(1), RangeInRevision(0, 50, 0, 52)); //foo compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 54, 0, 57)); } void TestUses::variableTypeChange() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" ranges; ranges << RangeInRevision(0, 25, 0, 27); ranges << RangeInRevision(0, 29, 0, 31); ranges << RangeInRevision(0, 37, 0, 39); ranges << RangeInRevision(0, 41, 0, 43); ranges << RangeInRevision(0, 51, 0, 53); compareUses(top->localDeclarations().at(1), ranges); } void TestUses::variableTypeChangeInFunction() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" ranges; ranges << RangeInRevision(0, 28, 0, 30); ranges << RangeInRevision(0, 32, 0, 34); ranges << RangeInRevision(0, 38, 0, 40); ranges << RangeInRevision(0, 42, 0, 44); compareUses(top->childContexts().at(1)->localDeclarations().at(0), ranges); } void TestUses::classExtends() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0), RangeInRevision(0, 31, 0, 32)); } void TestUses::classImplements() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0), RangeInRevision(0, 38, 0, 39)); } void TestUses::classImplementsMultiple() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0), RangeInRevision(0, 54, 0, 55)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 57, 0, 58)); } void TestUses::interfaceExtends() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0), RangeInRevision(0, 39, 0, 40)); } void TestUses::interfaceExtendsMultiple() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0), RangeInRevision(0, 55, 0, 56)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 58, 0, 59)); } void TestUses::staticMemberFunctionCall() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 47, 0, 48)); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 50, 0, 53)); } void TestUses::staticMemberVariable() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 43, 0, 44)); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 46, 0, 50)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 52, 0, 56)); } void TestUses::constant() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 28, 0, 29)); } void TestUses::classConstant() { { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 39, 0, 40)); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 42, 0, 45)); } { // bug: https://bugs.kde.org/show_bug.cgi?id=241597 // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("childContexts().first()->localDeclarations().first(); QVERIFY(dec->abstractType()->modifiers() & AbstractType::ConstModifier); QCOMPARE(dec->qualifiedIdentifier().toString(), QString("a::FOO")); compareUses(dec, QList() << RangeInRevision(1, 43, 1, 46) << RangeInRevision(2, 3, 2, 6) << RangeInRevision(3, 3, 3, 6)); } } void TestUses::classParent() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" range; range << RangeInRevision(0, 47, 0, 48); range << RangeInRevision(0, 66, 0, 72); compareUses(top->localDeclarations().first(), range); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 74, 0, 75)); } void TestUses::classSelf() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 28, 0, 32)); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 34, 0, 35)); } void TestUses::classThis() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("x(); } } "); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); compareUses(top->localDeclarations().first(), RangeInRevision(0, 28, 0, 33)); compareUses(top->childContexts().first()->localDeclarations().first(), RangeInRevision(0, 35, 0, 36)); } void TestUses::objectWithClassName() { // 0 1 2 3 4 5 6 7 8 // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("k; Aa::j; Aa::$i;"); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QList ranges; ranges << RangeInRevision(0, 66, 0, 66 + 2); ranges << RangeInRevision(0, 78, 0, 78 + 2); ranges << RangeInRevision(0, 85, 0, 85 + 2); compareUses(top->localDeclarations().first(), ranges); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 70, 0, 70 + 3)); } void TestUses::classAndConstWithSameName() { // 0 1 2 3 4 5 6 7 8 // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 38, 0, 39)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 31, 0, 32)); compareUses(top->localDeclarations().at(2), RangeInRevision(0, 76, 0, 77)); compareUses(top->localDeclarations().at(3), RangeInRevision(0, 73, 0, 74)); } void TestUses::classAndFunctionWithSameName() { // 0 1 2 3 4 5 6 7 8 // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 35, 0, 36)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 38, 0, 39)); } void TestUses::constAndVariableWithSameName() { // 0 1 2 3 4 5 6 7 8 // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(), RangeInRevision(0, 30, 0, 32)); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 27, 0, 28)); } void TestUses::functionAndClassWithSameName() { // 0 1 2 3 4 5 6 7 8 // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("childContexts().first()->localDeclarations().first(); QCOMPARE(fnAsdf->uses().keys().count(), 0); compareUses(top->localDeclarations().at(1), RangeInRevision(0, 70, 0, 74)); } void TestUses::constantInClassMember() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("findDeclarations(Identifier(QStringLiteral("TEST"))).first(); QList uses; uses << RangeInRevision(0, 41, 0, 45); uses << RangeInRevision(0, 63, 0, 67); uses << RangeInRevision(0, 73, 0, 77); compareUses(constant, uses); } void TestUses::useInAsignment() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().first(); compareUses(d, RangeInRevision(0, 16, 0, 18)); } void TestUses::foreachArray() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("$i) { var_dump($k, $i); } "); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); // $a, $k, $i QCOMPARE(top->localDeclarations().size(), 3); // $a Declaration *d = top->localDeclarations().at(0); compareUses(d, RangeInRevision(0, 26, 0, 28)); // $k d = top->localDeclarations().at(1); compareUses(d, RangeInRevision(0, 51, 0, 53)); // $i d = top->localDeclarations().at(2); compareUses(d, RangeInRevision(0, 55, 0, 57)); } void TestUses::assignmentToMemberArray() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("y[$a] = true; } }"); TopDUContext* top = parse(method, DumpAll); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); // class x Declaration *x = top->localDeclarations().first(); QVERIFY(x); // $this compareUses(x, RangeInRevision(0, 50, 0, 55)); // var $y Declaration *y = x->logicalInternalContext(top)->findDeclarations(Identifier(QStringLiteral("y"))).first(); QVERIFY(y); // $this->y compareUses(y, RangeInRevision(0, 57, 0, 58)); // function z Declaration *z = x->logicalInternalContext(top)->findDeclarations(Identifier(QStringLiteral("z"))).first(); QVERIFY(z); // $a Declaration *a = z->logicalInternalContext(top)->findDeclarations(Identifier(QStringLiteral("a"))).first(); QVERIFY(a); compareUses(a, QList() // $b = $a << RangeInRevision(0, 46, 0, 48) // $this->y[$a] << RangeInRevision(0, 59, 0, 61) ); } void TestUses::staticArrayIndex() { // bug: https://bugs.kde.org/show_bug.cgi?id=241160 // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("childContexts().first()->localDeclarations().first(); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, RangeInRevision(0, 68, 0, 70)); Declaration* i = top->childContexts().first()->childContexts().first()->localDeclarations().first(); QCOMPARE(i->identifier().toString(), QString("i")); compareUses(i, RangeInRevision(0, 71, 0, 73)); } void TestUses::functionParamNewDeclaration() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("childContexts().first()->localDeclarations().first(); QList ranges; ranges << RangeInRevision(0, 22, 0, 24); ranges << RangeInRevision(0, 26, 0, 28); compareUses(d, ranges); } void TestUses::catchClass() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("findDeclarations(QualifiedIdentifier(QStringLiteral("exception"))).first(); compareUses(d, RangeInRevision(0, 18, 0, 27)); } void TestUses::variableRedeclaration() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" decs = top->findDeclarations(QualifiedIdentifier(QStringLiteral("s"))); QCOMPARE(decs.size(), 1); Declaration *d = decs.first(); compareUses(d, QList() << RangeInRevision(0, 13, 0, 15) << RangeInRevision(0, 18, 0, 20) << RangeInRevision(0, 23, 0, 25) ); } void TestUses::caseInsensitiveFunction() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" decs = top->findLocalDeclarations(Identifier(QStringLiteral("foobar"))); QCOMPARE(decs.size(), 1); Declaration *d = decs.first(); compareUses(d, QList() << RangeInRevision(1, 0, 1, 6) << RangeInRevision(2, 0, 2, 6) << RangeInRevision(3, 0, 3, 6) ); } void TestUses::caseInsensitiveMethod() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("fOoBar();\n$a->FOOBAR();\n$a->foobar();\n" "asdf::barfoo();\nasdf::bArFoo();\nasdf::BARFOO();\n"); TopDUContext* top = parse(method, DumpNone); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); { QList decs = top->childContexts().first()->findDeclarations(QualifiedIdentifier(QStringLiteral("foobar"))); QCOMPARE(decs.size(), 1); Declaration *d = decs.first(); compareUses(d, QList() << RangeInRevision(1, 4, 1, 10) << RangeInRevision(2, 4, 2, 10) << RangeInRevision(3, 4, 3, 10) ); } { QList decs = top->childContexts().first()->findDeclarations(QualifiedIdentifier(QStringLiteral("barfoo"))); QCOMPARE(decs.size(), 1); Declaration *d = decs.first(); compareUses(d, QList() << RangeInRevision(4, 6, 4, 12) << RangeInRevision(5, 6, 5, 12) << RangeInRevision(6, 6, 6, 12) ); } } void TestUses::caseInsensitiveClass() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" decs = top->findLocalDeclarations(Identifier(QStringLiteral("asdf"))); QCOMPARE(decs.size(), 1); Declaration *d = decs.first(); compareUses(d, QList() << RangeInRevision(1, 4, 1, 8) << RangeInRevision(2, 4, 2, 8) << RangeInRevision(3, 4, 3, 8) ); } void TestUses::functionUseBeforeDeclaration() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" decs = top->localDeclarations(); QCOMPARE(decs.size(), 1); QCOMPARE(decs.first()->range(), RangeInRevision(0, 20, 0, 24)); compareUses(decs.first(), RangeInRevision(0, 3, 0, 7)); } void TestUses::propertyAndMethodWithSameName() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("name1(); $a->name1;\n" "$a->name2; $a->name2();"); TopDUContext* top = parse(method, DumpNone); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QVector decs = top->childContexts().first()->localDeclarations(); QCOMPARE(decs.size(), 4); // method name1 QVERIFY(decs[0]->identifier().nameEquals(Identifier("name1"))); QVERIFY(decs[0]->isFunctionDeclaration()); compareUses(decs[0], RangeInRevision(2, 4, 2, 9)); // property name1 QVERIFY(decs[1]->identifier().nameEquals(Identifier("name1"))); QVERIFY(!decs[1]->isFunctionDeclaration()); compareUses(decs[1], RangeInRevision(2, 17, 2, 22)); // property name2 QVERIFY(decs[2]->identifier().nameEquals(Identifier("name2"))); QVERIFY(!decs[2]->isFunctionDeclaration()); compareUses(decs[2], RangeInRevision(3, 4, 3, 9)); // method name2 QVERIFY(decs[3]->identifier().nameEquals(Identifier("name2"))); QVERIFY(decs[3]->isFunctionDeclaration()); compareUses(decs[3], RangeInRevision(3, 15, 3, 20)); } void TestUses::nestedMethodCalls() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("a($b->b());"); TopDUContext* top = parse(method, DumpNone); DUChainReleaser releaseTop(top); DUChainWriteLocker lock(DUChain::lock()); QVector topDecs = top->localDeclarations(); QCOMPARE(topDecs.size(), 4); // class a QVERIFY(topDecs[0]->identifier().nameEquals(Identifier("a"))); QVERIFY(dynamic_cast(topDecs[0])); compareUses(topDecs[0], RangeInRevision(3, 9, 3, 10)); // class b QVERIFY(topDecs[1]->identifier().nameEquals(Identifier("b"))); QVERIFY(dynamic_cast(topDecs[1])); compareUses(topDecs[1], RangeInRevision(4, 9, 4, 10)); // $a QVERIFY(topDecs[2]->identifier().nameEquals(Identifier("a"))); QVERIFY(dynamic_cast(topDecs[2])); compareUses(topDecs[2], RangeInRevision(5, 0, 5, 2)); // $b QVERIFY(topDecs[3]->identifier().nameEquals(Identifier("b"))); QVERIFY(dynamic_cast(topDecs[3])); compareUses(topDecs[3], RangeInRevision(5, 6, 5, 8)); // function a Declaration* methodADec = topDecs[0]->internalContext()->localDeclarations().first(); QVERIFY(methodADec->isFunctionDeclaration()); compareUses(methodADec, RangeInRevision(5, 4, 5, 5)); // function b Declaration* methodBDec = topDecs[1]->internalContext()->localDeclarations().first(); QVERIFY(methodBDec->isFunctionDeclaration()); compareUses(methodBDec, RangeInRevision(5, 10, 5, 11)); } void TestUses::unset() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method(" decs = top->localDeclarations(); QCOMPARE(decs.size(), 1); QCOMPARE(decs.first()->range(), RangeInRevision(0, 3, 0, 5)); compareUses(decs.first(), RangeInRevision(0, 17, 0, 19)); } void TestUses::functionArguments() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("childContexts().size(), 2); QCOMPARE(top->childContexts().first()->type(), DUContext::Function); // $a Declaration *d = top->childContexts().at(0)->localDeclarations().at(0); compareUses(d, RangeInRevision(0, 27, 0, 29)); // $b d = top->childContexts().at(0)->localDeclarations().at(1); compareUses(d, RangeInRevision(0, 35, 0, 37)); } void TestUses::namespaces() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("findDeclarations(QualifiedIdentifier(QStringLiteral("foo"))).last(); QCOMPARE(dec->kind(), Declaration::Namespace); compareUses(dec, QList() << RangeInRevision(9, 1, 9, 4) << RangeInRevision(10, 1, 10, 4) << RangeInRevision(11, 1, 11, 4) << RangeInRevision(12, 5, 12, 8) << RangeInRevision(13, 15, 13, 18) << RangeInRevision(14, 17, 14, 20) << RangeInRevision(14, 45, 14, 48)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar"))).first(); QCOMPARE(dec->kind(), Declaration::Namespace); QVERIFY(dec->internalContext()); compareUses(dec, QList() << RangeInRevision(9, 5, 9, 8) << RangeInRevision(10, 5, 10, 8) << RangeInRevision(11, 5, 11, 8) << RangeInRevision(12, 9, 12, 12) << RangeInRevision(13, 19, 13, 22) << RangeInRevision(14, 21, 14, 24) << RangeInRevision(14, 49, 14, 52)); QCOMPARE(dec->internalContext()->localDeclarations().size(), 4); foreach(Declaration* d, dec->internalContext()->localDeclarations()) { qDebug() << d->toString() << d->qualifiedIdentifier(); } dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::MyConst"))).first(); compareUses(dec, QList() << RangeInRevision(3, 5, 3, 12) << RangeInRevision(9, 9, 9, 16)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::myclass"))).first(); QVERIFY(dynamic_cast(dec)); compareUses(dec, QList() << RangeInRevision(10, 9, 10, 16) << RangeInRevision(12, 13, 12, 20) << RangeInRevision(13, 23, 13, 30) << RangeInRevision(14, 25, 14, 32) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::myinterface"))).first(); QVERIFY(dynamic_cast(dec)); compareUses(dec, RangeInRevision(14, 53, 14, 64) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::myclass::ClassConst"))).first(); compareUses(dec, RangeInRevision(10, 18, 10, 28)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::myfunc"))).first(); compareUses(dec, RangeInRevision(11, 9, 11, 15)); } void TestUses::useNamespace() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("findDeclarations(QualifiedIdentifier(QStringLiteral("foo"))).first(); QCOMPARE(dec->kind(), Declaration::Namespace); compareUses(dec, RangeInRevision(5, 4, 5, 7)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar"))).first(); QCOMPARE(dec->kind(), Declaration::Namespace); compareUses(dec, RangeInRevision(5, 8, 5, 11)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("verylong"))).first(); QCOMPARE(dec->kind(), Declaration::Namespace); compareUses(dec, RangeInRevision(5, 13, 5, 21)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("bar"))).first(); QCOMPARE(dec->kind(), Declaration::NamespaceAlias); compareUses(dec, QList() << RangeInRevision(7, 4, 7, 7) << RangeInRevision(7, 11, 7, 14) << RangeInRevision(7, 20, 7, 23) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("short"))).first(); QCOMPARE(dec->kind(), Declaration::NamespaceAlias); compareUses(dec, QList() << RangeInRevision(8, 4, 8, 9) << RangeInRevision(8, 13, 8, 18) << RangeInRevision(8, 24, 8, 29) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("baz::a"))).first(); compareUses(dec, QList() << RangeInRevision(6, 8, 6, 9) << RangeInRevision(9, 4, 9, 10)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::a"))).first(); compareUses(dec, RangeInRevision(7, 8, 7, 9)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::b"))).first(); compareUses(dec, RangeInRevision(7, 15, 7, 16)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("foo::bar::C"))).first(); compareUses(dec, RangeInRevision(7, 24, 7, 25)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("verylong::a"))).first(); compareUses(dec, RangeInRevision(8, 10, 8, 11)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("verylong::b"))).first(); compareUses(dec, RangeInRevision(8, 19, 8, 20)); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("verylong::C"))).first(); compareUses(dec, RangeInRevision(8, 30, 8, 31)); } void TestUses::lateStatic() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("childContexts().first()->localDeclarations().first(), RangeInRevision(0, 39, 0, 40)); } void TestUses::closures() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("localDeclarations().count(), 4); Declaration* a = top->localDeclarations().at(0); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, QList() << RangeInRevision(1, 23, 1, 25) << RangeInRevision(1, 36, 1, 38)); Declaration* b = top->localDeclarations().at(1); QCOMPARE(b->identifier().toString(), QString("b")); QVERIFY(b->uses().isEmpty()); } +void TestUses::closureTypehints() { + TopDUContext* top = parse("localDeclarations().count(), 3); + Declaration* a = top->localDeclarations().at(0); + QCOMPARE(a->qualifiedIdentifier(), QualifiedIdentifier("a")); + compareUses(a, QList() << RangeInRevision(0, 32, 0, 33) << RangeInRevision(0, 39, 0, 40)); +} + void TestUses::instanceof() { // 0 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("localDeclarations().count(), 3); Declaration* a = top->localDeclarations().at(0); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, QList() << RangeInRevision(1, 9, 1, 10) << RangeInRevision(2, 19, 2, 20)); } void TestUses::classNameString() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 QByteArray method("localDeclarations().at(0); QCOMPARE(foo->identifier().toString(), QString("foo")); compareUses(foo, RangeInRevision(0, 22, 0, 27)); } void TestUses::useTrait() { // 0 1 2 3 4 5 6 7 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 TopDUContext* top = parse("one(); $a->two();\n" "$b = new Bar(); $b->one(); $b->two(); $b->four();\n" "$c = new Baz(); $c->one(); $c->two(); $c->baz; $c::six();\n", DumpAll); QVERIFY(top); DUChainReleaser releaseTop(top); DUChainWriteLocker lock; Declaration* dec; QVector topDecs = top->localDeclarations(); TraitMethodAliasDeclaration* method; TraitMemberAliasDeclaration* member; QCOMPARE(topDecs.size(), 9); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("a"))).first(); compareUses(dec, QList() << RangeInRevision(4, 16, 4, 17) << RangeInRevision(5, 16, 5, 17) << RangeInRevision(5, 22, 5, 23) << RangeInRevision(6, 16, 6, 17) << RangeInRevision(6, 41, 6, 42) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("b"))).first(); compareUses(dec, QList() << RangeInRevision(5, 18, 5, 19) << RangeInRevision(5, 39, 5, 40) << RangeInRevision(5, 42, 5, 43) << RangeInRevision(6, 18, 6, 19) << RangeInRevision(6, 43, 6, 44) ); dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("c"))).first(); compareUses(dec, QList() << RangeInRevision(6, 20, 6, 21) << RangeInRevision(6, 24, 6, 25) << RangeInRevision(6, 46, 6, 47) ); dec = topDecs[3]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("one"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); compareUses(dec, QList() << RangeInRevision(7, 20, 7, 23) ); dec = topDecs[3]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("two"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); compareUses(dec, QList() << RangeInRevision(7, 31, 7, 34) ); dec = topDecs[4]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("one"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); compareUses(dec, QList() << RangeInRevision(8, 20, 8, 23) ); dec = topDecs[4]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("two"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); compareUses(dec, QList() << RangeInRevision(8, 31, 8, 34) ); dec = topDecs[4]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("four"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("b")); QCOMPARE(method->aliasedDeclaration().data()->identifier(), Identifier("three")); QCOMPARE(method->accessPolicy(), Declaration::AccessPolicy::Public); compareUses(dec, QList() << RangeInRevision(8, 42, 8, 46) ); dec = topDecs[5]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("one"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("c")); compareUses(dec, QList() << RangeInRevision(9, 20, 9, 23) ); dec = topDecs[5]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("two"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); compareUses(dec, QList() << RangeInRevision(9, 31, 9, 34) ); dec = topDecs[5]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("baz"))).first(); member = dynamic_cast(dec); QVERIFY(member); QCOMPARE(member->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("c")); compareUses(dec, QList() << RangeInRevision(9, 42, 9, 45) ); dec = topDecs[5]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("six"))).first(); method = dynamic_cast(dec); QVERIFY(method); QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("c")); QCOMPARE(method->aliasedDeclaration().data()->identifier(), Identifier("five")); QVERIFY(method->isStatic()); compareUses(dec, QList() << RangeInRevision(9, 51, 9, 54) ); } void TestUses::exceptionFinally() { // 0 1 2 3 4 // 01234567890123456789012345678901234567890123456 QByteArray method("localDeclarations().at(0); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, QList() << RangeInRevision(0, 17, 0, 19) << RangeInRevision(0, 37, 0, 39)); } void TestUses::returnTypeClassFunction() { QByteArray method("localDeclarations().at(0); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, QList() << RangeInRevision(0, 29, 0, 30) << RangeInRevision(0, 50, 0, 54)); } void TestUses::returnTypeFunction() { QByteArray method("localDeclarations().at(0); QCOMPARE(a->identifier().toString(), QString("a")); compareUses(a, QList() << RangeInRevision(0, 30, 0, 31)); } +void TestUses::defaultValue() { + QByteArray method("localDeclarations().at(0); + QCOMPARE(a->identifier().toString(), QString("a")); + compareUses(a, QList() << RangeInRevision(0, 44, 0, 45)); + + Declaration *c = top->childContexts().at(0)->localDeclarations().at(0); + QCOMPARE(c->identifier().toString(), QString("C")); + compareUses(c, QList() << RangeInRevision(0, 47, 0, 48)); +} + } diff --git a/duchain/tests/uses.h b/duchain/tests/uses.h index ae52d4c..fa4580f 100644 --- a/duchain/tests/uses.h +++ b/duchain/tests/uses.h @@ -1,94 +1,96 @@ /* This file is part of KDevelop Copyright 2008 Niko Sams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TESTUSES_H #define TESTUSES_H #include "../../duchain/tests/duchaintestbase.h" namespace Php { class TestUses : public DUChainTestBase { Q_OBJECT public: TestUses(); private slots: void newObject(); void functionCall(); void memberFunctionCall(); void memberVariable(); void variable(); void varInString(); void variableInNamespace(); void globalVariableInNamespace(); void variableInOtherNamespace(); void memberVarInString(); void memberFunctionInString(); void variableTypeChange(); void variableTypeChangeInFunction(); void classExtends(); void classImplements(); void classImplementsMultiple(); void interfaceExtends(); void interfaceExtendsMultiple(); void staticMemberFunctionCall(); void staticMemberVariable(); void constant(); void classConstant(); void classParent(); void classSelf(); void classThis(); void objectWithClassName(); void classAndConstWithSameName(); void classAndFunctionWithSameName(); void constAndVariableWithSameName(); void functionAndClassWithSameName(); void constantInClassMember(); void useInAsignment(); void foreachArray(); void assignmentToMemberArray(); void staticArrayIndex(); void functionParamNewDeclaration(); void catchClass(); void variableRedeclaration(); void caseInsensitiveFunction(); void caseInsensitiveMethod(); void caseInsensitiveClass(); void functionUseBeforeDeclaration(); void propertyAndMethodWithSameName(); void nestedMethodCalls(); void unset(); void functionArguments(); void namespaces(); void useNamespace(); void lateStatic(); void closures(); + void closureTypehints(); void instanceof(); void classNameString(); void useTrait(); void exceptionFinally(); void returnTypeClassFunction(); void returnTypeFunction(); + void defaultValue(); }; } #endif