File indexing completed on 2024-05-05 16:42:26

0001 /*
0002     SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
0003     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0004     SPDX-FileCopyrightText: 2010-2014 Sven Brauch <svenbrauch@googlemail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #ifndef PYTHON_CONTEXTBUILDER_H
0010 #define PYTHON_CONTEXTBUILDER_H
0011 
0012 #include "astdefaultvisitor.h"
0013 
0014 #include <language/duchain/builders/abstractcontextbuilder.h>
0015 #include <language/editor/rangeinrevision.h>
0016 #include <language/duchain/topducontext.h>
0017 
0018 #include "pythonduchainexport.h"
0019 
0020 using namespace KDevelop;
0021 
0022 namespace Python
0023 {
0024 
0025 class PythonEditorIntegrator;
0026 class FileIndentInformation;
0027 
0028 typedef KDevelop::AbstractContextBuilder<Ast, Identifier> ContextBuilderBase;
0029 
0030 /**
0031  * @brief The context builder, which calculates the scopes in a file.
0032  *
0033  * For practical reasons, some building of scopes also happens
0034  * in the declaration builder.
0035  */
0036 class KDEVPYTHONDUCHAIN_EXPORT ContextBuilder: public ContextBuilderBase, public Python::AstDefaultVisitor
0037 {
0038 public:
0039     ContextBuilder() = default;
0040 
0041     /**
0042      * @brief Entry function called by KDevPlatform API.
0043      */
0044     ReferencedTopDUContext build(const KDevelop::IndexedString& url, Ast* node,
0045                                  const ReferencedTopDUContext& updateContext = ReferencedTopDUContext()) override;
0046 
0047     /**
0048      * @brief Set the editor integrator.
0049      */
0050     void setEditor(PythonEditorIntegrator* editor);
0051 
0052     /**
0053      * @brief Set the modification revision which will be created by this builder.
0054      */
0055     void setFutureModificationRevision(const ModificationRevision& rev);
0056 
0057     /**
0058      * @brief Get the editor integrator.
0059      */
0060     PythonEditorIntegrator* editor() const;
0061 
0062     /**
0063      * @brief Find the URL which would be imported by the dotted name @p name.
0064      *
0065      * @param name a dotted name, such as PyQt4.QtCore.QWidget
0066      * @param currentDocument the current document, for resolving relative imports
0067      * @return QPair< QUrl, QStringList > the URL if found, and a list of components from
0068      *  the end of the name which were not yet consumed
0069      */
0070     static QPair<QUrl, QStringList> findModulePath(const QString& name, const QUrl& currentDocument);
0071 
0072     /**
0073      * @brief Get the range which encompasses the given @p node.
0074      * @param moveRight true to make the range longer by one character
0075      */
0076     static RangeInRevision rangeForNode(Ast* node, bool moveRight);
0077 
0078     /**
0079      * @brief Get the range of @p identifier.
0080      * @param moveRight true to make the range longer by one character
0081      */
0082     static RangeInRevision rangeForNode(Identifier* identifier, bool moveRight);
0083 
0084     /**
0085      * @brief Find the range of a comprehension.
0086      * @param node Comprehension to find the range of, e.g. a ListComprehensionAst.
0087      */
0088     RangeInRevision comprehensionRange(Ast* node);
0089 
0090     /**
0091      * @brief Calculate the range of the arguments context of the given @p node.
0092      * @return Range the argument list of this function encompasses.
0093      */
0094     RangeInRevision rangeForArgumentsContext(Python::FunctionDefinitionAst* node);
0095 
0096     /**
0097      * @brief Add @p module to the list of unresolved imports in this builder.
0098      */
0099     void addUnresolvedImport(const IndexedString& module);
0100 
0101     /**
0102      * @brief Retrieve a list of imports not resolved by this builder pass.
0103      */
0104     QList<IndexedString> unresolvedImports() const;
0105 
0106 public:
0107     // ugly because this collides with currentDocument(), but we have to use it;
0108     // for some reason the UseBuilder does not have m_url set, and it's private (not even protected) to AbstractContextBuilder.
0109     // so at least keep this consistent within the plugin and use this everywhere.
0110     // maybe we can remove this hack later. TODO maybe change something in kdevplatform, or maybe we're doing something wrong here?
0111     IndexedString currentlyParsedDocument() const;
0112     void setCurrentlyParsedDocument(const IndexedString& document);
0113 
0114 protected:
0115     /**
0116      * @brief Create a new top context and set it as this builder's active context.
0117      *
0118      * @param range Range to encompass
0119      * @return KDevelop::TopDUContext* weak pointer to the created top context.
0120      */
0121     TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file) override;
0122 
0123     /**
0124      * @brief Create a new context.
0125      * Overridden to create instances of Python's specialized DUContext.
0126      */
0127     KDevelop::DUContext* newContext(const KDevelop::RangeInRevision& range) override;
0128 
0129 protected:
0130     // AST visitor functions
0131     void visitLambda(Python::LambdaAst * node) override;
0132     void visitFunctionDefinition( FunctionDefinitionAst* ) override;
0133     void visitClassDefinition( ClassDefinitionAst* ) override;
0134     void visitCode(CodeAst* node) override;
0135     void visitListComprehension(ListComprehensionAst* node) override;
0136     void visitDictionaryComprehension(DictionaryComprehensionAst* node) override;
0137     void visitGeneratorExpression(GeneratorExpressionAst* node) override;
0138     void visitComprehensionCommon(Ast* node);
0139 
0140     void startVisiting(Ast* node) override;
0141     KDevelop::RangeInRevision editorFindRange(Ast* fromNode, Ast* toNode) override;
0142     virtual KDevelop::CursorInRevision editorFindPositionSafe(Ast* node);
0143     virtual KDevelop::CursorInRevision startPos(Ast* node);
0144     KDevelop::QualifiedIdentifier identifierForNode(Identifier* node) override;
0145 
0146     /**
0147      * @brief Set @p context as the context of @p node.
0148      * The context is stored inside the AST itself.
0149      */
0150     void setContextOnNode(Ast* node, KDevelop::DUContext* context) override;
0151 
0152     /**
0153      * @brief Get the context set on @p node as previously set by @ref setContextOnNode.
0154      */
0155     KDevelop::DUContext* contextFromNode(Ast* node) override;
0156 
0157     /**
0158      * @brief Add the saved list of contexts to import to the current context, and clear it.
0159      */
0160     void addImportedContexts();
0161 
0162     // helpers which need to be called separately from DeclarationBuilder
0163     virtual void visitFunctionArguments(FunctionDefinitionAst* node);
0164     virtual void visitFunctionBody(FunctionDefinitionAst* node);
0165     void openContextForClassDefinition(ClassDefinitionAst* node);
0166 
0167 protected:
0168     // those functions can be used if you want to do something to a context you are in,
0169     // but which is not the current one. Example: You want to add a variable to a class context,
0170     // but the current context is inside that class context (method declaration, ...)
0171     bool contextAlreadyOpen(DUContextPointer context);
0172     void activateAlreadyOpenedContext(DUContextPointer context);
0173     void closeAlreadyOpenedContext(DUContextPointer context);
0174     QList<DUContextPointer> m_temporarilyClosedContexts;
0175 
0176 protected:
0177     // true if the first of the two performed passes is currently active
0178     bool m_prebuilding = false;
0179 
0180     // List of imports which were encountered, but could not be resolved
0181     QList<IndexedString> m_unresolvedImports;
0182 
0183     // The ModificationRevision this context will be valid for
0184     ModificationRevision m_futureModificationRevision;
0185 
0186     IndexedString m_currentlyParsedDocument;
0187 
0188 private:
0189     // The top-context being built.
0190     ReferencedTopDUContext m_topContext;
0191     PythonEditorIntegrator* m_editor = nullptr;
0192     QList<KDevelop::DUContext*> m_importedParentContexts;
0193     QSharedPointer<FileIndentInformation> m_indentInformationCache;
0194 };
0195 
0196 }
0197 
0198 #endif