diff --git a/completion/context.cpp b/completion/context.cpp --- a/completion/context.cpp +++ b/completion/context.cpp @@ -678,6 +678,7 @@ case Parser::Token_VOID: case Parser::Token_WHILE: case Parser::Token_WHITESPACE: + case Parser::Token_YIELD: /// TODO: code completion after goto case Parser::Token_GOTO: case Parser::TokenTypeSize: diff --git a/duchain/builders/contextbuilder.h b/duchain/builders/contextbuilder.h --- a/duchain/builders/contextbuilder.h +++ b/duchain/builders/contextbuilder.h @@ -67,9 +67,14 @@ KDevelop::CursorInRevision startPos( AstNode* node); KDevelop::QualifiedIdentifier identifierForNode(IdentifierAst* id) override; + KDevelop::QualifiedIdentifier identifierForNode(SemiReservedIdentifierAst* id); KDevelop::QualifiedIdentifier identifierForNode(VariableIdentifierAst* id); IdentifierPair identifierPairForNode(IdentifierAst* id); + IdentifierPair identifierPairForNode(SemiReservedIdentifierAst* id); + IdentifierPair identifierPairForNode(ReservedNonModifierIdentifierAst* id); QString stringForNode(IdentifierAst* node) const; + QString stringForNode(SemiReservedIdentifierAst* node) const; + QString stringForNode(ReservedNonModifierIdentifierAst* node) const; QString stringForNode(VariableIdentifierAst* node) const; void visitClassDeclarationStatement(ClassDeclarationStatementAst*) override; @@ -103,6 +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, 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 @@ -182,6 +182,15 @@ return QualifiedIdentifier(stringForNode(id)); } + +QualifiedIdentifier ContextBuilder::identifierForNode(SemiReservedIdentifierAst* id) +{ + if (!id) + return QualifiedIdentifier(); + + return QualifiedIdentifier(stringForNode(id)); +} + QualifiedIdentifier ContextBuilder::identifierForNode(VariableIdentifierAst* id) { if (!id) @@ -191,7 +200,27 @@ return QualifiedIdentifier(ret); } -IdentifierPair ContextBuilder::identifierPairForNode( IdentifierAst* id ) +IdentifierPair ContextBuilder::identifierPairForNode(IdentifierAst* id ) +{ + if (!id) { + return qMakePair(IndexedString(), QualifiedIdentifier()); + } + const QString ret = stringForNode(id); + + return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower())); +} + +IdentifierPair ContextBuilder::identifierPairForNode(SemiReservedIdentifierAst* id ) +{ + if (!id) { + return qMakePair(IndexedString(), QualifiedIdentifier()); + } + const QString ret = stringForNode(id); + + return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower())); +} + +IdentifierPair ContextBuilder::identifierPairForNode(ReservedNonModifierIdentifierAst* id ) { if (!id) { return qMakePair(IndexedString(), QualifiedIdentifier()); @@ -205,6 +234,17 @@ { return m_editor->parseSession()->symbol(node->string); } + +QString ContextBuilder::stringForNode(SemiReservedIdentifierAst* node) const +{ + return m_editor->parseSession()->symbol(node->string); +} + +QString ContextBuilder::stringForNode(ReservedNonModifierIdentifierAst* node) const +{ + return m_editor->parseSession()->symbol(node->string); +} + QString ContextBuilder::stringForNode(VariableIdentifierAst* node) const { return m_editor->parseSession()->symbol(node->variable); @@ -244,7 +284,7 @@ visitOptionalModifiers(node->modifiers); if (node->methodName) { //method declaration - DUContext* parameters = openContext(node->parameters, DUContext::Function, node->methodName); + DUContext* parameters = openContext(node->parameters, DUContext::Function, identifierForNode(node->methodName)); Q_ASSERT(!parameters->inSymbolTable()); visitParameterList(node->parameters); @@ -255,7 +295,7 @@ 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); + DUContext* body = openContext(node->methodBody, DUContext::Other, identifierForNode(node->methodName)); if (compilingContexts()) { DUChainWriteLocker lock(DUChain::lock()); body->addImportedParentContext(parameters); @@ -492,6 +532,18 @@ return findDeclarationImportHelper(currentContext(), id, declarationType); } +DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType, + SemiReservedIdentifierAst* 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) { diff --git a/duchain/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h --- a/duchain/builders/declarationbuilder.h +++ b/duchain/builders/declarationbuilder.h @@ -75,6 +75,7 @@ void visitFunctionDeclarationStatement(FunctionDeclarationStatementAst *node) override; void visitClassVariable(ClassVariableAst *node) override; void visitConstantDeclaration(ConstantDeclarationAst *node) override; + void visitClassConstantDeclaration(ClassConstantDeclarationAst *node) override; void visitTraitAliasStatement(TraitAliasStatementAst *node) override; virtual void createTraitAliasDeclarations(TraitAliasStatementAst *node, KDevelop::DeclarationPointer dec); void visitOuterTopStatement(OuterTopStatementAst* node) override; diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -618,6 +618,30 @@ } void DeclarationBuilder::visitConstantDeclaration(ConstantDeclarationAst *node) +{ + if (m_reportErrors) { + // check for redeclarations + DUChainWriteLocker lock(DUChain::lock()); + foreach(Declaration * dec, currentContext()->findLocalDeclarations(identifierForNode(node->identifier).first(), startPos(node->identifier))) + { + if (wasEncountered(dec) && !dec->isFunctionDeclaration() && dec->abstractType()->modifiers() & AbstractType::ConstModifier) { + reportRedeclarationError(dec, node->identifier); + break; + } + } + } + ClassMemberDeclaration* dec = openDefinition(identifierForNode(node->identifier), m_editor->findRange(node->identifier)); + { + DUChainWriteLocker lock(DUChain::lock()); + dec->setAccessPolicy(Declaration::Public); + dec->setStatic(true); + dec->setKind(Declaration::Instance); + } + DeclarationBuilderBase::visitConstantDeclaration(node); + closeDeclaration(); +} + +void DeclarationBuilder::visitClassConstantDeclaration(ClassConstantDeclarationAst *node) { if (m_reportErrors) { // Check for constants in traits @@ -630,6 +654,12 @@ } } + // check for 'class' constant + if (identifierForNode(node->identifier).toString().toLower() == QLatin1String("class")) + { + reportError(i18n("A class constant must not be called 'class'; it is reserved for class name fetching"), node); + } + // check for redeclarations DUChainWriteLocker lock(DUChain::lock()); foreach(Declaration * dec, currentContext()->findLocalDeclarations(identifierForNode(node->identifier).first(), startPos(node->identifier))) @@ -640,14 +670,14 @@ } } } - ClassMemberDeclaration* dec = openDefinition(node->identifier, node->identifier); + ClassMemberDeclaration* dec = openDefinition(identifierForNode(node->identifier), m_editor->findRange(node->identifier)); { DUChainWriteLocker lock(DUChain::lock()); dec->setAccessPolicy(Declaration::Public); dec->setStatic(true); dec->setKind(Declaration::Instance); } - DeclarationBuilderBase::visitConstantDeclaration(node); + DeclarationBuilderBase::visitClassConstantDeclaration(node); closeDeclaration(); } @@ -674,6 +704,8 @@ QualifiedIdentifier alias; if (node->aliasIdentifier) { alias = identifierPairForNode(node->aliasIdentifier).second; + } else if (node->aliasNonModifierIdentifier) { + alias = identifierPairForNode(node->aliasNonModifierIdentifier).second; } else { alias = original; } @@ -683,9 +715,14 @@ TraitMethodAliasDeclaration* newdec; // no existing declaration found, create one - if (node->aliasIdentifier) { - newdec = openDefinition(alias, m_editor->findRange(node->aliasIdentifier)); - newdec->setPrettyName(identifierPairForNode(node->aliasIdentifier).first); + if (node->aliasIdentifier || node->aliasNonModifierIdentifier) { + if (node->aliasIdentifier) { + newdec = openDefinition(alias, m_editor->findRange(node->aliasIdentifier)); + newdec->setPrettyName(identifierPairForNode(node->aliasIdentifier).first); + } else { + newdec = openDefinition(alias, m_editor->findRange(node->aliasNonModifierIdentifier)); + newdec->setPrettyName(identifierPairForNode(node->aliasNonModifierIdentifier).first); + } newdec->setAccessPolicy(olddec->accessPolicy()); openAbstractType(olddec->abstractType()); if (node->modifiers) { @@ -697,20 +734,42 @@ newdec->setAccessPolicy(Declaration::Private); } + if (node->modifiers->modifiers & ModifierAbstract) { + reportError(i18n("Cannot use 'abstract' as method modifier"), node->modifiers, IProblem::Error); + } if (node->modifiers->modifiers & ModifierFinal) { reportError(i18n("Cannot use 'final' as method modifier"), node->modifiers, IProblem::Error); } if (node->modifiers->modifiers & ModifierStatic) { reportError(i18n("Cannot use 'static' as method modifier"), node->modifiers, IProblem::Error); } - } } else { CursorInRevision cursor = m_editor->findRange(node->importIdentifier).start; newdec = openDefinition(alias, RangeInRevision(cursor, cursor)); newdec->setPrettyName(identifierPairForNode(node->importIdentifier->methodIdentifier).first); newdec->setAccessPolicy(olddec->accessPolicy()); openAbstractType(olddec->abstractType()); + + if (node->modifiers) { + if (node->modifiers->modifiers & ModifierPublic) { + newdec->setAccessPolicy(Declaration::Public); + } else if (node->modifiers->modifiers & ModifierProtected) { + newdec->setAccessPolicy(Declaration::Protected); + } else if (node->modifiers->modifiers & ModifierPrivate) { + newdec->setAccessPolicy(Declaration::Private); + } + + if (node->modifiers->modifiers & ModifierAbstract) { + reportError(i18n("Cannot use 'abstract' as method modifier"), node->modifiers, IProblem::Error); + } + if (node->modifiers->modifiers & ModifierFinal) { + reportError(i18n("Cannot use 'final' as method modifier"), node->modifiers, IProblem::Error); + } + if (node->modifiers->modifiers & ModifierStatic) { + reportError(i18n("Cannot use 'static' as method modifier"), node->modifiers, IProblem::Error); + } + } } newdec->setKind(Declaration::Type); newdec->setAliasedDeclaration(IndexedDeclaration(olddec)); diff --git a/duchain/builders/typebuilder.h b/duchain/builders/typebuilder.h --- a/duchain/builders/typebuilder.h +++ b/duchain/builders/typebuilder.h @@ -54,6 +54,7 @@ void visitClassStatement(ClassStatementAst *node) override; void visitClassVariable(ClassVariableAst *node) override; void visitConstantDeclaration(ConstantDeclarationAst* node) override; + void visitClassConstantDeclaration(ClassConstantDeclarationAst* node) override; void visitParameter(ParameterAst *node) override; void visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node) override; void visitClosure(ClosureAst* node) override; diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp --- a/duchain/builders/typebuilder.cpp +++ b/duchain/builders/typebuilder.cpp @@ -350,6 +350,22 @@ } } +void TypeBuilder::visitClassConstantDeclaration(ClassConstantDeclarationAst* node) +{ + if (!m_gotTypeFromDocComment || !currentAbstractType()) { + AbstractType::Ptr type = getTypeForNode(node->scalar); + type->setModifiers(type->modifiers() | AbstractType::ConstModifier); + openAbstractType(type); + + TypeBuilderBase::visitClassConstantDeclaration(node); + + closeType(); + } else { + currentAbstractType()->setModifiers(currentAbstractType()->modifiers() & AbstractType::ConstModifier); + TypeBuilderBase::visitClassConstantDeclaration(node); + } +} + void TypeBuilder::visitParameter(ParameterAst *node) { AbstractType::Ptr phpDocTypehint; diff --git a/duchain/tests/duchain.h b/duchain/tests/duchain.h --- a/duchain/tests/duchain.h +++ b/duchain/tests/duchain.h @@ -32,9 +32,11 @@ private slots: void declareFunction(); + void declareSemiReservedFunction(); void declareVar(); void varTypehint(); void declareClass(); + void declareClassWithSemiReservedMethod(); void classMemberVar(); void declareTypehintFunction(); void declareVariadicFunction(); @@ -74,8 +76,10 @@ void ownStaticMemberVariable(); void classConst(); void classConst_data(); + void semiReservedClassConst(); void fileConst(); void fileConst_data(); + void semiReservedFileConst(); void define(); void defaultFunctionParam(); void defaultFunctionParamWithTypehint(); diff --git a/duchain/tests/duchain.cpp b/duchain/tests/duchain.cpp --- a/duchain/tests/duchain.cpp +++ b/duchain/tests/duchain.cpp @@ -86,6 +86,16 @@ QCOMPARE(top->childContexts().at(1)->type(), DUContext::Other); } +void TestDUChain::declareSemiReservedFunction() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("accessPolicy(), Declaration::Public); } +void TestDUChain::declareClassWithSemiReservedMethod() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("parentContext()); + QCOMPARE(top->childContexts().count(), 1); + + DUContext* contextClassA = top->childContexts().first(); + + QCOMPARE(top->localDeclarations().count(), 1); + Declaration* dec = top->localDeclarations().first(); + QCOMPARE(dec->kind(), Declaration::Type); + QCOMPARE(dec->toString(), QString("class A")); + QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier("a")); + QCOMPARE(dec->isDefinition(), true); + QCOMPARE(dec->logicalInternalContext(top), contextClassA); + + qDebug() << contextClassA->localScopeIdentifier().toString(); + QCOMPARE(contextClassA->localScopeIdentifier(), QualifiedIdentifier("a")); + QCOMPARE(contextClassA->childContexts().count(), 4); + QCOMPARE(contextClassA->childContexts().first()->localScopeIdentifier(), QualifiedIdentifier("switch")); + + DUContext* contextMethodBodyFoo = contextClassA->childContexts().at(1); + QCOMPARE(contextMethodBodyFoo->localScopeIdentifier(), QualifiedIdentifier("switch")); + QCOMPARE(contextMethodBodyFoo->importedParentContexts().count(), 1); + QCOMPARE(contextMethodBodyFoo->childContexts().count(), 0); + QVERIFY(contextMethodBodyFoo->importedParentContexts().first().context(top) == + contextClassA->childContexts().first()); + + //switch() + dec = contextClassA->localDeclarations().at(0); + ClassFunctionDeclaration* funDec = dynamic_cast(dec); + QVERIFY(funDec); + QCOMPARE(funDec->kind(), Declaration::Type); + QCOMPARE(funDec->identifier(), Identifier("switch")); + QCOMPARE(funDec->accessPolicy(), Declaration::Public); + QCOMPARE(funDec->isStatic(), false); + + { + // no return means void as return type + FunctionType::Ptr ftype = FunctionType::Ptr::dynamicCast(dec->abstractType()); + QVERIFY(ftype); + IntegralType::Ptr itype = IntegralType::Ptr::dynamicCast(ftype->returnType()); + QVERIFY(itype->dataType() == IntegralType::TypeVoid); + } + + //public() + dec = contextClassA->localDeclarations().at(1); + funDec = dynamic_cast(dec); + QVERIFY(funDec); + QCOMPARE(funDec->identifier(), Identifier("public")); + QCOMPARE(funDec->accessPolicy(), Declaration::Protected); + QCOMPARE(funDec->isStatic(), true); +} + void TestDUChain::classMemberVar() { // 0 1 2 3 4 5 6 7 @@ -1305,6 +1376,26 @@ QCOMPARE(top->findDeclarations(QualifiedIdentifier("a::C")).first()->context(), top->childContexts().last()); } +void TestDUChain::semiReservedClassConst() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("childContexts().count(), 1); + QCOMPARE(top->problems().count(), 0); + + QCOMPARE(top->findDeclarations(QualifiedIdentifier("a::SWITCH")).count(), 1); + QCOMPARE(top->findDeclarations(QualifiedIdentifier("a::SWITCH")).first()->context(), top->childContexts().last()); + + QCOMPARE(top->findDeclarations(QualifiedIdentifier("a::PUBLIC")).count(), 1); + QCOMPARE(top->findDeclarations(QualifiedIdentifier("a::PUBLIC")).first()->context(), top->childContexts().last()); +} + void TestDUChain::fileConst_data() { QTest::addColumn("code"); @@ -1342,6 +1433,16 @@ QVERIFY(type->modifiers() & AbstractType::ConstModifier); } +void TestDUChain::semiReservedFileConst() +{ + // 0 1 2 3 4 5 6 7 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + QByteArray method("("code"); - QTest::newRow("staticModifier") << "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); + "$c = new Baz(); $c->one(); $c->two(); $c->baz; $c::six();\n" + "$d = new Boo(); $d->one(); $d->switch(); $d->three(); $d->static();\n", DumpAll); QVERIFY(top); DUChainReleaser releaseTop(top); DUChainWriteLocker lock; @@ -1166,84 +1168,121 @@ TraitMethodAliasDeclaration* method; TraitMemberAliasDeclaration* member; - QCOMPARE(topDecs.size(), 9); + QCOMPARE(topDecs.size(), 11); 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) ); + << RangeInRevision(6, 41, 6, 42) + << RangeInRevision(7, 16, 7, 17) + << RangeInRevision(7, 24, 7, 25) + << RangeInRevision(7, 46, 7, 47) ); 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) ); + << RangeInRevision(6, 43, 6, 44) + << RangeInRevision(7, 18, 7, 19) + << RangeInRevision(7, 41, 7, 42) + << RangeInRevision(7, 64, 7, 65) ); 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) ); + << RangeInRevision(6, 46, 6, 47) + << RangeInRevision(7, 20, 7, 21) + << RangeInRevision(7, 43, 7, 44) + << RangeInRevision(7, 84, 7, 85) ); 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) ); + compareUses(dec, QList() << RangeInRevision(8, 20, 8, 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) ); + compareUses(dec, QList() << RangeInRevision(8, 31, 8, 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) ); + compareUses(dec, QList() << RangeInRevision(9, 20, 9, 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) ); + compareUses(dec, QList() << RangeInRevision(9, 31, 9, 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) ); + compareUses(dec, QList() << RangeInRevision(9, 42, 9, 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) ); + compareUses(dec, QList() << RangeInRevision(10, 20, 10, 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) ); + compareUses(dec, QList() << RangeInRevision(10, 31, 10, 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) ); + compareUses(dec, QList() << RangeInRevision(10, 42, 10, 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) ); + compareUses(dec, QList() << RangeInRevision(10, 51, 10, 54) ); + + dec = topDecs[6]->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(11, 20, 11, 23) ); + + dec = topDecs[6]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("switch"))).first(); + method = dynamic_cast(dec); + QVERIFY(method); + QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("a")); + QCOMPARE(method->aliasedDeclaration().data()->identifier(), Identifier("two")); + compareUses(dec, QList() << RangeInRevision(11, 31, 11, 37) ); + + dec = topDecs[6]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("three"))).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(11, 45, 11, 50) ); + + dec = topDecs[6]->internalContext()->findLocalDeclarations(Identifier(QStringLiteral("static"))).first(); + method = dynamic_cast(dec); + QVERIFY(method); + QCOMPARE(method->aliasedDeclaration().data()->context()->owner()->identifier(), Identifier("c")); + QCOMPARE(method->aliasedDeclaration().data()->identifier(), Identifier("five")); + compareUses(dec, QList() << RangeInRevision(11, 58, 11, 64) ); } void TestUses::exceptionFinally() diff --git a/parser/php.g b/parser/php.g --- a/parser/php.g +++ b/parser/php.g @@ -238,7 +238,7 @@ REQUIRE_ONCE ("require_once"), NAMESPACE ("namespace"), NAMESPACE_C("__NAMESPACE__"), USE("use"), GOTO ("goto"), TRAIT ("trait"), INSTEADOF ("insteadof"), CALLABLE ("callable"), ITERABLE ("iterable"), BOOL ("bool"), FLOAT ("float"), INT ("int"), STRING_TYPE ("string"), - VOID ("void"), DIR ("__DIR__"), TRAIT_C ("__TRAIT__") ;; + VOID ("void"), DIR ("__DIR__"), TRAIT_C ("__TRAIT__"), YIELD ("yield") ;; -- casts: %token INT_CAST ("int cast"), DOUBLE_CAST ("double cast"), STRING_CAST ("string cast"), @@ -656,7 +656,7 @@ LPAREN stringParameterList=functionCallParameterList RPAREN | PAAMAYIM_NEKUDOTAYIM ( - stringFunctionName=identifier LPAREN stringParameterList=functionCallParameterList RPAREN + stringFunctionName=semiReservedIdentifier LPAREN stringParameterList=functionCallParameterList RPAREN | varFunctionName=variableWithoutObjects LPAREN stringParameterList=functionCallParameterList RPAREN | LBRACE (expr=expr) RBRACE LPAREN stringParameterList=functionCallParameterList RPAREN ) @@ -728,6 +728,9 @@ identifier=identifier ASSIGN scalar=expr -> constantDeclaration ;; + identifier=semiReservedIdentifier ASSIGN scalar=expr +-> classConstantDeclaration ;; + SEMICOLON | CLOSE_TAG -> semicolonOrCloseTag ;; @@ -842,8 +845,7 @@ ( PAAMAYIM_NEKUDOTAYIM classConstant=classConstant | 0 ) -> constantOrClassConst ;; - CLASS - | identifier + semiReservedIdentifier -> classConstant ;; #encaps=encaps* @@ -945,6 +947,47 @@ string=STRING -> identifier ;; + INCLUDE | INCLUDE_ONCE | EVAL | REQUIRE | REQUIRE_ONCE | LOGICAL_OR | LOGICAL_XOR | LOGICAL_AND + | INSTANCEOF | NEW | CLONE | EXIT | IF | ELSEIF | ELSE | ENDIF | ECHO | DO | WHILE | ENDWHILE + | FOR | ENDFOR | FOREACH | ENDFOREACH | DECLARE | ENDDECLARE | AS | TRY | CATCH | FINALLY + | THROW | USE | INSTEADOF | GLOBAL | VAR | UNSET | ISSET | EMPTY | CONTINUE | GOTO + | FUNCTION | CONST | RETURN | PRINT | YIELD | LIST | SWITCH | ENDSWITCH | CASE | DEFAULT | BREAK + | ARRAY | CALLABLE | EXTENDS | IMPLEMENTS | NAMESPACE | TRAIT | INTERFACE | CLASS + | CLASS_C | TRAIT_C | FUNC_C | METHOD_C | LINE | FILE | DIR | NAMESPACE_C +-> reservedNonModifiers ;; + + reservedNonModifiers + | STATIC | ABSTRACT | FINAL | PRIVATE | PROTECTED | PUBLIC +-> semiReserved ;; + + identifier +[: + qint64 index = tokenStream->index() - 2; + (*yynode)->string = index; +:] + | semiReserved +[: + qint64 index = tokenStream->index() - 2; + (*yynode)->string = index; +:] +-> semiReservedIdentifier [ + member variable string: qint64; +] ;; + + identifier +[: + qint64 index = tokenStream->index() - 2; + (*yynode)->string = index; +:] + | reservedNonModifiers +[: + qint64 index = tokenStream->index() - 2; + (*yynode)->string = index; +:] +-> reservedNonModifierIdentifier [ + member variable string: qint64; +] ;; + variable=VARIABLE -> variableIdentifier ;; @@ -991,11 +1034,11 @@ RBRACE [: rewind(tokenStream->index() - 2); :] -> classBody ;; - CONST #consts=constantDeclaration @ COMMA SEMICOLON + CONST #consts=classConstantDeclaration @ COMMA SEMICOLON | VAR variable=classVariableDeclaration SEMICOLON | modifiers=optionalModifiers ( variable=classVariableDeclaration SEMICOLON - | FUNCTION (BIT_AND | 0) methodName=identifier LPAREN parameters=parameterList RPAREN + | FUNCTION (BIT_AND | 0) methodName=semiReservedIdentifier LPAREN parameters=parameterList RPAREN ( COLON returnType=returnType | 0) methodBody=methodBody ) @@ -1006,10 +1049,18 @@ @ (SEMICOLON [: if (yytoken == Token_RBRACE) { break; } :]) RBRACE -> traitAliasDeclaration ;; - importIdentifier=traitAliasIdentifier (AS (modifiers=optionalModifiers | 0) aliasIdentifier=identifier|INSTEADOF #conflictIdentifier=namespacedIdentifier @ COMMA) + importIdentifier=traitAliasIdentifier +-- first/first conflict resolved by LA(2) +-- We can either have a single token (modifier or identifier), or a combination + ( AS (?[: LA(2).kind == Token_SEMICOLON :] + (modifiers=traitVisibilityModifiers | aliasNonModifierIdentifier=reservedNonModifierIdentifier) + | modifiers=traitVisibilityModifiers aliasIdentifier=semiReservedIdentifier + ) + | INSTEADOF #conflictIdentifier=namespacedIdentifier @ COMMA + ) -> traitAliasStatement ;; - identifier=namespacedIdentifier PAAMAYIM_NEKUDOTAYIM methodIdentifier=identifier + identifier=namespacedIdentifier PAAMAYIM_NEKUDOTAYIM methodIdentifier=semiReservedIdentifier -> traitAliasIdentifier ;; SEMICOLON -- abstract method @@ -1022,6 +1073,16 @@ variable=variableIdentifier (ASSIGN value=staticScalar | 0) -> classVariable ;; + PUBLIC [: (*yynode)->modifiers |= ModifierPublic; :] + | PROTECTED [: (*yynode)->modifiers |= ModifierProtected; :] + | PRIVATE [: (*yynode)->modifiers |= ModifierPrivate; :] + | STATIC [: (*yynode)->modifiers |= ModifierStatic; :] + | ABSTRACT [: (*yynode)->modifiers |= ModifierAbstract; :] + | FINAL [: (*yynode)->modifiers |= ModifierFinal; :] +-> traitVisibilityModifiers[ + member variable modifiers: unsigned int; +] ;; + ( PUBLIC [: (*yynode)->modifiers |= ModifierPublic; :] | PROTECTED [: (*yynode)->modifiers |= ModifierProtected; :] diff --git a/parser/phplexer.cpp b/parser/phplexer.cpp --- a/parser/phplexer.cpp +++ b/parser/phplexer.cpp @@ -810,6 +810,8 @@ token = Parser::Token_STRING_TYPE; } else if (name.compare(QLatin1String("void"), Qt::CaseInsensitive) == 0) { token = Parser::Token_VOID; + } else if (name.compare(QLatin1String("yield"), Qt::CaseInsensitive) == 0) { + token = Parser::Token_YIELD; } else { token = Parser::Token_STRING; }