diff --git a/duchain/builders/contextbuilder.h b/duchain/builders/contextbuilder.h --- a/duchain/builders/contextbuilder.h +++ b/duchain/builders/contextbuilder.h @@ -108,7 +108,7 @@ KDevelop::IProblem::Severity severity = KDevelop::IProblem::Error); KDevelop::DeclarationPointer findDeclarationImport(DeclarationType declarationType, IdentifierAst* node); - KDevelop::DeclarationPointer findDeclarationImport(DeclarationType declarationType, SemiReservedIdentifierAst* node); + KDevelop::DeclarationPointer findDeclarationImport(DeclarationType declarationType, SemiReservedIdentifierAst* node, DeclarationScope declarationScope = LocalScope); KDevelop::DeclarationPointer findDeclarationImport(DeclarationType declarationType, VariableIdentifierAst* node); KDevelop::DeclarationPointer findDeclarationImport(DeclarationType declarationType, const KDevelop::QualifiedIdentifier &identifier); diff --git a/duchain/builders/contextbuilder.cpp b/duchain/builders/contextbuilder.cpp --- a/duchain/builders/contextbuilder.cpp +++ b/duchain/builders/contextbuilder.cpp @@ -533,14 +533,20 @@ } DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType, - SemiReservedIdentifierAst* node) + SemiReservedIdentifierAst* node, + DeclarationScope declarationScope) { QualifiedIdentifier id; if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) { id = identifierPairForNode(node).second; } else { id = identifierForNode(node); } + + if (declarationScope == GlobalScope) { + id.setExplicitlyGlobal(true); + } + return findDeclarationImportHelper(currentContext(), id, declarationType); } diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -1241,9 +1241,18 @@ DeclarationPointer dec; if ( node->stringFunctionName ) { dec = findDeclarationImport(FunctionDeclarationType, node->stringFunctionName); + + if (!dec) { + dec = findDeclarationImport(FunctionDeclarationType, node->stringFunctionName, GlobalScope); + } } else if ( node->stringFunctionNameOrClass ) { id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); dec = findDeclarationImport(FunctionDeclarationType, id); + + if (!dec) { + id.setExplicitlyGlobal(true); + dec = findDeclarationImport(FunctionDeclarationType, id); + } } else { ///TODO: node->varFunctionName } @@ -1539,11 +1548,15 @@ ///TODO: case insensitive! QualifiedIdentifier qid = identifierForNamespace(node->identifier, m_editor); - ///TODO: find out why this must be done (see mail to kdevelop-devel on jan 18th 2011) - qid.setExplicitlyGlobal( false ); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, qid); + if (!dec && !qid.explicitlyGlobal()) { + QualifiedIdentifier globalQid = qid; + globalQid.setExplicitlyGlobal(true); + dec = findDeclarationImport(ClassDeclarationType, globalQid); + } + if (dec) { // Check for a name conflict @@ -1564,6 +1577,9 @@ } else { + // NamespaceAliasDeclarations can't use a global import identifier + qid.setExplicitlyGlobal(false); + NamespaceAliasDeclaration* decl = openDefinition(id.second, m_editor->findRange(idNode)); decl->setImportIdentifier( qid ); diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp --- a/duchain/builders/typebuilder.cpp +++ b/duchain/builders/typebuilder.cpp @@ -537,7 +537,8 @@ } if (classDec) { /// Qualified identifier for 'iterator' - static const QualifiedIdentifier iteratorQId(QStringLiteral("iterator")); + static QualifiedIdentifier iteratorQId(QStringLiteral("iterator")); + iteratorQId.setExplicitlyGlobal(true); ClassDeclaration* iteratorDecl = dynamic_cast( findDeclarationImport(ClassDeclarationType, iteratorQId).data() ); @@ -580,7 +581,9 @@ { if (hasCurrentContextType() && node->isGenerator != -1 && !m_gotReturnTypeFromDocComment) { FunctionType::Ptr ft = FunctionType::Ptr::dynamicCast(currentContextType()); - DeclarationPointer generatorDecl = findDeclarationImport(ClassDeclarationType, QualifiedIdentifier("generator")); + static QualifiedIdentifier generatorQId(QStringLiteral("generator")); + generatorQId.setExplicitlyGlobal(true); + DeclarationPointer generatorDecl = findDeclarationImport(ClassDeclarationType, generatorQId); if (ft && generatorDecl) { AbstractType::Ptr generatorType = generatorDecl->abstractType(); diff --git a/duchain/builders/usebuilder.cpp b/duchain/builders/usebuilder.cpp --- a/duchain/builders/usebuilder.cpp +++ b/duchain/builders/usebuilder.cpp @@ -234,8 +234,32 @@ void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationType lastType) { - const QualifiedIdentifier identifier = identifierForNamespace(node, m_editor); + QualifiedIdentifier identifier = identifierForNamespace(node, m_editor); + QualifiedIdentifier curId; + + // check if we need to resolve the namespaced identifier globally or locally + DeclarationPointer tempDec = findDeclarationImport(ClassDeclarationType, identifier); + + // if we couldn't find a class declaration, it might be a partial namespace identifier + if (!tempDec) { + tempDec = findDeclarationImport(NamespaceDeclarationType, identifier); + } + + if (!tempDec && !identifier.explicitlyGlobal()) { + identifier.setExplicitlyGlobal(true); + tempDec = findDeclarationImport(ClassDeclarationType, identifier); + + if (!tempDec) { + tempDec = findDeclarationImport(NamespaceDeclarationType, identifier); + } + + // Can't resolve either globally or locally, so revert back to original + if (!tempDec) { + identifier.setExplicitlyGlobal(false); + } + } + curId.setExplicitlyGlobal(identifier.explicitlyGlobal()); Q_ASSERT(identifier.count() == node->namespaceNameSequence->count()); for ( int i = 0; i < identifier.count() - 1; ++i ) { diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp --- a/duchain/expressionvisitor.cpp +++ b/duchain/expressionvisitor.cpp @@ -371,8 +371,12 @@ m_result.setDeclaration(dec); } else { //global function call foo(); - const QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); + QualifiedIdentifier id = identifierForNamespace(node->stringFunctionNameOrClass, m_editor); DeclarationPointer dec = findDeclarationImport(FunctionDeclarationType, id); + if (!dec) { + id.setExplicitlyGlobal(true); + 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); @@ -457,6 +461,10 @@ //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) { + id.setExplicitlyGlobal(true); + declaration = findDeclarationImport(ConstantDeclarationType, id); + } if (!declaration) { ///TODO: is this really wanted? //it could also be a global function call, without () diff --git a/duchain/helper.h b/duchain/helper.h --- a/duchain/helper.h +++ b/duchain/helper.h @@ -51,6 +51,11 @@ NamespaceDeclarationType }; +enum DeclarationScope { + GlobalScope, + LocalScope +}; + KDEVPHPDUCHAIN_EXPORT bool isMatch(KDevelop::Declaration* declaration, DeclarationType declarationType); KDEVPHPDUCHAIN_EXPORT bool isClassTypehint(GenericTypeHintAst* parameterType, EditorIntegrator *editor); diff --git a/duchain/helper.cpp b/duchain/helper.cpp --- a/duchain/helper.cpp +++ b/duchain/helper.cpp @@ -129,7 +129,19 @@ /// Qualified identifier for 'static' static const QualifiedIdentifier staticQId(QStringLiteral("static")); - ifDebug(qCDebug(DUCHAIN) << id.toString() << declarationType;) + QualifiedIdentifier lookup; + + if (id.explicitlyGlobal()) { + ifDebug(qCDebug(DUCHAIN) << id.toString() << declarationType;) + + lookup = id; + lookup.setExplicitlyGlobal(false); + } else { + lookup = identifierWithNamespace(id, currentContext); + + ifDebug(qCDebug(DUCHAIN) << lookup.toString() << declarationType;) + } + if (declarationType == ClassDeclarationType && id == selfQId) { DUChainReadLocker lock(DUChain::lock()); if (currentContext->type() == DUContext::Class) { @@ -168,17 +180,7 @@ return DeclarationPointer(); } else { DUChainReadLocker lock; - QList foundDeclarations = currentContext->topContext()->findDeclarations(id); - if (foundDeclarations.isEmpty()) { - // If it's not in the top context, try the current context (namespaces...) - // this fixes the bug: https://bugs.kde.org/show_bug.cgi?id=322274 - foundDeclarations = currentContext->findDeclarations(id); - } - if (foundDeclarations.isEmpty()) { - // If it is neither in the top not the current context it might be defined in a different context - // Look up with fully qualified identifier - foundDeclarations = currentContext->topContext()->findDeclarations(identifierWithNamespace(id, currentContext)); - } + QList foundDeclarations = currentContext->topContext()->findDeclarations(lookup); foreach(Declaration *declaration, foundDeclarations) { if (isMatch(declaration, declarationType)) { @@ -196,12 +198,7 @@ ifDebug(qCDebug(DUCHAIN) << "No declarations found with findDeclarations, trying through PersistentSymbolTable";) DeclarationPointer decl; - decl = findDeclarationInPST(currentContext, id, declarationType); - - if (!decl) - { - decl = findDeclarationInPST(currentContext, identifierWithNamespace(id, currentContext), declarationType); - } + decl = findDeclarationInPST(currentContext, lookup, declarationType); if (decl) { ifDebug(qCDebug(DUCHAIN) << "PST declaration exists";) diff --git a/duchain/tests/duchain_multiplefiles.h b/duchain/tests/duchain_multiplefiles.h --- a/duchain/tests/duchain_multiplefiles.h +++ b/duchain/tests/duchain_multiplefiles.h @@ -46,6 +46,7 @@ void testUpdateForeach(); void testTodoExtractorReparse(); void testIteratorForeachReparse(); + void testNamespacedIdentifierInPST(); private: KDevelop::TestProjectController* m_projectController; }; diff --git a/duchain/tests/duchain_multiplefiles.cpp b/duchain/tests/duchain_multiplefiles.cpp --- a/duchain/tests/duchain_multiplefiles.cpp +++ b/duchain/tests/duchain_multiplefiles.cpp @@ -318,3 +318,40 @@ QVERIFY(type->dataType() == IntegralType::TypeMixed); } } + +void TestDUChainMultipleFiles::testNamespacedIdentifierInPST() { + auto features = TopDUContext::AllDeclarationsAndContexts; + + TestProject* project = new TestProject; + m_projectController->closeAllProjects(); + m_projectController->addProject(project); + + TestFile f1(QStringLiteral("class_a = new \\Test\\A(); }}"), QStringLiteral("php"), project); + f2.parse(features); + QVERIFY(f2.waitForParsed()); + + TestFile f3(QStringLiteral("class_a = new Test\\A(); }}"), QStringLiteral("php"), project); + f3.parse(features); + QVERIFY(f3.waitForParsed()); + + DUChainWriteLocker lock(DUChain::lock()); + QVERIFY(f1.topContext()); + QVERIFY(f2.topContext()); + QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0))); + QVERIFY(f3.topContext()); + QVERIFY(!f3.topContext()->imports(f1.topContext(), CursorInRevision(0, 0))); +}