diff --git a/duchain/expressionvisitor.h b/duchain/expressionvisitor.h --- a/duchain/expressionvisitor.h +++ b/duchain/expressionvisitor.h @@ -67,6 +67,7 @@ void visitEncapsVar(EncapsVarAst *node) override; void visitVariableProperty(VariablePropertyAst *node) override; void visitStaticMember(StaticMemberAst* node) override; + void visitClassNameReference(ClassNameReferenceAst* node) override; void visitUnaryExpression(UnaryExpressionAst* node) override; void visitAdditiveExpressionRest(AdditiveExpressionRestAst* node) override; void visitVariable(VariableAst* node) override; diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp --- a/duchain/expressionvisitor.cpp +++ b/duchain/expressionvisitor.cpp @@ -209,16 +209,16 @@ void ExpressionVisitor::visitVarExpressionNewObject(VarExpressionNewObjectAst *node) { DefaultVisitor::visitVarExpressionNewObject(node); - if (node->className->staticIdentifier != -1) { + if (node->classNameReference->className && node->classNameReference->className->staticIdentifier != -1) { static const QualifiedIdentifier id(QStringLiteral("static")); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); - usingDeclaration(node->className, dec); + usingDeclaration(node->classNameReference->className, dec); m_result.setDeclaration(dec); - } else if (node->className->identifier) { - const QualifiedIdentifier id = identifierForNamespace(node->className->identifier, m_editor); + } else if (node->classNameReference->className && node->classNameReference->className->identifier) { + const QualifiedIdentifier id = identifierForNamespace(node->classNameReference->className->identifier, m_editor); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); - usingDeclaration(node->className->identifier->namespaceNameSequence->back()->element, dec); - buildNamespaceUses(node->className->identifier, id); + usingDeclaration(node->classNameReference->className->identifier->namespaceNameSequence->back()->element, dec); + buildNamespaceUses(node->classNameReference->className->identifier, id); m_result.setDeclaration(dec); } } @@ -681,30 +681,143 @@ { //don't call DefaultVisitor::visitStaticMember(node); //because we would end up in visitCompoundVariableWithSimpleIndirectReference - if (node->variable->variable->variable) { + if (node->staticProperty->staticProperty->variable->variable) { DUContext* context = findClassContext(node->className); if (context) { DUChainReadLocker lock(DUChain::lock()); - m_result.setDeclarations(context->findDeclarations(identifierForNode(node->variable->variable->variable))); + m_result.setDeclarations(context->findDeclarations(identifierForNode(node->staticProperty->staticProperty->variable))); lock.unlock(); if (!m_result.allDeclarations().isEmpty()) { - usingDeclaration(node->variable->variable->variable, m_result.allDeclarations().last()); + usingDeclaration(node->staticProperty->staticProperty->variable, m_result.allDeclarations().last()); } else { - usingDeclaration(node->variable->variable->variable, DeclarationPointer()); + usingDeclaration(node->staticProperty->staticProperty->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(); + if (node->staticProperty->offsetItemsSequence) { + const KDevPG::ListNode< DimListItemAst* >* it = node->staticProperty->offsetItemsSequence->front(); do { visitDimListItem(it->element); } while(it->hasNext() && (it = it->next)); } } } +void ExpressionVisitor::visitClassNameReference(ClassNameReferenceAst* node) +{ + if (node->staticProperty) { + DUContext* context = findClassContext(node->className->identifier); + + if (context) { + DUChainReadLocker lock(DUChain::lock()); + m_result.setDeclarations(context->findDeclarations(identifierForNode(node->staticProperty->staticProperty->variable))); + lock.unlock(); + if (!m_result.allDeclarations().isEmpty()) { + usingDeclaration(node->staticProperty->staticProperty->variable, m_result.allDeclarations().last()); + } else { + usingDeclaration(node->staticProperty->staticProperty->variable, DeclarationPointer()); + } + } else { + m_result.setType(AbstractType::Ptr()); + } + + if (node->staticProperty->offsetItemsSequence) { + const KDevPG::ListNode< DimListItemAst* >* dim_it = node->staticProperty->offsetItemsSequence->front(); + do { + visitDimListItem(dim_it->element); + } while(dim_it->hasNext() && (dim_it = dim_it->next)); + } + } + + if (node->baseVariable) { + DefaultVisitor::visitVariableWithoutObjects(node->baseVariable); + } + + if (node->propertiesSequence) { + if (!m_result.allDeclarations().isEmpty()) { + DUContext* context = nullptr; + StructureType::Ptr type; + + Declaration *declaration = nullptr; + const KDevPG::ListNode< ClassPropertyAst* >* it = node->propertiesSequence->front(); + DUChainReadLocker lock; + + do { + if (it->element->property && it->element->property->variableWithoutObjects + && it->element->property->variableWithoutObjects->variable->variable) { + VariableIdentifierAst *varnode = it->element->property->variableWithoutObjects->variable->variable; + lock.lock(); + m_result.setDeclarations(m_currentContext->findDeclarations(identifierForNode(varnode))); + lock.unlock(); + if (!m_result.allDeclarations().isEmpty()) { + usingDeclaration(varnode, m_result.allDeclarations().last()); + } else { + usingDeclaration(varnode, DeclarationPointer()); + } + } else if (!m_result.allDeclarations().isEmpty()) { + type = m_result.allDeclarations().last()->type(); + + if (type) { + lock.lock(); + declaration = type->declaration(m_currentContext->topContext()); + lock.unlock(); + + if (declaration) { + context = declaration->internalContext(); + + if (context && context->type() == DUContext::Class) { + if (it->element->staticProperty) { + VariableIdentifierAst *varnode = it->element->staticProperty->staticProperty->variable; + lock.lock(); + m_result.setDeclarations(context->findDeclarations(identifierForNode(varnode))); + lock.unlock(); + if (!m_result.allDeclarations().isEmpty()) { + usingDeclaration(varnode, m_result.allDeclarations().last()); + } else { + usingDeclaration(varnode, DeclarationPointer()); + } + } else if (it->element->property && it->element->property->objectDimList + && it->element->property->objectDimList->variableName->name) { + IdentifierAst *varidnode = it->element->property->objectDimList->variableName->name; + lock.lock(); + m_result.setDeclarations(context->findDeclarations(identifierForNode(varidnode))); + lock.unlock(); + if (!m_result.allDeclarations().isEmpty()) { + usingDeclaration(varidnode, m_result.allDeclarations().last()); + } else { + usingDeclaration(varidnode, DeclarationPointer()); + } + } else { + context = nullptr; + } + } + } else { + context = nullptr; + } + } else { + context = nullptr; + } + + if (it->element->property && it->element->property->objectDimList && it->element->property->objectDimList->offsetItemsSequence) { + const KDevPG::ListNode< DimListItemAst* >* dim_it = it->element->property->objectDimList->offsetItemsSequence->front(); + do { + visitDimListItem(dim_it->element); + } while(dim_it->hasNext() && (dim_it = dim_it->next)); + } else if (it->element->staticProperty && it->element->staticProperty->offsetItemsSequence) { + const KDevPG::ListNode< DimListItemAst* >* dim_it = it->element->staticProperty->offsetItemsSequence->front(); + do { + visitDimListItem(dim_it->element); + } while(dim_it->hasNext() && (dim_it = dim_it->next)); + } + } + } while(it->hasNext() && (it = it->next)); + } + } + +} + void ExpressionVisitor::visitUnaryExpression(UnaryExpressionAst* node) { DefaultVisitor::visitUnaryExpression(node); @@ -756,11 +869,11 @@ void ExpressionVisitor::visitRelationalExpression(RelationalExpressionAst *node) { DefaultVisitor::visitRelationalExpression(node); - if (node->instanceofType && node->instanceofType->identifier) { - const QualifiedIdentifier id = identifierForNamespace(node->instanceofType->identifier, m_editor); + if (node->instanceofType && node->instanceofType->className && node->instanceofType->className->identifier) { + const QualifiedIdentifier id = identifierForNamespace(node->instanceofType->className->identifier, m_editor); DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, id); - usingDeclaration(node->instanceofType->identifier->namespaceNameSequence->back()->element, dec); - buildNamespaceUses(node->instanceofType->identifier, id); + usingDeclaration(node->instanceofType->className->identifier->namespaceNameSequence->back()->element, dec); + buildNamespaceUses(node->instanceofType->className->identifier, id); m_result.setType(AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean))); } diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp --- a/duchain/tests/duchain.cpp +++ b/duchain/tests/duchain.cpp @@ -3856,6 +3856,10 @@ QTest::newRow("double print expression") << QStringLiteral("foo;\n"); + QTest::newRow("instanceof with class constant") << QStringLiteral("foo();\n"); + QTest::newRow("instanceof with static method") << QStringLiteral(" #include +#include #include "../declarations/classdeclaration.h" #include "../declarations/variabledeclaration.h" #include "../declarations/traitmethodaliasdeclaration.h" #include "../declarations/traitmemberaliasdeclaration.h" +#include "../types/structuretype.h" + using namespace KDevelop; QTEST_MAIN(Php::TestUses) @@ -1125,7 +1128,236 @@ compareUses(a, QList() << RangeInRevision(1, 9, 1, 10) << RangeInRevision(2, 19, 2, 20)); +} + +void TestUses::instanceofClassProperty() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("foo->bar->foo;\n", DumpNone); + + QVERIFY(top); + DUChainReleaser releaseTop(top); + DUChainWriteLocker lock; + + QVERIFY(top->problems().isEmpty()); + + QVERIFY(!top->parentContext()); + QCOMPARE(top->childContexts().count(), 2); + QCOMPARE(top->localDeclarations().count(), 3); + + Declaration* dec = top->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(2, 9, 2, 10)); + + dec = top->localDeclarations().at(2); + QCOMPARE(dec->identifier(), Identifier("a")); + StructureType::Ptr classType = dec->type(); + QVERIFY(classType); + QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier("a")); + QVERIFY(classType->equals(dec->abstractType().data())); + compareUses(dec, QList() + << RangeInRevision(2, 14, 2, 16) + << RangeInRevision(2, 28, 2, 30)); + + dec = top->childContexts().at(0)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("foo")); + compareUses(dec, QList() + << RangeInRevision(2, 32, 2, 35) + << RangeInRevision(2, 42, 2, 45)); + + dec = top->childContexts().at(1)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(2, 37, 2, 40)); +} + +void TestUses::instanceofStaticProperty() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("problems().isEmpty()); + + QVERIFY(!top->parentContext()); + QCOMPARE(top->childContexts().count(), 2); + QCOMPARE(top->localDeclarations().count(), 3); + + Declaration* dec = top->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(2, 9, 2, 10) + << RangeInRevision(2, 28, 2, 29)); + + dec = top->localDeclarations().at(2); + QCOMPARE(dec->identifier(), Identifier("a")); + StructureType::Ptr classType = dec->type(); + QVERIFY(classType); + QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier("a")); + QVERIFY(classType->equals(dec->abstractType().data())); + compareUses(dec, QList() + << RangeInRevision(2, 14, 2, 16)); + + dec = top->childContexts().at(0)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("foo")); + compareUses(dec, QList() + << RangeInRevision(2, 31, 2, 35) + << RangeInRevision(2, 43, 2, 47)); + + dec = top->childContexts().at(1)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(2, 37, 2, 41)); +} + +void TestUses::instanceofMixedProperty() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("bar::$foo;\n", DumpNone); + + QVERIFY(top); + DUChainReleaser releaseTop(top); + DUChainWriteLocker lock; + + QVERIFY(top->problems().isEmpty()); + + QVERIFY(!top->parentContext()); + QCOMPARE(top->childContexts().count(), 2); + QCOMPARE(top->localDeclarations().count(), 3); + + Declaration* dec = top->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(2, 9, 2, 10) + << RangeInRevision(2, 28, 2, 29)); + + dec = top->localDeclarations().at(2); + QCOMPARE(dec->identifier(), Identifier("a")); + StructureType::Ptr classType = dec->type(); + QVERIFY(classType); + QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier("a")); + QVERIFY(classType->equals(dec->abstractType().data())); + compareUses(dec, QList() + << RangeInRevision(2, 14, 2, 16)); + + dec = top->childContexts().at(0)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("foo")); + compareUses(dec, QList() + << RangeInRevision(2, 31, 2, 35) + << RangeInRevision(2, 42, 2, 46)); + + dec = top->childContexts().at(1)->localDeclarations().at(0); + QVERIFY(dec); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(2, 37, 2, 40)); +} + +void TestUses::instanceofVariableProperty() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("$foo->$bar->$foo;\n", DumpNone); + + QVERIFY(top); + DUChainReleaser releaseTop(top); + DUChainWriteLocker lock; + + QVERIFY(top->problems().isEmpty()); + + QVERIFY(!top->parentContext()); + QCOMPARE(top->childContexts().count(), 2); + QCOMPARE(top->localDeclarations().count(), 5); + + Declaration* dec = top->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(3, 9, 3, 10)); + + dec = top->localDeclarations().at(4); + QCOMPARE(dec->identifier(), Identifier("a")); + StructureType::Ptr classType = dec->type(); + QVERIFY(classType); + QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier("a")); + QVERIFY(classType->equals(dec->abstractType().data())); + compareUses(dec, QList() + << RangeInRevision(3, 14, 3, 16) + << RangeInRevision(3, 28, 3, 30)); + + dec = top->localDeclarations().at(2); + QCOMPARE(dec->identifier(), Identifier("foo")); + compareUses(dec, QList() + << RangeInRevision(3, 32, 3, 36) + << RangeInRevision(3, 44, 3, 48)); + + dec = top->localDeclarations().at(3); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(3, 38, 3, 42)); +} + +void TestUses::instanceofPropertyArrayAccess() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("foo[0]::$bar[0]->foo;\n", DumpNone); + + QVERIFY(top); + DUChainReleaser releaseTop(top); + DUChainWriteLocker lock; + + QVERIFY(top->problems().isEmpty()); + + QVERIFY(!top->parentContext()); + QCOMPARE(top->childContexts().count(), 2); + QCOMPARE(top->localDeclarations().count(), 3); + + Declaration* dec = top->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(2, 9, 2, 10)); + + dec = top->localDeclarations().at(2); + QCOMPARE(dec->identifier(), Identifier("a")); + StructureType::Ptr classType = dec->type(); + QVERIFY(classType); + QCOMPARE(classType->qualifiedIdentifier(), QualifiedIdentifier("a")); + QVERIFY(classType->equals(dec->abstractType().data())); + compareUses(dec, QList() + << RangeInRevision(2, 14, 2, 16) + << RangeInRevision(2, 28, 2, 30)); + + dec = top->childContexts().at(0)->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("foo")); + compareUses(dec, QList() + << RangeInRevision(2, 32, 2, 35)); + dec = top->childContexts().at(1)->localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("bar")); + QVERIFY(dec->uses().isEmpty()); } void TestUses::classNameString() diff --git a/parser/php.g b/parser/php.g --- a/parser/php.g +++ b/parser/php.g @@ -603,7 +603,7 @@ (isRef=BIT_AND | 0) variable=variableIdentifier -> lexicalVar ;; - NEW className=classNameReference ctor=ctorArguments + NEW classNameReference=classNameReference ctor=ctorArguments -> varExpressionNewObject ;; LPAREN parameterList=functionCallParameterList RPAREN @@ -681,7 +681,7 @@ ( DOLLAR ( DOLLAR+ | 0 ) ( indirectVariable=variableIdentifier | LBRACE expr=expr RBRACE ) | variable=variableIdentifier ) -> compoundVariableWithSimpleIndirectReference ;; - className=namespacedIdentifier PAAMAYIM_NEKUDOTAYIM variable=variableWithoutObjects + className=namespacedIdentifier staticProperty=staticProperty -> staticMember ;; LBRACE try/recover(statements=innerStatementList) RBRACE @@ -806,22 +806,19 @@ ELSE COLON statements=innerStatementList | 0 -> newElseSingle ;; ---TODO --resolve STRING vs. staticMember conflict --- ?[: LA(2).kind != Token_PAAMAYIM_NEKUDOTAYIM :] - identifier=namespacedIdentifier - | staticIdentifier = STATIC - | dynamicClassNameReference=dynamicClassNameReference + className=className (staticProperty=staticProperty #properties=classProperty* | 0) + | baseVariable=variableWithoutObjects #properties=classProperty* -> classNameReference ;; - baseVariable=baseVariable (OBJECT_OPERATOR objectProperty=objectProperty - properties=dynamicClassNameVariableProperties | 0) --> dynamicClassNameReference ;; + identifier=namespacedIdentifier + | staticIdentifier = STATIC +-> className ;; - #properties=dynamicClassNameVariableProperty* --> dynamicClassNameVariableProperties ;; + PAAMAYIM_NEKUDOTAYIM staticProperty=compoundVariableWithSimpleIndirectReference #offsetItems=dimListItem* +-> staticProperty ;; - OBJECT_OPERATOR property=objectProperty --> dynamicClassNameVariableProperty ;; + (staticProperty=staticProperty | OBJECT_OPERATOR property=objectProperty) +-> classProperty ;; objectDimList=objectDimList | variableWithoutObjects=variableWithoutObjects