File indexing completed on 2024-05-05 16:41:07

0001 /*
0002     SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "usebuilder.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 #include "editorintegrator.h"
0012 #include "expressionvisitor.h"
0013 #include "parsesession.h"
0014 #include <duchaindebug.h>
0015 
0016 using namespace KDevelop;
0017 
0018 namespace Php
0019 {
0020 
0021 class UseExpressionVisitor : public ExpressionVisitor
0022 {
0023 public:
0024     UseExpressionVisitor(EditorIntegrator* editor, UseBuilder* useBuilder)
0025             : ExpressionVisitor(editor), m_builder(useBuilder) {
0026     }
0027 
0028 protected:
0029     void usingDeclaration(AstNode* node, const DeclarationPointer& decl) override {
0030         m_builder->newCheckedUse(node, decl);
0031     }
0032 
0033 private:
0034     UseBuilder* m_builder;
0035 };
0036 
0037 UseBuilder::UseBuilder( EditorIntegrator* editor )
0038 {
0039     m_editor = editor;
0040 }
0041 
0042 ReferencedTopDUContext UseBuilder::build ( const IndexedString& url, AstNode* node, const ReferencedTopDUContext& updateContext )
0043 {
0044     // just for safety purposes: running the UseBuilder on the internal function file
0045     // will lead to undefined behavior due to the amount of optimization it has received
0046     // (esp. in the contextbuilder)
0047     Q_ASSERT(url != internalFunctionFile());
0048     return UseBuilderBase::build ( url, node, updateContext );
0049 }
0050 
0051 void UseBuilder::visitParameter(ParameterAst *node)
0052 {
0053     if (node->parameterType) {
0054         visitParameterType(node->parameterType);
0055     }
0056     if (node->defaultValue) {
0057         visitNodeWithExprVisitor(node->defaultValue);
0058     }
0059 }
0060 
0061 void UseBuilder::visitClassImplements(ClassImplementsAst *node)
0062 {
0063     if (node->implementsSequence) {
0064         const KDevPG::ListNode<NamespacedIdentifierAst*> *__it = node->implementsSequence->front(), *__end = __it;
0065         do {
0066             buildNamespaceUses(__it->element);
0067             __it = __it->next;
0068         } while (__it != __end);
0069     }
0070 }
0071 
0072 void UseBuilder::visitClassExtends(ClassExtendsAst *node)
0073 {
0074     buildNamespaceUses(node->identifier);
0075 }
0076 
0077 void UseBuilder::visitClassStatement(ClassStatementAst *node)
0078 {
0079      if (!node->traitsSequence) {
0080         UseBuilderBase::visitClassStatement(node);
0081         return;
0082      }
0083 
0084     const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->traitsSequence->front();
0085     forever {
0086         buildNamespaceUses(it->element, ClassDeclarationType);
0087 
0088         if ( it->hasNext() ) {
0089             it = it->next;
0090         } else {
0091             break;
0092         }
0093     }
0094 
0095     if (node->imports) {
0096         visitTraitAliasDeclaration(node->imports);
0097     }
0098 
0099     UseBuilderBase::visitClassStatement(node);
0100 }
0101 
0102 void UseBuilder::visitTraitAliasStatement(TraitAliasStatementAst *node)
0103 {
0104     if (node->conflictIdentifierSequence) {
0105         const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->conflictIdentifierSequence->front();
0106         forever {
0107             buildNamespaceUses(it->element, ClassDeclarationType);
0108 
0109             if ( it->hasNext() ) {
0110                 it = it->next;
0111             } else {
0112                 break;
0113             }
0114         }
0115     }
0116 
0117     DUChainWriteLocker lock;
0118     DeclarationPointer dec = findDeclarationImport(ClassDeclarationType, identifierForNamespace(node->importIdentifier->identifier, m_editor));
0119 
0120     if (dec) {
0121         QualifiedIdentifier original = identifierPairForNode(node->importIdentifier->methodIdentifier).second;
0122         QList <Declaration*> list = dec.data()->internalContext()->findLocalDeclarations(original.last(), dec.data()->internalContext()->range().start);
0123 
0124         if (!list.isEmpty()) {
0125             UseBuilderBase::newUse(node->importIdentifier->methodIdentifier, DeclarationPointer(list.first()));
0126         }
0127     }
0128 
0129     lock.unlock();
0130 
0131     visitTraitAliasIdentifier(node->importIdentifier);
0132 }
0133 
0134 void UseBuilder::visitTraitAliasIdentifier(TraitAliasIdentifierAst *node)
0135 {
0136     buildNamespaceUses(node->identifier, ClassDeclarationType);
0137 }
0138 
0139 void UseBuilder::visitExpr(ExprAst* node)
0140 {
0141     visitNodeWithExprVisitor(node);
0142 }
0143 
0144 void UseBuilder::visitGlobalVar(GlobalVarAst* node)
0145 {
0146     if (node->var) {
0147         DeclarationPointer dec = findDeclarationImport(GlobalVariableDeclarationType, node->var);
0148         if (dec) {
0149             newCheckedUse(node->var, dec);
0150         }
0151     }
0152 }
0153 
0154 void UseBuilder::visitStaticScalar(StaticScalarAst* node)
0155 {
0156     if (currentContext()->type() == DUContext::Class) {
0157         visitNodeWithExprVisitor(node);
0158     }
0159 }
0160 
0161 void UseBuilder::visitStatement(StatementAst *node)
0162 {
0163     if (node->foreachVar) {
0164         visitNodeWithExprVisitor(node->foreachVar);
0165     } else if (node->unsetVariablesSequence) {
0166         visitNodeWithExprVisitor(node);
0167     }
0168 
0169     if (node->foreachExprAsVar) {
0170         visitNodeWithExprVisitor(node->foreachExprAsVar);
0171     }
0172     if (node->foreachVarAsVar) {
0173         visitNodeWithExprVisitor(node->foreachVarAsVar);
0174     }
0175     if (node->foreachVariable) {
0176         visitNodeWithExprVisitor(node->foreachVariable);
0177     }
0178 
0179     UseBuilderBase::visitStatement(node);
0180 }
0181 
0182 void UseBuilder::visitCatchItem(CatchItemAst *node)
0183 {
0184     if (node->catchClassSequence) {
0185         const KDevPG::ListNode< NamespacedIdentifierAst* >* it = node->catchClassSequence->front();
0186         forever {
0187             buildNamespaceUses(it->element, ClassDeclarationType);
0188 
0189             if ( it->hasNext() ) {
0190                 it = it->next;
0191             } else {
0192                 break;
0193             }
0194         }
0195     }
0196     UseBuilderBase::visitCatchItem(node);
0197 }
0198 
0199 void UseBuilder::newCheckedUse(AstNode* node, const DeclarationPointer& declaration, bool reportNotFound)
0200 {
0201     if ( declaration && declaration->comment().contains("@deprecated") ) {
0202         reportError(i18n("Usage of %1 is deprecated.", declaration->toString()), node, IProblem::Hint);
0203     } else if ( !declaration && reportNotFound ) {
0204         reportError(i18n("Declaration not found: %1", m_editor->parseSession()->symbol(node)), node, IProblem::Hint);
0205     }
0206     UseBuilderBase::newUse(node, declaration);
0207 }
0208 
0209 void UseBuilder::visitUnaryExpression( UnaryExpressionAst* node )
0210 {
0211     IndexedString includeFile = getIncludeFileForNode(node, m_editor);
0212     if ( !includeFile.isEmpty() ) {
0213         QualifiedIdentifier identifier(includeFile.str());
0214 
0215         DUChainWriteLocker lock(DUChain::lock());
0216         foreach ( Declaration* dec, currentContext()->topContext()->findDeclarations(identifier) ) {
0217             if ( dec->kind() == Declaration::Import ) {
0218                 newUse(node->includeExpression, DeclarationPointer(dec));
0219                 return;
0220             }
0221         }
0222     }
0223 }
0224 
0225 void UseBuilder::visitUseNamespaceOrUseGroupedNamespace(UseNamespaceOrUseGroupedNamespaceAst* node)
0226 {
0227     if (node->compoundNamespace) {
0228         QualifiedIdentifier identifier = identifierForNamespace(node->identifier, m_editor, false);
0229         buildNamespaceUses(
0230             identifier,
0231             nullptr,
0232             node->identifier->namespaceNameSequence,
0233             NamespaceDeclarationType);
0234         m_compoundNamespacePrefix = node->identifier;
0235         visitCompoundNamespace(node->compoundNamespace);
0236     } else {
0237         buildNamespaceUses(node->identifier, node->useImportType);
0238     }
0239 }
0240 
0241 void UseBuilder::visitInnerUseNamespace(InnerUseNamespaceAst* node)
0242 {
0243     Php::DeclarationType lastType;
0244     if (node->useImportType == ConstantImport) {
0245         lastType = ConstantDeclarationType;
0246     } else if (node->useImportType == FunctionImport) {
0247         lastType = FunctionDeclarationType;
0248     } else {
0249         lastType = NamespaceDeclarationType;
0250     }
0251 
0252     QualifiedIdentifier identifier = identifierForNamespace(
0253         m_compoundNamespacePrefix,
0254         node,
0255         m_editor,
0256         node->useImportType == ConstantImport);
0257     buildNamespaceUses(
0258         identifier,
0259         m_compoundNamespacePrefix->namespaceNameSequence,
0260         node->namespaceNameSequence,
0261         lastType);
0262 }
0263 
0264 void UseBuilder::visitGenericTypeHint(GenericTypeHintAst* node) {
0265     if (node->genericType && isGenericClassTypehint(node->genericType, m_editor)) {
0266         buildNamespaceUses(node->genericType);
0267     }
0268 }
0269 
0270 void UseBuilder::buildNamespaceUses(NamespacedIdentifierBeforeGroupedNamespaceAst* node, UseImportType useImportType)
0271 {
0272     Php::DeclarationType lastType;
0273     if (useImportType == ConstantImport) {
0274         lastType = ConstantDeclarationType;
0275     } else if (useImportType == FunctionImport) {
0276         lastType = FunctionDeclarationType;
0277     } else {
0278         lastType = NamespaceDeclarationType;
0279     }
0280 
0281     QualifiedIdentifier identifier = identifierForNamespace(node, m_editor, useImportType == ConstantImport);
0282     buildNamespaceUses(identifier, nullptr, node->namespaceNameSequence, lastType);
0283 }
0284 
0285 void UseBuilder::buildNamespaceUses(NamespacedIdentifierAst* node, DeclarationType lastType)
0286 {
0287     QualifiedIdentifier identifier = identifierForNamespace(node, m_editor, lastType == ConstantDeclarationType);
0288     buildNamespaceUses(identifier, nullptr, node->namespaceNameSequence, lastType);
0289 }
0290 
0291 void UseBuilder::buildNamespaceUses(
0292     KDevelop::QualifiedIdentifier identifier,
0293     const KDevPG::ListNode<IdentifierAst *>* prefixNamespaceNameSequence,
0294     const KDevPG::ListNode<IdentifierAst *>* namespaceNameSequence,
0295     Php::DeclarationType lastType)
0296 {
0297     QualifiedIdentifier curId;
0298 
0299     // check if we need to resolve the namespaced identifier globally or locally
0300     DeclarationPointer tempDec = findDeclarationImport(lastType, identifier);
0301 
0302     // if we couldn't find a class declaration, it might be a partial namespace identifier
0303     if (!tempDec) {
0304         tempDec = findDeclarationImport(NamespaceDeclarationType, identifier);
0305     }
0306 
0307     if (!tempDec && !identifier.explicitlyGlobal()) {
0308         identifier.setExplicitlyGlobal(true);
0309         tempDec = findDeclarationImport(lastType, identifier);
0310 
0311         if (!tempDec) {
0312             tempDec = findDeclarationImport(NamespaceDeclarationType, identifier);
0313         }
0314 
0315         // Can't resolve either globally or locally, so revert back to original
0316         if (!tempDec) {
0317             identifier.setExplicitlyGlobal(false);
0318         }
0319     }
0320 
0321     curId.setExplicitlyGlobal(identifier.explicitlyGlobal());
0322     int prefixCount = prefixNamespaceNameSequence == nullptr ? 0 : prefixNamespaceNameSequence->count();
0323     Q_ASSERT(identifier.count() == prefixCount + namespaceNameSequence->count());
0324     for ( int i = 0; i < identifier.count() - 1; ++i ) {
0325         curId.push(identifier.at(i));
0326         if (i>=prefixCount) {
0327             AstNode* n = namespaceNameSequence->at(i)->element;
0328             DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, curId);
0329             if (!dec || dec->range() != editorFindRange(n, n)) {
0330                 newCheckedUse(n, dec, true);
0331             }
0332         }
0333     }
0334     bool reportNotFound = lastType == ClassDeclarationType
0335         || lastType == ConstantDeclarationType
0336         || lastType == FunctionDeclarationType
0337         || lastType == NamespaceDeclarationType;
0338     newCheckedUse(namespaceNameSequence->back()->element, findDeclarationImport(lastType, identifier), reportNotFound);
0339 }
0340 
0341 void UseBuilder::openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node,
0342                                const IdentifierPair& identifier, const RangeInRevision& range)
0343 {
0344     if (node != parent->namespaceNameSequence->back()->element) {
0345         DeclarationPointer dec = findDeclarationImport(NamespaceDeclarationType, identifier.second);
0346         if (!dec || dec->range() != editorFindRange(node, node)) {
0347             newCheckedUse(node, dec);
0348         }
0349     }
0350     UseBuilderBase::openNamespace(parent, node, identifier, range);
0351 }
0352 
0353 void UseBuilder::visitNodeWithExprVisitor(AstNode* node)
0354 {
0355     UseExpressionVisitor v(m_editor, this);
0356     node->ducontext = currentContext();
0357     v.visitNode(node);
0358 
0359     if (v.result().hadUnresolvedIdentifiers()) {
0360         m_hadUnresolvedIdentifiers = true;
0361     }
0362 }
0363 
0364 }