diff --git a/duchain/builders/contextbuilder.cpp b/duchain/builders/contextbuilder.cpp index 929dc9a..ba6f638 100644 --- a/duchain/builders/contextbuilder.cpp +++ b/duchain/builders/contextbuilder.cpp @@ -1,498 +1,507 @@ /*************************************************************************** * 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 "contextbuilder.h" #include #include #include #include #include #include #include #include #include #include #include "../editorintegrator.h" #include "../helper.h" #include "../phpducontext.h" #include "../parser/parsesession.h" #include "../parser/phpast.h" #include using namespace KDevelop; namespace Php { ContextBuilder::ContextBuilder() : m_isInternalFunctions(false), m_reportErrors(true), m_mapAst(false), m_hadUnresolvedIdentifiers(false), m_editor(nullptr), m_openNamespaces(nullptr) { } ContextBuilder::~ContextBuilder() { } EditorIntegrator* ContextBuilder::editor() const { return m_editor; } ReferencedTopDUContext ContextBuilder::build(const IndexedString& url, AstNode* node, ReferencedTopDUContext updateContext) { m_isInternalFunctions = url == internalFunctionFile(); if ( m_isInternalFunctions ) { m_reportErrors = false; } else if ( ICore::self() ) { m_reportErrors = ICore::self()->languageController()->completionSettings()->highlightSemanticProblems(); } if (!updateContext) { DUChainReadLocker lock(DUChain::lock()); updateContext = DUChain::self()->chainForDocument(url); } if (updateContext) { qCDebug(DUCHAIN) << "re-compiling" << url.str(); DUChainWriteLocker lock(DUChain::lock()); updateContext->clearImportedParentContexts(); updateContext->parsingEnvironmentFile()->clearModificationRevisions(); updateContext->clearProblems(); updateContext->updateImportsCache(); } else { qCDebug(DUCHAIN) << "compiling" << url.str(); } ReferencedTopDUContext top = ContextBuilderBase::build(url, node, updateContext); { DUChainWriteLocker lock(DUChain::lock()); top->updateImportsCache(); } return top; } bool ContextBuilder::hadUnresolvedIdentifiers() const { return m_hadUnresolvedIdentifiers; } void ContextBuilder::startVisiting(AstNode* node) { if (compilingContexts()) { TopDUContext* top = dynamic_cast(currentContext()); Q_ASSERT(top); { DUChainWriteLocker lock(DUChain::lock()); top->updateImportsCache(); //Mark that we will use a cached import-structure } bool hasImports; { DUChainReadLocker lock(DUChain::lock()); hasImports = !top->importedParentContexts().isEmpty(); } if (!hasImports && top->url() != internalFunctionFile()) { DUChainWriteLocker lock(DUChain::lock()); TopDUContext* import = DUChain::self()->chainForDocument(internalFunctionFile()); if (!import) { qWarning() << "importing internalFunctions failed" << currentContext()->url().str(); Q_ASSERT(false); } else { top->addImportedParentContext(import); top->updateImportsCache(); } } } visitNode(node); if (m_openNamespaces) { closeNamespaces(m_openNamespaces); m_openNamespaces = nullptr; } } DUContext* ContextBuilder::newContext(const RangeInRevision& range) { return new PhpDUContext(range, currentContext()); } TopDUContext* ContextBuilder::newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file) { if (!file) { file = new ParsingEnvironmentFile(m_editor->parseSession()->currentDocument()); /// Indexed string for 'Php', identifies environment files from this language plugin static const IndexedString phpLangString("Php"); file->setLanguage(phpLangString); } TopDUContext* ret = new PhpDUContext(m_editor->parseSession()->currentDocument(), range, file); ret->setType(DUContext::Global); return ret; } void ContextBuilder::setContextOnNode(AstNode* node, DUContext* ctx) { node->ducontext = ctx; } DUContext* ContextBuilder::contextFromNode(AstNode* node) { return node->ducontext; } RangeInRevision ContextBuilder::editorFindRange(AstNode* fromRange, AstNode* toRange) { return m_editor->findRange(fromRange, toRange ? toRange : fromRange); } CursorInRevision ContextBuilder::startPos(AstNode* node) { return m_editor->findPosition(node->startToken, EditorIntegrator::FrontEdge); } QualifiedIdentifier ContextBuilder::identifierForNode(IdentifierAst* id) { if (!id) return QualifiedIdentifier(); return QualifiedIdentifier(stringForNode(id)); } QualifiedIdentifier ContextBuilder::identifierForNode(VariableIdentifierAst* id) { if (!id) return QualifiedIdentifier(); QString ret(stringForNode(id)); ret = ret.mid(1); //cut off $ return QualifiedIdentifier(ret); } IdentifierPair ContextBuilder::identifierPairForNode( IdentifierAst* id ) { if (!id) { return qMakePair(IndexedString(), QualifiedIdentifier()); } const QString ret = stringForNode(id); return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower())); } QString ContextBuilder::stringForNode(IdentifierAst* node) const { return m_editor->parseSession()->symbol(node->string); } QString ContextBuilder::stringForNode(VariableIdentifierAst* node) const { return m_editor->parseSession()->symbol(node->variable); } void ContextBuilder::visitClassDeclarationStatement(ClassDeclarationStatementAst* node) { openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->className).second); classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration()) DefaultVisitor::visitClassDeclarationStatement(node); closeContext(); } void ContextBuilder::classContextOpened(DUContext* context) { Q_UNUSED(context); } void ContextBuilder::visitInterfaceDeclarationStatement(InterfaceDeclarationStatementAst* node) { openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->interfaceName).second); classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration()) DefaultVisitor::visitInterfaceDeclarationStatement(node); closeContext(); } void ContextBuilder::visitTraitDeclarationStatement(TraitDeclarationStatementAst* node) { openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->traitName).second); classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration()) DefaultVisitor::visitTraitDeclarationStatement(node); closeContext(); } void ContextBuilder::visitClassStatement(ClassStatementAst *node) { visitOptionalModifiers(node->modifiers); if (node->methodName) { //method declaration DUContext* parameters = openContext(node->parameters, DUContext::Function, node->methodName); Q_ASSERT(!parameters->inSymbolTable()); visitParameterList(node->parameters); + if (node->returnType) { + visitReturnType(node->returnType); + } closeContext(); if ( !m_isInternalFunctions && node->methodBody ) { // the internal functions file has only empty method bodies, so skip them DUContext* body = openContext(node->methodBody, DUContext::Other, node->methodName); if (compilingContexts()) { DUChainWriteLocker lock(DUChain::lock()); body->addImportedParentContext(parameters); body->setInSymbolTable(false); } visitMethodBody(node->methodBody); closeContext(); } } else { //member-variable or const DefaultVisitor::visitClassStatement(node); } } void ContextBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node) { visitIdentifier(node->functionName); DUContext* parameters = openContext(node->parameters, DUContext::Function, node->functionName); Q_ASSERT(!parameters->inSymbolTable()); visitParameterList(node->parameters); + if (node->returnType) { + visitReturnType(node->returnType); + } closeContext(); if ( !m_isInternalFunctions && node->functionBody ) { // the internal functions file has only empty method bodies, so skip them DUContext* body = openContext(node->functionBody, DUContext::Other, node->functionName); if (compilingContexts()) { DUChainWriteLocker lock(DUChain::lock()); body->addImportedParentContext(parameters); body->setInSymbolTable(false); } visitInnerStatementList(node->functionBody); closeContext(); } } void ContextBuilder::visitClosure(ClosureAst* node) { DUContext* parameters = openContext(node->parameters, DUContext::Function); Q_ASSERT(!parameters->inSymbolTable()); visitParameterList(node->parameters); + if (node->returnType) { + visitReturnType(node->returnType); + } closeContext(); DUContext* imported = nullptr; if ( node->lexicalVars ) { imported = openContext(node->lexicalVars, DUContext::Other); Q_ASSERT(!imported->inSymbolTable()); visitLexicalVarList(node->lexicalVars); closeContext(); } if ( !m_isInternalFunctions && node->functionBody ) { // the internal functions file has only empty method bodies, so skip them DUContext* body = openContext(node->functionBody, DUContext::Other); if (compilingContexts()) { DUChainWriteLocker lock; body->addImportedParentContext(parameters); if (imported) { body->addImportedParentContext(imported, CursorInRevision::invalid(), true); } body->setInSymbolTable(false); } visitInnerStatementList(node->functionBody); closeContext(); } } void ContextBuilder::visitNamespaceDeclarationStatement(NamespaceDeclarationStatementAst* node) { // close existing namespace context if (m_openNamespaces) { closeNamespaces(m_openNamespaces); m_openNamespaces = nullptr; } if ( !node->namespaceNameSequence ) { if (node->body) { // global namespace DefaultVisitor::visitInnerStatementList(node->body); } return; } { // open ///TODO: support \ as separator RangeInRevision bodyRange; if (node->body) { bodyRange = editorFindRange(node->body, node->body); } else { bodyRange = RangeInRevision(m_editor->findPosition(node->endToken), currentContext()->topContext()->range().end); } const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); do { openNamespace(node, it->element, identifierPairForNode(it->element), bodyRange); } while(it->hasNext() && (it = it->next)); } if (node->body) { DefaultVisitor::visitInnerStatementList(node->body); closeNamespaces(node); } else { m_openNamespaces = node; } } void ContextBuilder::closeNamespaces(NamespaceDeclarationStatementAst* namespaces) { ///TODO: support \ as separator const KDevPG::ListNode< IdentifierAst* >* it = namespaces->namespaceNameSequence->front(); do { Q_ASSERT(currentContext()->type() == DUContext::Namespace); closeNamespace(namespaces, it->element, identifierPairForNode(it->element)); } while(it->hasNext() && (it = it->next)); } void ContextBuilder::openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const RangeInRevision& range) { if ( node == parent->namespaceNameSequence->back()->element ) { openContext(node, range, DUContext::Namespace, identifier.second); } else { openContext(node, range, DUContext::Namespace, identifier.second); } } void ContextBuilder::closeNamespace(NamespaceDeclarationStatementAst* /*parent*/, IdentifierAst* /*node*/, const IdentifierPair& /*identifier*/) { closeContext(); } void ContextBuilder::addBaseType(NamespacedIdentifierAst * identifier) { DUChainWriteLocker lock(DUChain::lock()); Q_ASSERT(currentContext()->type() == DUContext::Class); ClassDeclaration* currentClass = dynamic_cast(currentContext()->owner()); ClassDeclaration* baseClass = dynamic_cast( findDeclarationImport(ClassDeclarationType, identifierForNamespace(identifier, m_editor)).data() ); if (currentClass && baseClass) { if (DUContext* baseContext = baseClass->logicalInternalContext(nullptr)) { // prevent circular context imports which could lead to segfaults if (!baseContext->imports(currentContext()) && !currentContext()->imports(baseContext)) { currentContext()->addImportedParentContext(baseContext); BaseClassInstance base; base.baseClass = baseClass->indexedType(); base.access = Declaration::Public; base.virtualInheritance = false; currentClass->addBaseClass(base); } else if (m_reportErrors) { reportError(i18n("Circular inheritance of %1 and %2", currentClass->toString(), baseClass->toString()), identifier); } } } if (!baseClass) { qCDebug(DUCHAIN) << "unresolved identifier"; m_hadUnresolvedIdentifiers = true; } } void ContextBuilder::visitUnaryExpression(UnaryExpressionAst* node) { DefaultVisitor::visitUnaryExpression(node); if (!compilingContexts()) { return; } IndexedString includeFile = getIncludeFileForNode(node, m_editor); if ( !includeFile.isEmpty() ) { DUChainWriteLocker lock(DUChain::lock()); TopDUContext *top = DUChain::self()->chainForDocument(includeFile); if (top) { currentContext()->topContext()->addImportedParentContext(top); currentContext()->topContext()->parsingEnvironmentFile() ->addModificationRevisions(top->parsingEnvironmentFile()->allModificationRevisions()); } } } void ContextBuilder::reportError(const QString& errorMsg, AstNode* node, IProblem::Severity severity) { reportError(errorMsg, m_editor->findRange(node), severity); } void ContextBuilder::reportError(const QString& errorMsg, QList< AstNode* > nodes, IProblem::Severity severity) { RangeInRevision range = RangeInRevision::invalid(); foreach ( AstNode* node, nodes ) { if ( !range.isValid() ) { range = m_editor->findRange(node); } else { range.end = m_editor->findPosition(node->endToken); } } reportError(errorMsg, range, severity); } void ContextBuilder::reportError(const QString& errorMsg, RangeInRevision range, IProblem::Severity severity) { Problem *p = new Problem(); p->setSeverity(severity); p->setSource(IProblem::DUChainBuilder); p->setDescription(errorMsg); p->setFinalLocation(DocumentRange(m_editor->parseSession()->currentDocument(), range.castToSimpleRange())); { DUChainWriteLocker lock(DUChain::lock()); qCDebug(DUCHAIN) << "Problem" << p->description() << p->finalLocation(); currentContext()->topContext()->addProblem(ProblemPointer(p)); } } DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType, IdentifierAst* node) { QualifiedIdentifier id; if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) { id = identifierPairForNode(node).second; } else { id = identifierForNode(node); } return findDeclarationImportHelper(currentContext(), id, declarationType); } DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType, VariableIdentifierAst* node) { return findDeclarationImportHelper(currentContext(), identifierForNode(node), declarationType); } DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType, const QualifiedIdentifier &identifier) { return findDeclarationImportHelper(currentContext(), identifier, declarationType); } } diff --git a/duchain/builders/usebuilder.cpp b/duchain/builders/usebuilder.cpp index 836cd50..044da88 100644 --- a/duchain/builders/usebuilder.cpp +++ b/duchain/builders/usebuilder.cpp @@ -1,275 +1,281 @@ /*************************************************************************** * 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); } } 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/builders/usebuilder.h b/duchain/builders/usebuilder.h index 60ca1dd..ef73779 100644 --- a/duchain/builders/usebuilder.h +++ b/duchain/builders/usebuilder.h @@ -1,81 +1,82 @@ /*************************************************************************** * 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. * ***************************************************************************/ #ifndef USEBUILDER_H #define USEBUILDER_H #include #include "helper.h" #include "contextbuilder.h" #include "phpduchainexport.h" namespace Php { class ParseSession; typedef KDevelop::AbstractUseBuilder UseBuilderBase; /** * A class which iterates the AST to extract uses of definitions. */ class KDEVPHPDUCHAIN_EXPORT UseBuilder: public UseBuilderBase { public: UseBuilder(EditorIntegrator* editor); /** * Reports a problem if the use'd declaration is deprecated. * Also reports an error if @p reportNotFound is true and @p declaration is null. */ void newCheckedUse(Php::AstNode* node, const KDevelop::DeclarationPointer& declaration, bool reportNotFound = false); KDevelop::ReferencedTopDUContext build(const KDevelop::IndexedString& url, AstNode* node, KDevelop::ReferencedTopDUContext updateContext = KDevelop::ReferencedTopDUContext()) override; protected: void visitParameter(ParameterAst *node) override; void visitClassImplements(ClassImplementsAst *node) override; void visitClassExtends(ClassExtendsAst *node) override; void visitClassStatement(ClassStatementAst *node) override; void visitTraitAliasStatement(TraitAliasStatementAst *node) override; void visitTraitAliasIdentifier(TraitAliasIdentifierAst *node) override; void visitExpr(ExprAst* node) override; void visitGlobalVar(GlobalVarAst* node) override; void visitStaticScalar(StaticScalarAst* node) override; void visitStatement(StatementAst* node) override; void visitCatchItem(CatchItemAst* node) override; void visitUnaryExpression( UnaryExpressionAst* node ) override; void visitUseNamespace(UseNamespaceAst* node) override; void openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const KDevelop::RangeInRevision& range) override; + void visitReturnType(ReturnTypeAst* node) override; private: void buildNamespaceUses(Php::NamespacedIdentifierAst* node, Php::DeclarationType lastType = Php::ClassDeclarationType); void visitNodeWithExprVisitor(AstNode* node); }; } #endif // USEBUILDER_H diff --git a/duchain/tests/uses.cpp b/duchain/tests/uses.cpp index 5735476..0615882 100644 --- a/duchain/tests/uses.cpp +++ b/duchain/tests/uses.cpp @@ -1,1225 +1,1247 @@ /* 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::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)); +} + } diff --git a/duchain/tests/uses.h b/duchain/tests/uses.h index 7276a0e..ae52d4c 100644 --- a/duchain/tests/uses.h +++ b/duchain/tests/uses.h @@ -1,92 +1,94 @@ /* 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 instanceof(); void classNameString(); void useTrait(); void exceptionFinally(); + void returnTypeClassFunction(); + void returnTypeFunction(); }; } #endif