Commit 01bee889 authored by Hugues Mitonneau's avatar Hugues Mitonneau Committed by Heinz Wiesinger

Support importing functions and constants from other namespaces

Summary:
T6810
Add support for syntax `use const xxx\xxx` and `use function xxx\xxx`

BUG: 408609
FIXED-IN: 5.5.0

Reviewers: pprkut

Reviewed By: pprkut

Subscribers: pprkut, kdevelop-devel

Tags: #kdevelop

Differential Revision: https://phabricator.kde.org/D25956
parent dda2e6e7
......@@ -201,14 +201,18 @@ QualifiedIdentifier ContextBuilder::identifierForNode(VariableIdentifierAst* id)
return QualifiedIdentifier(ret);
}
IdentifierPair ContextBuilder::identifierPairForNode(IdentifierAst* id )
IdentifierPair ContextBuilder::identifierPairForNode(IdentifierAst* id, bool isConstIdentifier)
{
if (!id) {
return qMakePair(IndexedString(), QualifiedIdentifier());
}
const QString ret = stringForNode(id);
return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower()));
if ( isConstIdentifier ) {
return qMakePair(IndexedString(ret), QualifiedIdentifier(ret));
} else {
return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower()));
}
}
IdentifierPair ContextBuilder::identifierPairForNode(SemiReservedIdentifierAst* id )
......
......@@ -69,7 +69,7 @@ protected:
KDevelop::QualifiedIdentifier identifierForNode(IdentifierAst* id) override;
KDevelop::QualifiedIdentifier identifierForNode(SemiReservedIdentifierAst* id);
KDevelop::QualifiedIdentifier identifierForNode(VariableIdentifierAst* id);
IdentifierPair identifierPairForNode(IdentifierAst* id);
IdentifierPair identifierPairForNode(IdentifierAst* id, bool isConstIdentifier = false);
IdentifierPair identifierPairForNode(SemiReservedIdentifierAst* id);
IdentifierPair identifierPairForNode(ReservedNonModifierIdentifierAst* id);
QString stringForNode(IdentifierAst* node) const;
......
......@@ -1625,9 +1625,27 @@ void DeclarationBuilder::closeNamespace(NamespaceDeclarationStatementAst* parent
closeDeclaration();
}
void DeclarationBuilder::visitUseStatement(UseStatementAst* node)
{
if ( node->useFunction != -1 )
{
m_useNamespaceType = FunctionDeclarationType;
}
else if ( node->useConst != -1 )
{
m_useNamespaceType = ConstantDeclarationType;
}
else
{
m_useNamespaceType = ClassDeclarationType;
}
DeclarationBuilderBase::visitUseStatement(node);
}
void DeclarationBuilder::visitUseNamespace(UseNamespaceAst* node)
{
DUChainWriteLocker lock;
bool isConstIdentifier = ( m_useNamespaceType == ConstantDeclarationType );
if ( currentContext()->type() != DUContext::Namespace &&
!node->aliasIdentifier && node->identifier->namespaceNameSequence->count() == 1 ) {
......@@ -1637,23 +1655,22 @@ void DeclarationBuilder::visitUseNamespace(UseNamespaceAst* node)
return;
}
IdentifierAst* idNode = node->aliasIdentifier ? node->aliasIdentifier : node->identifier->namespaceNameSequence->back()->element;
IdentifierPair id = identifierPairForNode(idNode);
IdentifierPair id = identifierPairForNode(idNode, isConstIdentifier);
///TODO: case insensitive!
QualifiedIdentifier qid = identifierForNamespace(node->identifier, m_editor);
DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, qid);
QualifiedIdentifier qid = identifierForNamespace(node->identifier, m_editor, isConstIdentifier);
DeclarationPointer dec = findDeclarationImport(m_useNamespaceType, qid);
if (!dec && !qid.explicitlyGlobal()) {
QualifiedIdentifier globalQid = qid;
globalQid.setExplicitlyGlobal(true);
dec = findDeclarationImport(ClassDeclarationType, globalQid);
dec = findDeclarationImport(m_useNamespaceType, globalQid);
}
if (dec)
{
// Check for a name conflict
DeclarationPointer dec2 = findDeclarationImport(ClassDeclarationType, id.second);
DeclarationPointer dec2 = findDeclarationImport(m_useNamespaceType, id.second);
if (dec2 && dec2->context()->scopeIdentifier() == currentContext()->scopeIdentifier() &&
dec2->context()->topContext() == currentContext()->topContext() &&
......
......@@ -55,6 +55,7 @@ public:
{
m_editor = editor;
m_findVariable.find = false;
m_useNamespaceType = ClassDeclarationType;
}
KDevelop::ReferencedTopDUContext build(const KDevelop::IndexedString& url, AstNode* node,
const KDevelop::ReferencedTopDUContext& updateContext
......@@ -94,6 +95,7 @@ protected:
void visitAssignmentListElement(AssignmentListElementAst* node) override;
void openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const KDevelop::RangeInRevision& range) override;
void closeNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier) override;
void visitUseStatement(UseStatementAst* node) override;
void visitUseNamespace(UseNamespaceAst* node) override;
void visitClosure(ClosureAst* node) override;
void visitLexicalVar(LexicalVarAst* node) override;
......@@ -150,6 +152,8 @@ private:
ParameterAst *m_functionDeclarationPreviousArgument = nullptr;
/// The AstNode of the previous function call argument
FunctionCallParameterListElementAst *m_functionCallPreviousArgument = nullptr;
/// Type of use
DeclarationType m_useNamespaceType;
unsigned int m_currentModifers;
QString m_lastTopStatementComment;
......
......@@ -227,19 +227,36 @@ void UseBuilder::visitUnaryExpression( UnaryExpressionAst* node )
}
}
void UseBuilder::visitUseStatement(UseStatementAst* node)
{
if ( node->useFunction != -1 )
{
m_useNamespaceType = FunctionDeclarationType;
}
else if ( node->useConst != -1 )
{
m_useNamespaceType = ConstantDeclarationType;
}
else
{
m_useNamespaceType = NamespaceDeclarationType;
}
UseBuilderBase::visitUseStatement(node);
}
void UseBuilder::visitUseNamespace(UseNamespaceAst* node)
{
buildNamespaceUses(node->identifier, NamespaceDeclarationType);
buildNamespaceUses(node->identifier, m_useNamespaceType);
}
void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationType lastType)
{
QualifiedIdentifier identifier = identifierForNamespace(node, m_editor);
QualifiedIdentifier identifier = identifierForNamespace(node, m_editor, lastType == ConstantDeclarationType);
QualifiedIdentifier curId;
// check if we need to resolve the namespaced identifier globally or locally
DeclarationPointer tempDec = findDeclarationImport(ClassDeclarationType, identifier);
DeclarationPointer tempDec = findDeclarationImport(lastType, identifier);
// if we couldn't find a class declaration, it might be a partial namespace identifier
if (!tempDec) {
......@@ -248,7 +265,7 @@ void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationTy
if (!tempDec && !identifier.explicitlyGlobal()) {
identifier.setExplicitlyGlobal(true);
tempDec = findDeclarationImport(ClassDeclarationType, identifier);
tempDec = findDeclarationImport(lastType, identifier);
if (!tempDec) {
tempDec = findDeclarationImport(NamespaceDeclarationType, identifier);
......
......@@ -66,6 +66,7 @@ protected:
void visitStatement(StatementAst* node) override;
void visitCatchItem(CatchItemAst* node) override;
void visitUnaryExpression( UnaryExpressionAst* node ) override;
void visitUseStatement(UseStatementAst* 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;
......@@ -74,6 +75,9 @@ private:
void buildNamespaceUses(Php::NamespacedIdentifierAst* node, Php::DeclarationType lastType = Php::ClassDeclarationType);
void visitNodeWithExprVisitor(AstNode* node);
/// Type of use
DeclarationType m_useNamespaceType;
};
}
......
......@@ -1101,6 +1101,63 @@ void TestUses::useNamespace()
compareUses(dec, RangeInRevision(8, 30, 8, 31));
}
void TestUses::useNamespaceFunctionConst()
{
// 0 1 2 3 4 5 6 7
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789
TopDUContext* top = parse("<?php\n"
/* 1*/ "namespace Foo\\Bar {function A(){} const B = 1;}\n"
/* 2*/ "namespace Qux {function F(){} const C = 2;}\n"
/* 3*/ "namespace Baz {function F(){} const C = 3;}\n"
/* 4*/ "namespace {\n"
/* 5*/ "use function Foo\\Bar\\A;\n"
/* 6*/ "use const Foo\\Bar\\B;\n"
/* 7*/ "use function Qux\\F as QuxF, Baz\\F as BazF;\n"
/* 8*/ "use const Qux\\C as QuxC, Baz\\C as BazC;\n"
/* 9*/ "echo A();\n"
/*10*/ "echo B;\n"
/*11*/ "echo QuxF();\n"
/*12*/ "echo QuxC;\n"
/*13*/ "echo BazF();\n"
/*14*/ "echo BazC;\n"
"}\n", DumpNone);
QVERIFY(top);
DUChainReleaser releaseTop(top);
DUChainWriteLocker lock;
Declaration* dec;
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("a"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Type);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(5, 21, 5, 22) << RangeInRevision(9, 5, 9, 6));
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("B"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Instance);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(6, 18, 6, 19) << RangeInRevision(10, 5, 10, 6));
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("quxf"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Type);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(7, 17, 7, 18) << RangeInRevision(11, 5, 11, 9));
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("bazf"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Type);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(7, 32, 7, 33) << RangeInRevision(13, 5, 13, 9));
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("QuxC"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Instance);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(8, 14, 8, 15) << RangeInRevision(12, 5, 12, 9));
dec = top->findDeclarations(QualifiedIdentifier(QStringLiteral("BazC"))).first();
QVERIFY(dec);
QCOMPARE(dec->kind(), Declaration::Instance);
compareUses(dec, QList<RangeInRevision>() << RangeInRevision(8, 29, 8, 30) << RangeInRevision(14, 5, 14, 9));
}
void TestUses::lateStatic()
{
// 0 1 2 3 4 5 6 7
......
......@@ -82,6 +82,7 @@ private slots:
void functionArguments();
void namespaces();
void useNamespace();
void useNamespaceFunctionConst();
void lateStatic();
void closures();
void closureTypehints();
......
......@@ -723,11 +723,14 @@ expression=nullCoalesceExpression
| OPEN_TAG_WITH_ECHO expr=expr semicolonOrCloseTag
| INLINE_HTML
| CONST #consts=constantDeclaration @ COMMA SEMICOLON
| USE #useNamespace=useNamespace @ COMMA SEMICOLON
| USE useStatement=useStatement
| GOTO gotoLabel=STRING SEMICOLON
| gotoTarget=STRING COLON
-> statement ;;
( useFunction=FUNCTION | useConst=CONST | 0 ) #useNamespace=useNamespace @ COMMA SEMICOLON
-> useStatement ;;
identifier=namespacedIdentifier (AS aliasIdentifier=identifier | 0)
-> useNamespace ;;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment