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

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 "contextbuilder.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 #include <language/duchain/duchain.h>
0012 #include <language/duchain/topducontext.h>
0013 #include <language/duchain/duchainlock.h>
0014 #include <language/duchain/declaration.h>
0015 #include <language/duchain/classdeclaration.h>
0016 #include <language/editor/documentrange.h>
0017 
0018 #include <interfaces/icore.h>
0019 #include <interfaces/ilanguagecontroller.h>
0020 #include <interfaces/icompletionsettings.h>
0021 
0022 #include "../editorintegrator.h"
0023 #include "../helper.h"
0024 #include "../phpducontext.h"
0025 
0026 #include "../parser/parsesession.h"
0027 #include "../parser/phpast.h"
0028 #include <duchaindebug.h>
0029 
0030 using namespace KDevelop;
0031 
0032 namespace Php
0033 {
0034 
0035 ContextBuilder::ContextBuilder()
0036     : m_isInternalFunctions(false), m_reportErrors(true),
0037       m_mapAst(false), m_hadUnresolvedIdentifiers(false),
0038       m_editor(nullptr), m_openNamespaces(nullptr)
0039 {
0040 }
0041 
0042 ContextBuilder::~ContextBuilder()
0043 {
0044 }
0045 
0046 EditorIntegrator* ContextBuilder::editor() const
0047 {
0048     return m_editor;
0049 }
0050 
0051 ReferencedTopDUContext ContextBuilder::build(const IndexedString& url, AstNode* node,
0052                                              const ReferencedTopDUContext& updateContext_)
0053 {
0054     ReferencedTopDUContext updateContext(updateContext_);
0055     m_isInternalFunctions = url == internalFunctionFile();
0056     if ( m_isInternalFunctions ) {
0057         m_reportErrors = false;
0058     } else if ( ICore::self() ) {
0059         m_reportErrors = ICore::self()->languageController()->completionSettings()->highlightSemanticProblems();
0060     }
0061 
0062     if (!updateContext) {
0063         DUChainReadLocker lock(DUChain::lock());
0064         updateContext = DUChain::self()->chainForDocument(url);
0065     }
0066     if (updateContext) {
0067         qCDebug(DUCHAIN) << "re-compiling" << url.str();
0068         DUChainWriteLocker lock(DUChain::lock());
0069         updateContext->clearImportedParentContexts();
0070         updateContext->parsingEnvironmentFile()->clearModificationRevisions();
0071         updateContext->clearProblems();
0072         updateContext->updateImportsCache();
0073     } else {
0074         qCDebug(DUCHAIN) << "compiling" << url.str();
0075     }
0076     ReferencedTopDUContext top = ContextBuilderBase::build(url, node, updateContext);
0077 
0078     {
0079         DUChainWriteLocker lock(DUChain::lock());
0080         top->updateImportsCache();
0081     }
0082 
0083     return top;
0084 }
0085 
0086 bool ContextBuilder::hadUnresolvedIdentifiers() const
0087 {
0088     return m_hadUnresolvedIdentifiers;
0089 }
0090 
0091 
0092 void ContextBuilder::startVisiting(AstNode* node)
0093 {
0094     if (compilingContexts()) {
0095         TopDUContext* top = dynamic_cast<TopDUContext*>(currentContext());
0096         Q_ASSERT(top);
0097         {
0098             DUChainWriteLocker lock(DUChain::lock());
0099             top->updateImportsCache(); //Mark that we will use a cached import-structure
0100         }
0101 
0102         bool hasImports;
0103         {
0104             DUChainReadLocker lock(DUChain::lock());
0105             hasImports = !top->importedParentContexts().isEmpty();
0106         }
0107         if (!hasImports && top->url() != internalFunctionFile()) {
0108             DUChainWriteLocker lock(DUChain::lock());
0109             TopDUContext* import = DUChain::self()->chainForDocument(internalFunctionFile());
0110             if (!import) {
0111                 qWarning() << "importing internalFunctions failed" << currentContext()->url().str();
0112                 Q_ASSERT(false);
0113             } else {
0114                 top->addImportedParentContext(import);
0115                 top->updateImportsCache();
0116             }
0117         }
0118 
0119     }
0120     visitNode(node);
0121     if (m_openNamespaces) {
0122         closeNamespaces(m_openNamespaces);
0123         m_openNamespaces = nullptr;
0124     }
0125 }
0126 
0127 DUContext* ContextBuilder::newContext(const RangeInRevision& range)
0128 {
0129     return new PhpDUContext<DUContext>(range, currentContext());
0130 }
0131 
0132 TopDUContext* ContextBuilder::newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file)
0133 {
0134     if (!file) {
0135         file = new ParsingEnvironmentFile(m_editor->parseSession()->currentDocument());
0136         /// Indexed string for 'Php', identifies environment files from this language plugin
0137         static const IndexedString phpLangString("Php");
0138         file->setLanguage(phpLangString);
0139     }
0140     TopDUContext* ret = new PhpDUContext<TopDUContext>(m_editor->parseSession()->currentDocument(), range, file);
0141     ret->setType(DUContext::Global);
0142     return ret;
0143 }
0144 
0145 void ContextBuilder::setContextOnNode(AstNode* node, DUContext* ctx)
0146 {
0147     node->ducontext = ctx;
0148 }
0149 
0150 DUContext* ContextBuilder::contextFromNode(AstNode* node)
0151 {
0152     return node->ducontext;
0153 }
0154 
0155 RangeInRevision ContextBuilder::editorFindRange(AstNode* fromRange, AstNode* toRange)
0156 {
0157     return m_editor->findRange(fromRange, toRange ? toRange : fromRange);
0158 }
0159 
0160 CursorInRevision ContextBuilder::startPos(AstNode* node)
0161 {
0162     return m_editor->findPosition(node->startToken, EditorIntegrator::FrontEdge);
0163 }
0164 
0165 QualifiedIdentifier ContextBuilder::identifierForNode(IdentifierAst* id)
0166 {
0167     if (!id)
0168         return QualifiedIdentifier();
0169 
0170     return QualifiedIdentifier(stringForNode(id));
0171 }
0172 
0173 QualifiedIdentifier ContextBuilder::identifierForNode(SemiReservedIdentifierAst* id)
0174 {
0175     if (!id)
0176         return QualifiedIdentifier();
0177 
0178     return QualifiedIdentifier(stringForNode(id));
0179 }
0180 
0181 QualifiedIdentifier ContextBuilder::identifierForNode(VariableIdentifierAst* id)
0182 {
0183     if (!id)
0184         return QualifiedIdentifier();
0185     QString ret(stringForNode(id));
0186     ret = ret.mid(1); //cut off $
0187     return QualifiedIdentifier(ret);
0188 }
0189 
0190 IdentifierPair ContextBuilder::identifierPairForNode(IdentifierAst* id, bool isConstIdentifier)
0191 {
0192     if (!id) {
0193         return qMakePair(IndexedString(), QualifiedIdentifier());
0194     }
0195     const QString ret = stringForNode(id);
0196 
0197     if ( isConstIdentifier ) {
0198         return qMakePair(IndexedString(ret), QualifiedIdentifier(ret));
0199     } else {
0200         return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower()));
0201     }
0202 }
0203 
0204 IdentifierPair ContextBuilder::identifierPairForNode(SemiReservedIdentifierAst* id )
0205 {
0206     if (!id) {
0207         return qMakePair(IndexedString(), QualifiedIdentifier());
0208     }
0209     const QString ret = stringForNode(id);
0210 
0211     return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower()));
0212 }
0213 
0214 IdentifierPair ContextBuilder::identifierPairForNode(ReservedNonModifierIdentifierAst* id )
0215 {
0216     if (!id) {
0217         return qMakePair(IndexedString(), QualifiedIdentifier());
0218     }
0219     const QString ret = stringForNode(id);
0220 
0221     return qMakePair(IndexedString(ret), QualifiedIdentifier(ret.toLower()));
0222 }
0223 
0224 QString ContextBuilder::stringForNode(IdentifierAst* node) const
0225 {
0226     return m_editor->parseSession()->symbol(node->string);
0227 }
0228 
0229 QString ContextBuilder::stringForNode(SemiReservedIdentifierAst* node) const
0230 {
0231     return m_editor->parseSession()->symbol(node->string);
0232 }
0233 
0234 QString ContextBuilder::stringForNode(ReservedNonModifierIdentifierAst* node) const
0235 {
0236     return m_editor->parseSession()->symbol(node->string);
0237 }
0238 
0239 QString ContextBuilder::stringForNode(VariableIdentifierAst* node) const
0240 {
0241     return m_editor->parseSession()->symbol(node->variable);
0242 }
0243 
0244 void ContextBuilder::visitClassDeclarationStatement(ClassDeclarationStatementAst* node)
0245 {
0246     openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->className).second);
0247     classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration())
0248     DefaultVisitor::visitClassDeclarationStatement(node);
0249     closeContext();
0250 }
0251 
0252 void ContextBuilder::classContextOpened(DUContext* context)
0253 {
0254     Q_UNUSED(context);
0255 }
0256 
0257 void ContextBuilder::visitInterfaceDeclarationStatement(InterfaceDeclarationStatementAst* node)
0258 {
0259     openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->interfaceName).second);
0260     classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration())
0261     DefaultVisitor::visitInterfaceDeclarationStatement(node);
0262     closeContext();
0263 }
0264 
0265 void ContextBuilder::visitTraitDeclarationStatement(TraitDeclarationStatementAst* node)
0266 {
0267     openContext(node, editorFindRange(node, node), DUContext::Class, identifierPairForNode(node->traitName).second);
0268     classContextOpened(currentContext()); //This callback is needed, so we can set the internal context and so find the declaration for the context (before closeDeclaration())
0269     DefaultVisitor::visitTraitDeclarationStatement(node);
0270     closeContext();
0271 }
0272 
0273 void ContextBuilder::visitClassStatement(ClassStatementAst *node)
0274 {
0275     visitOptionalModifiers(node->modifiers);
0276     if (node->methodName) {
0277         //method declaration
0278         DUContext* parameters = openContext(node->parameters, DUContext::Function, identifierForNode(node->methodName));
0279         Q_ASSERT(!parameters->inSymbolTable());
0280 
0281         visitParameterList(node->parameters);
0282         if (node->returnType) {
0283             visitReturnType(node->returnType);
0284         }
0285         closeContext();
0286 
0287         if ( !m_isInternalFunctions && node->methodBody ) {
0288             // the internal functions file has only empty method bodies, so skip them
0289             DUContext* body = openContext(node->methodBody, DUContext::Other, identifierForNode(node->methodName));
0290             if (compilingContexts()) {
0291                 DUChainWriteLocker lock(DUChain::lock());
0292                 body->addImportedParentContext(parameters);
0293                 body->setInSymbolTable(false);
0294             }
0295             visitMethodBody(node->methodBody);
0296             closeContext();
0297         }
0298     } else {
0299         //member-variable or const
0300         DefaultVisitor::visitClassStatement(node);
0301     }
0302 }
0303 
0304 void ContextBuilder::visitFunctionDeclarationStatement(FunctionDeclarationStatementAst* node)
0305 {
0306     visitIdentifier(node->functionName);
0307 
0308     DUContext* parameters = openContext(node->parameters, DUContext::Function, node->functionName);
0309     Q_ASSERT(!parameters->inSymbolTable());
0310 
0311     visitParameterList(node->parameters);
0312     if (node->returnType) {
0313         visitReturnType(node->returnType);
0314     }
0315     closeContext();
0316 
0317     if ( !m_isInternalFunctions && node->functionBody ) {
0318         // the internal functions file has only empty method bodies, so skip them
0319         DUContext* body = openContext(node->functionBody, DUContext::Other, node->functionName);
0320         if (compilingContexts()) {
0321             DUChainWriteLocker lock(DUChain::lock());
0322             body->addImportedParentContext(parameters);
0323             body->setInSymbolTable(false);
0324         }
0325         visitInnerStatementList(node->functionBody);
0326         closeContext();
0327     }
0328 }
0329 
0330 void ContextBuilder::visitClosure(ClosureAst* node)
0331 {
0332     DUContext* parameters = openContext(node->parameters, DUContext::Function);
0333     Q_ASSERT(!parameters->inSymbolTable());
0334 
0335     visitParameterList(node->parameters);
0336     if (node->returnType) {
0337         visitReturnType(node->returnType);
0338     }
0339     closeContext();
0340 
0341     DUContext* imported = nullptr;
0342     if ( node->lexicalVars ) {
0343         imported = openContext(node->lexicalVars, DUContext::Other);
0344         Q_ASSERT(!imported->inSymbolTable());
0345 
0346         visitLexicalVarList(node->lexicalVars);
0347         closeContext();
0348     }
0349 
0350     if ( !m_isInternalFunctions && node->functionBody ) {
0351         // the internal functions file has only empty method bodies, so skip them
0352         DUContext* body = openContext(node->functionBody, DUContext::Other);
0353         if (compilingContexts()) {
0354             DUChainWriteLocker lock;
0355             body->addImportedParentContext(parameters);
0356             if (imported) {
0357                 body->addImportedParentContext(imported, CursorInRevision::invalid(), true);
0358             }
0359             body->setInSymbolTable(false);
0360         }
0361         visitInnerStatementList(node->functionBody);
0362         closeContext();
0363     }
0364 }
0365 
0366 void ContextBuilder::visitNamespaceDeclarationStatement(NamespaceDeclarationStatementAst* node)
0367 {
0368     // close existing namespace context
0369     if (m_openNamespaces) {
0370         closeNamespaces(m_openNamespaces);
0371         m_openNamespaces = nullptr;
0372     }
0373 
0374     if ( !node->namespaceNameSequence ) {
0375         if (node->body) {
0376             // global namespace
0377             DefaultVisitor::visitInnerStatementList(node->body);
0378         }
0379         return;
0380     }
0381 
0382     { // open
0383     ///TODO: support \ as separator
0384 
0385     RangeInRevision bodyRange;
0386     if (node->body) {
0387         bodyRange = editorFindRange(node->body, node->body);
0388     } else {
0389         bodyRange = RangeInRevision(m_editor->findPosition(node->endToken), currentContext()->topContext()->range().end);
0390     }
0391     const KDevPG::ListNode< IdentifierAst* >* it = node->namespaceNameSequence->front();
0392     do {
0393         openNamespace(node, it->element, identifierPairForNode(it->element), bodyRange);
0394     } while(it->hasNext() && (it = it->next));
0395     }
0396 
0397     if (node->body) {
0398         DefaultVisitor::visitInnerStatementList(node->body);
0399         closeNamespaces(node);
0400     } else {
0401         m_openNamespaces = node;
0402     }
0403 }
0404 
0405 void ContextBuilder::closeNamespaces(NamespaceDeclarationStatementAst* namespaces)
0406 {
0407     ///TODO: support \ as separator
0408     const KDevPG::ListNode< IdentifierAst* >* it = namespaces->namespaceNameSequence->front();
0409     do {
0410         Q_ASSERT(currentContext()->type() == DUContext::Namespace);
0411         closeNamespace(namespaces, it->element, identifierPairForNode(it->element));
0412     } while(it->hasNext() && (it = it->next));
0413 }
0414 
0415 void ContextBuilder::openNamespace(NamespaceDeclarationStatementAst* parent, IdentifierAst* node, const IdentifierPair& identifier, const RangeInRevision& range)
0416 {
0417     if ( node == parent->namespaceNameSequence->back()->element ) {
0418         openContext(node, range, DUContext::Namespace, identifier.second);
0419     } else {
0420         openContext(node, range, DUContext::Namespace, identifier.second);
0421     }
0422 }
0423 
0424 void ContextBuilder::closeNamespace(NamespaceDeclarationStatementAst* /*parent*/, IdentifierAst* /*node*/, const IdentifierPair& /*identifier*/)
0425 {
0426     closeContext();
0427 }
0428 
0429 void ContextBuilder::addBaseType(NamespacedIdentifierAst * identifier)
0430 {
0431     DUChainWriteLocker lock(DUChain::lock());
0432 
0433     Q_ASSERT(currentContext()->type() == DUContext::Class);
0434 
0435     ClassDeclaration* currentClass = dynamic_cast<ClassDeclaration*>(currentContext()->owner());
0436 
0437     ClassDeclaration* baseClass = dynamic_cast<ClassDeclaration*>(
0438         findDeclarationImport(ClassDeclarationType, identifierForNamespace(identifier, m_editor)).data() );
0439 
0440     if (currentClass && baseClass) {
0441         if (DUContext* baseContext = baseClass->logicalInternalContext(nullptr)) {
0442             // prevent circular context imports which could lead to segfaults
0443             if (!baseContext->imports(currentContext()) && !currentContext()->imports(baseContext)) {
0444                 currentContext()->addImportedParentContext(baseContext);
0445                 BaseClassInstance base;
0446                 base.baseClass = baseClass->indexedType();
0447                 base.access = Declaration::Public;
0448                 base.virtualInheritance = false;
0449                 currentClass->addBaseClass(base);
0450             } else if (m_reportErrors && baseClass->classType() != ClassDeclarationData::Interface) {
0451                 reportError(i18n("Circular inheritance of %1 and %2", currentClass->toString(), baseClass->toString()), identifier);
0452             }
0453         }
0454     }
0455     if (!baseClass) {
0456         qCDebug(DUCHAIN) << "unresolved identifier";
0457         m_hadUnresolvedIdentifiers = true;
0458     }
0459 }
0460 
0461 
0462 void ContextBuilder::visitUnaryExpression(UnaryExpressionAst* node)
0463 {
0464     DefaultVisitor::visitUnaryExpression(node);
0465     if (!compilingContexts()) {
0466         return;
0467     }
0468     IndexedString includeFile = getIncludeFileForNode(node, m_editor);
0469 
0470     if ( !includeFile.isEmpty() ) {
0471         DUChainWriteLocker lock(DUChain::lock());
0472         TopDUContext *top = DUChain::self()->chainForDocument(includeFile);
0473         if (top) {
0474             currentContext()->topContext()->addImportedParentContext(top);
0475             currentContext()->topContext()->parsingEnvironmentFile()
0476             ->addModificationRevisions(top->parsingEnvironmentFile()->allModificationRevisions());
0477         }
0478     }
0479 }
0480 
0481 void ContextBuilder::visitFunctionCallParameterListElement(FunctionCallParameterListElementAst* node)
0482 {
0483     DefaultVisitor::visitFunctionCallParameterListElement(node);
0484 
0485     setContextOnNode(node, currentContext());
0486 }
0487 
0488 void ContextBuilder::reportError(const QString& errorMsg, AstNode* node, IProblem::Severity severity)
0489 {
0490     reportError(errorMsg, m_editor->findRange(node), severity);
0491 }
0492 
0493 void ContextBuilder::reportError(const QString& errorMsg, QList< AstNode* > nodes, IProblem::Severity severity)
0494 {
0495     RangeInRevision range = RangeInRevision::invalid();
0496     foreach ( AstNode* node, nodes ) {
0497         if ( !range.isValid() ) {
0498             range = m_editor->findRange(node);
0499         } else {
0500             range.end = m_editor->findPosition(node->endToken);
0501         }
0502     }
0503     reportError(errorMsg, range, severity);
0504 }
0505 
0506 void ContextBuilder::reportError(const QString& errorMsg, RangeInRevision range, IProblem::Severity severity)
0507 {
0508     auto *p = new Problem();
0509     p->setSeverity(severity);
0510     p->setSource(IProblem::DUChainBuilder);
0511     p->setDescription(errorMsg);
0512     p->setFinalLocation(DocumentRange(m_editor->parseSession()->currentDocument(),
0513                                                 range.castToSimpleRange()));
0514     {
0515         DUChainWriteLocker lock(DUChain::lock());
0516         qCDebug(DUCHAIN) << "Problem" << p->description() << p->finalLocation();
0517         currentContext()->topContext()->addProblem(ProblemPointer(p));
0518     }
0519 }
0520 
0521 DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType,
0522                                                          IdentifierAst* node)
0523 {
0524     QualifiedIdentifier id;
0525     if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) {
0526         id = identifierPairForNode(node).second;
0527     } else {
0528         id = identifierForNode(node);
0529     }
0530     return findDeclarationImportHelper(currentContext(), id, declarationType);
0531 }
0532 
0533 DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType,
0534                                                          SemiReservedIdentifierAst* node,
0535                                                          DeclarationScope declarationScope)
0536 {
0537     QualifiedIdentifier id;
0538     if ( declarationType == ClassDeclarationType || declarationType == FunctionDeclarationType ) {
0539         id = identifierPairForNode(node).second;
0540     } else {
0541         id = identifierForNode(node);
0542     }
0543 
0544     if (declarationScope == GlobalScope) {
0545         id.setExplicitlyGlobal(true);
0546     }
0547 
0548     return findDeclarationImportHelper(currentContext(), id, declarationType);
0549 }
0550 
0551 DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType,
0552                                                          VariableIdentifierAst* node)
0553 {
0554     return findDeclarationImportHelper(currentContext(), identifierForNode(node), declarationType);
0555 }
0556 
0557 DeclarationPointer ContextBuilder::findDeclarationImport(DeclarationType declarationType,
0558                                                          const QualifiedIdentifier &identifier)
0559 {
0560     return findDeclarationImportHelper(currentContext(), identifier, declarationType);
0561 }
0562 
0563 }