diff --git a/duchain/builders/declarationbuilder.h b/duchain/builders/declarationbuilder.h --- a/duchain/builders/declarationbuilder.h +++ b/duchain/builders/declarationbuilder.h @@ -96,7 +96,8 @@ 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 visitUseNamespaceOrUseGroupedNamespace(UseNamespaceOrUseGroupedNamespaceAst* node) override; + void visitNonGroupedUseNamespace(UseNamespaceOrUseGroupedNamespaceAst* node); void visitClosure(ClosureAst* node) override; void visitLexicalVar(LexicalVarAst* node) override; void visitVarExpression(VarExpressionAst* node) override; diff --git a/duchain/builders/declarationbuilder.cpp b/duchain/builders/declarationbuilder.cpp --- a/duchain/builders/declarationbuilder.cpp +++ b/duchain/builders/declarationbuilder.cpp @@ -1642,7 +1642,16 @@ DeclarationBuilderBase::visitUseStatement(node); } -void DeclarationBuilder::visitUseNamespace(UseNamespaceAst* node) +void DeclarationBuilder::visitUseNamespaceOrUseGroupedNamespace(UseNamespaceOrUseGroupedNamespaceAst* node) +{ + if (node->compoundNamespace) { + // TODO + } else { + visitNonGroupedUseNamespace(node); + } +} + +void DeclarationBuilder::visitNonGroupedUseNamespace(UseNamespaceOrUseGroupedNamespaceAst* node) { DUChainWriteLocker lock; bool isConstIdentifier = ( m_useNamespaceType == ConstantDeclarationType ); diff --git a/duchain/builders/typebuilder.cpp b/duchain/builders/typebuilder.cpp --- a/duchain/builders/typebuilder.cpp +++ b/duchain/builders/typebuilder.cpp @@ -47,6 +47,7 @@ TypeBuilder::TypeBuilder() : TypeBuilderBase() , m_gotTypeFromDocComment(false) + , m_gotTypeFromTypeHint(false) , m_gotReturnTypeFromDocComment(false) { } diff --git a/duchain/builders/usebuilder.h b/duchain/builders/usebuilder.h --- a/duchain/builders/usebuilder.h +++ b/duchain/builders/usebuilder.h @@ -66,19 +66,29 @@ 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 visitUseNamespaceOrUseGroupedNamespace(UseNamespaceOrUseGroupedNamespaceAst* node) override; + void visitInnerUseNamespace(InnerUseNamespaceAst* node) override; void openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const KDevelop::RangeInRevision& range) override; void visitPropertyType(PropertyTypeAst* node) override; void visitReturnType(ReturnTypeAst* node) override; private: - void buildNamespaceUses(Php::NamespacedIdentifierAst* node, Php::DeclarationType lastType = Php::ClassDeclarationType); + void buildNamespaceUses( + Php::NamespacedIdentifierAst* node, + Php::DeclarationType lastType = Php::ClassDeclarationType); + void buildNamespaceUses( + Php::NamespacedIdentifierBeforeGroupedNamespaceAst* node, + Php::UseImportType useImportType); + void buildNamespaceUses( + KDevelop::QualifiedIdentifier identifier, + const KDevPG::ListNode* prefixNamespaceNameSequence, + const KDevPG::ListNode* namespaceNameSequence, + Php::DeclarationType lastType); void visitNodeWithExprVisitor(AstNode* node); - /// Type of use - DeclarationType m_useNamespaceType; + /// Prefix in front of grouped namespace + NamespacedIdentifierBeforeGroupedNamespaceAst *m_compoundNamespacePrefix; }; } diff --git a/duchain/builders/usebuilder.cpp b/duchain/builders/usebuilder.cpp --- a/duchain/builders/usebuilder.cpp +++ b/duchain/builders/usebuilder.cpp @@ -236,32 +236,72 @@ } } -void UseBuilder::visitUseStatement(UseStatementAst* node) +void UseBuilder::visitUseNamespaceOrUseGroupedNamespace(UseNamespaceOrUseGroupedNamespaceAst* node) { - if ( node->useFunction != -1 ) - { - m_useNamespaceType = FunctionDeclarationType; + if (node->compoundNamespace) { + QualifiedIdentifier identifier = identifierForNamespace(node->identifier, m_editor, false); + buildNamespaceUses( + identifier, + nullptr, + node->identifier->namespaceNameSequence, + NamespaceDeclarationType); + m_compoundNamespacePrefix = node->identifier; + visitCompoundNamespace(node->compoundNamespace); + } else { + buildNamespaceUses(node->identifier, node->useImportType); } - else if ( node->useConst != -1 ) - { - m_useNamespaceType = ConstantDeclarationType; - } - else - { - m_useNamespaceType = NamespaceDeclarationType; +} + +void UseBuilder::visitInnerUseNamespace(InnerUseNamespaceAst* node) +{ + Php::DeclarationType lastType; + if (node->useImportType == ConstantImport) { + lastType = ConstantDeclarationType; + } else if (node->useImportType == FunctionImport) { + lastType = FunctionDeclarationType; + } else { + lastType = NamespaceDeclarationType; } - UseBuilderBase::visitUseStatement(node); + + QualifiedIdentifier identifier = identifierForNamespace( + m_compoundNamespacePrefix, + node, + m_editor, + node->useImportType == ConstantImport); + buildNamespaceUses( + identifier, + m_compoundNamespacePrefix->namespaceNameSequence, + node->namespaceNameSequence, + lastType); } -void UseBuilder::visitUseNamespace(UseNamespaceAst* node) +void UseBuilder::buildNamespaceUses(NamespacedIdentifierBeforeGroupedNamespaceAst* node, UseImportType useImportType) { - buildNamespaceUses(node->identifier, m_useNamespaceType); + Php::DeclarationType lastType; + if (useImportType == ConstantImport) { + lastType = ConstantDeclarationType; + } else if (useImportType == FunctionImport) { + lastType = FunctionDeclarationType; + } else { + lastType = NamespaceDeclarationType; + } + + QualifiedIdentifier identifier = identifierForNamespace(node, m_editor, useImportType == ConstantImport); + buildNamespaceUses(identifier, nullptr, node->namespaceNameSequence, lastType); } void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationType lastType) { QualifiedIdentifier identifier = identifierForNamespace(node, m_editor, lastType == ConstantDeclarationType); + buildNamespaceUses(identifier, nullptr, node->namespaceNameSequence, lastType); +} +void UseBuilder::buildNamespaceUses( + KDevelop::QualifiedIdentifier identifier, + const KDevPG::ListNode* prefixNamespaceNameSequence, + const KDevPG::ListNode* namespaceNameSequence, + Php::DeclarationType lastType) +{ QualifiedIdentifier curId; // check if we need to resolve the namespaced identifier globally or locally @@ -287,19 +327,23 @@ } curId.setExplicitlyGlobal(identifier.explicitlyGlobal()); - Q_ASSERT(identifier.count() == node->namespaceNameSequence->count()); + int prefixCount = prefixNamespaceNameSequence == nullptr ? 0 : prefixNamespaceNameSequence->count(); + Q_ASSERT(identifier.count() == prefixCount + namespaceNameSequence->count()); for ( int i = 0; i < identifier.count() - 1; ++i ) { curId.push(identifier.at(i)); - AstNode* n = node->namespaceNameSequence->at(i)->element; - DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId); - if (!dec || dec->range() != editorFindRange(n, n)) { - newCheckedUse(n, dec, true); + if (i>=prefixCount) { + AstNode* n = namespaceNameSequence->at(i)->element; + DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId); + if (!dec || dec->range() != editorFindRange(n, n)) { + newCheckedUse(n, dec, true); + } } } - newCheckedUse(node->namespaceNameSequence->back()->element, - findDeclarationImport(lastType, identifier), - lastType == ClassDeclarationType || lastType == ConstantDeclarationType - || lastType == FunctionDeclarationType || lastType == NamespaceDeclarationType); + bool reportNotFound = lastType == ClassDeclarationType + || lastType == ConstantDeclarationType + || lastType == FunctionDeclarationType + || lastType == NamespaceDeclarationType; + newCheckedUse(namespaceNameSequence->back()->element, findDeclarationImport(lastType, identifier), reportNotFound); } void UseBuilder::openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, diff --git a/duchain/helper.h b/duchain/helper.h --- a/duchain/helper.h +++ b/duchain/helper.h @@ -26,6 +26,7 @@ #include #include #include +#include "phpast.h" namespace KDevelop { @@ -34,15 +35,6 @@ } namespace Php { -struct UnaryExpressionAst; -struct AstNode; -struct CommonScalarAst; -struct NamespacedIdentifierAst; -struct ParameterAst; -struct GenericTypeHintAst; -struct ReturnTypeAst; -struct ClassStatementAst; -struct PropertyTypeHintAst; class EditorIntegrator; enum DeclarationType { @@ -96,6 +88,15 @@ */ KDEVPHPDUCHAIN_EXPORT KDevelop::QualifiedIdentifier identifierForNamespace(NamespacedIdentifierAst* node, EditorIntegrator* editor, bool lastIsConstIdentifier = false); +KDEVPHPDUCHAIN_EXPORT KDevelop::QualifiedIdentifier identifierForNamespace( + NamespacedIdentifierBeforeGroupedNamespaceAst* node, + EditorIntegrator* editor, + bool lastIsConstIdentifier); +KDEVPHPDUCHAIN_EXPORT KDevelop::QualifiedIdentifier identifierForNamespace( + NamespacedIdentifierBeforeGroupedNamespaceAst* prefixNode, + InnerUseNamespaceAst* node, + EditorIntegrator* editor, + bool lastIsConstIdentifier); /** * Get proper QualifiedIdentifier for a basic identifier. diff --git a/duchain/helper.cpp b/duchain/helper.cpp --- a/duchain/helper.cpp +++ b/duchain/helper.cpp @@ -456,6 +456,51 @@ return id; } +QualifiedIdentifier identifierForNamespace(NamespacedIdentifierBeforeGroupedNamespaceAst* node, EditorIntegrator* editor, bool lastIsConstIdentifier) +{ + QualifiedIdentifier id; + if (node->isGlobal != -1) { + id.setExplicitlyGlobal(true); + } + const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front(); + do { + if (lastIsConstIdentifier && !it->hasNext()) { + id.push(Identifier(editor->parseSession()->symbol(it->element))); + } else { + id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); + } + } while (it->hasNext() && (it = it->next)); + return id; +} + +QualifiedIdentifier identifierForNamespace( + NamespacedIdentifierBeforeGroupedNamespaceAst* prefixNode, + InnerUseNamespaceAst* node, + EditorIntegrator* editor, + bool lastIsConstIdentifier) +{ + QualifiedIdentifier id; + if (prefixNode->isGlobal != -1) { + id.setExplicitlyGlobal(true); + } + const KDevPG::ListNode< IdentifierAst* >* it; + + it = prefixNode->namespaceNameSequence->front(); + do { + id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); + } + while (it->hasNext() && (it = it->next)); + it = node->namespaceNameSequence->front(); + do { + if (lastIsConstIdentifier && !it->hasNext()) { + id.push(Identifier(editor->parseSession()->symbol(it->element))); + } else { + id.push(Identifier(editor->parseSession()->symbol(it->element).toLower())); + } + } while (it->hasNext() && (it = it->next)); + return id; +} + QualifiedIdentifier identifierWithNamespace(const QualifiedIdentifier& base, DUContext* context) { DUChainReadLocker lock; diff --git a/parser/php.g b/parser/php.g --- a/parser/php.g +++ b/parser/php.g @@ -138,6 +138,13 @@ OperationSr, OperationSpaceship, }; + + enum UseImportType { + NamespaceOrClassImport, + ConstantImport, + FunctionImport + }; + :] ------------------------------------------------------------ @@ -182,6 +189,13 @@ DefaultState = 1 }; + enum ParserUseImportType { + NamespaceOrClassImport, + ConstantImport, + FunctionImport + }; + ParserUseImportType m_useImportType; + ParserUseImportType m_useCompoundImportType; :] %parserclass (private declaration) @@ -728,11 +742,51 @@ | gotoTarget=STRING COLON -> statement ;; - ( useFunction=FUNCTION | useConst=CONST | 0 ) #useNamespace=useNamespace @ COMMA SEMICOLON + ( useFunction=FUNCTION [: m_useImportType = FunctionImport; :] + | useConst=CONST [: m_useImportType = ConstantImport; :] + | 0 [: m_useImportType = NamespaceOrClassImport; :] + ) #useNamespace=useNamespaceOrUseGroupedNamespace @ COMMA SEMICOLON -> useStatement ;; - identifier=namespacedIdentifier (AS aliasIdentifier=identifier | 0) --> useNamespace ;; + [: (*yynode)->useImportType = (UseImportType)(int)m_useImportType; :] + identifier=namespacedIdentifierBeforeGroupedNamespace + (AS aliasIdentifier=identifier | compoundNamespace=compoundNamespace | 0) +-> useNamespaceOrUseGroupedNamespace [ + member variable useImportType: UseImportType; +] ;; + + LBRACE + #compoundNamespaceStatement=compoundNamespaceStatement + -- break because "use Foo\{bar1,}" is allowed (solves FIRST/FOLLOW conflict) + @ (COMMA [: if (yytoken == Token_RBRACE) { break; } :] ) + RBRACE +-> compoundNamespace ;; + + ( useFunction=FUNCTION [: m_useCompoundImportType = FunctionImport; :] + | useConst=CONST [: m_useCompoundImportType = ConstantImport; :] + | 0 [: m_useCompoundImportType = NamespaceOrClassImport; :] + ) + [: + if (m_useImportType != NamespaceOrClassImport && m_useCompoundImportType != NamespaceOrClassImport) + { + reportProblem(Error, QStringLiteral("Can't use mixed import."), -2); + } + :] + #useNamespace=innerUseNamespace +-> compoundNamespaceStatement ;; + + [: + if (m_useCompoundImportType != NamespaceOrClassImport) { + (*yynode)->useImportType = (UseImportType)(int)m_useCompoundImportType; + } else { + (*yynode)->useImportType = (UseImportType)(int)m_useImportType; + } + :] + #namespaceName=identifier+ @ BACKSLASH + (AS aliasIdentifier=identifier | 0) +-> innerUseNamespace [ + member variable useImportType: UseImportType; +] ;; identifier=identifier ASSIGN scalar=expr -> constantDeclaration ;; @@ -939,6 +993,10 @@ #namespaceName=identifier+ @ BACKSLASH -> namespacedIdentifier ;; + (isGlobal=BACKSLASH | 0) + #namespaceName=identifier+ @ ( BACKSLASH [: if (yytoken == Token_LBRACE) { break; } :] ) +-> namespacedIdentifierBeforeGroupedNamespace ;; + string=STRING -> identifier ;;