diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp --- a/duchain/expressionvisitor.cpp +++ b/duchain/expressionvisitor.cpp @@ -697,33 +697,52 @@ { //don't call DefaultVisitor::visitStaticMember(node); //because we would end up in visitCompoundVariableWithSimpleIndirectReference - if (node->staticProperty->staticProperty->variable->variable) { - DUContext* context = findClassContext(node->className); - if (context) { - useDeclaration(node->staticProperty->staticProperty->variable, context); - } else { - usingDeclaration(node->className, DeclarationPointer()); + if (node->staticProperty && node->staticProperty->staticProperty) { + if (node->staticProperty->staticProperty->variable) { + DUContext* context = findClassContext(node->className); + if (context) { + useDeclaration(node->staticProperty->staticProperty->variable, context); + } else { + usingDeclaration(node->className, DeclarationPointer()); + m_result.setType(AbstractType::Ptr()); + } + } else if (node->staticProperty->staticProperty->expr) { + const QualifiedIdentifier id = identifierForNamespace(node->className, m_editor); + DeclarationPointer declaration = findDeclarationImport(ClassDeclarationType, id); + usingDeclaration(node->className->namespaceNameSequence->back()->element, declaration); + buildNamespaceUses(node->className, id); + + visitExpr(node->staticProperty->staticProperty->expr); + m_result.setType(AbstractType::Ptr()); } - if (node->staticProperty->offsetItemsSequence) { - const KDevPG::ListNode< DimListItemAst* >* it = node->staticProperty->offsetItemsSequence->front(); - do { - visitDimListItem(it->element); - } while(it->hasNext() && (it = it->next)); - } + } + + if (node->staticProperty && 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) { - useDeclaration(node->staticProperty->staticProperty->variable, context); + if (context && node->staticProperty && node->staticProperty->staticProperty) { + if (node->staticProperty->staticProperty->variable) { + // static properties (object::$property) + useDeclaration(node->staticProperty->staticProperty->variable, context); + } else if (node->staticProperty->staticProperty->expr) { + // variable static properties (object::${$property}) + visitExpr(node->staticProperty->staticProperty->expr); + usingDeclaration(node->className, DeclarationPointer()); + } } - if (node->staticProperty->offsetItemsSequence) { + if (node->staticProperty && node->staticProperty->offsetItemsSequence) { const KDevPG::ListNode< DimListItemAst* >* dim_it = node->staticProperty->offsetItemsSequence->front(); do { visitDimListItem(dim_it->element); @@ -749,6 +768,10 @@ && it->element->property->variableWithoutObjects->variable->variable) { VariableIdentifierAst *varnode = it->element->property->variableWithoutObjects->variable->variable; useDeclaration(varnode, m_currentContext); + } else if (it->element->property && it->element->property->variableWithoutObjects + && it->element->property->variableWithoutObjects->variable->expr) { + // variable dynamic properties ($object->${$property}) + visitExpr(it->element->property->variableWithoutObjects->variable->expr); } else if (!m_result.allDeclarations().isEmpty()) { // handle array indices after normal/static properties ($object->property[$index] // $object::$property[$index]) if (it->element->property && it->element->property->objectDimList && it->element->property->objectDimList->offsetItemsSequence) { @@ -763,6 +786,14 @@ } while(dim_it->hasNext() && (dim_it = dim_it->next)); } + // Handle dynamic static properties first, as they don't need a class context + if (it->element->staticProperty && it->element->staticProperty->staticProperty + && it->element->staticProperty->staticProperty->expr) { + // variable static properties ($object::${$property}) + visitExpr(it->element->staticProperty->staticProperty->expr); + usingDeclaration(it->element->staticProperty, DeclarationPointer()); + } + type = m_result.allDeclarations().last()->type(); if (!type) { @@ -786,7 +817,8 @@ continue; } - if (it->element->staticProperty) { + if (it->element->staticProperty && it->element->staticProperty->staticProperty + && it->element->staticProperty->staticProperty->variable) { // static properties ($object::$property) VariableIdentifierAst *varnode = it->element->staticProperty->staticProperty->variable; useDeclaration(varnode, context); diff --git a/duchain/tests/uses.h b/duchain/tests/uses.h --- a/duchain/tests/uses.h +++ b/duchain/tests/uses.h @@ -53,6 +53,7 @@ void interfaceExtendsMultiple(); void staticMemberFunctionCall(); void staticMemberVariable(); + void dynamicStaticMemberVariable(); void constant(); void classConstant(); void classParent(); @@ -89,6 +90,8 @@ void instanceofStaticProperty(); void instanceofMixedProperty(); void instanceofVariableProperty(); + void instanceofDynamicStaticProperty(); + void instanceofDynamicVariableProperty(); void instanceofPropertyArrayAccess(); void classNameString(); void useTrait(); diff --git a/duchain/tests/uses.cpp b/duchain/tests/uses.cpp --- a/duchain/tests/uses.cpp +++ b/duchain/tests/uses.cpp @@ -390,6 +390,26 @@ compareUses(top->localDeclarations().at(1), RangeInRevision(0, 52, 0, 56)); } +void TestUses::dynamicStaticMemberVariable() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("localDeclarations().at(0); + QCOMPARE(dec->identifier(), Identifier("a")); + compareUses(dec, QList() + << RangeInRevision(0, 47, 0, 48)); + + dec = top->localDeclarations().at(1); + QCOMPARE(dec->identifier(), Identifier("var")); + compareUses(dec, QList() + << RangeInRevision(0, 52, 0, 56)); +} + void TestUses::constant() { // 0 1 2 3 4 5 6 7 @@ -1333,6 +1353,98 @@ << RangeInRevision(3, 38, 3, 42)); } +void TestUses::instanceofDynamicStaticProperty() +{ + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + TopDUContext* top = parse("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, 34, 3, 38) + << RangeInRevision(3, 52, 3, 56)); + + dec = top->localDeclarations().at(3); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(3, 43, 3, 47)); +} + +void TestUses::instanceofDynamicVariableProperty() +{ + // 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, 34, 3, 38) + << RangeInRevision(3, 52, 3, 56)); + + dec = top->localDeclarations().at(3); + QCOMPARE(dec->identifier(), Identifier("bar")); + compareUses(dec, QList() + << RangeInRevision(3, 43, 3, 47)); +} + void TestUses::instanceofPropertyArrayAccess() { // 0 1 2 3 4 5