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 }