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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
0003     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0004     SPDX-FileCopyrightText: 2011-2014 Sven Brauch <svenbrauch@googlemail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #ifndef PYTHON_DECLARATIONBUILDER_H
0010 #define PYTHON_DECLARATIONBUILDER_H
0011 
0012 #include <language/duchain/builders/abstracttypebuilder.h>
0013 #include <language/duchain/builders/abstractdeclarationbuilder.h>
0014 
0015 #include <QList>
0016 
0017 #include "contextbuilder.h"
0018 #include "declarations/functiondeclaration.h"
0019 #include "ast.h"
0020 
0021 namespace Python
0022 {
0023 
0024 class CorrectionHelper;
0025 
0026 typedef KDevelop::AbstractTypeBuilder<Ast, Identifier, ContextBuilder> TypeBuilderBase;
0027 
0028 typedef KDevelop::AbstractDeclarationBuilder<Ast, Identifier, TypeBuilderBase> DeclarationBuilderBase;
0029 
0030 class KDEVPYTHONDUCHAIN_EXPORT DeclarationBuilder: public DeclarationBuilderBase
0031 {
0032 public:
0033     DeclarationBuilder(PythonEditorIntegrator* editor, int ownPriority);
0034     ~DeclarationBuilder() override;
0035 
0036     /**
0037      * @brief Entry function, called by KDevPlatform.
0038      */
0039     ReferencedTopDUContext build(const IndexedString& url, Ast* node,
0040                                  const ReferencedTopDUContext& updateContext = ReferencedTopDUContext()) override;
0041 
0042     /**
0043      * @brief Set whether the current running pass is the first or the second one.
0044      * @param prebuilding true if first pass, false if second
0045      */
0046     void setPrebuilding(bool prebuilding);
0047 
0048     /**
0049      * @brief Priority of this parse job.
0050      */
0051     int jobPriority() const;
0052 
0053     /**
0054      * @brief Get the docstring which belongs to the given body
0055      * @param body Body of statements which is in the documented function or class
0056      */
0057     QString getDocstring(QList<Ast*> body) const;
0058 
0059     /**
0060      * @brief Construct the dotted name of a module from an import ... from ... as statement.
0061      *
0062      * @param node the import ... from node
0063      * @param alias the ... as ... node
0064      * @param intermediate an additional string to prepend to the module name (dot is added internally)
0065      */
0066     QString buildModuleNameFromNode(ImportFromAst* node, AliasAst* alias, const QString& intermediate) const;
0067 
0068     /**
0069      * @brief Get a list of all module names which were not found while parsing.
0070      */
0071     QVector<IndexedString> missingModules() const {
0072         return m_missingModules;
0073     }
0074 
0075 protected:
0076     /// AST visitor functions
0077     void visitClassDefinition(ClassDefinitionAst* node) override;
0078     void visitFunctionDefinition(FunctionDefinitionAst* node) override;
0079     void visitAssignment(AssignmentAst* node) override;
0080     void visitAnnotationAssignment(AnnotationAssignmentAst* node) override;
0081     void visitAssignmentExpression(AssignmentExpressionAst* node) override;
0082     void visitFor(ForAst* node) override;
0083     void visitImport(ImportAst* node) override;
0084     void visitImportFrom(ImportFromAst* node) override;
0085     void visitArguments(ArgumentsAst* node) override;
0086     void visitExceptionHandler(ExceptionHandlerAst* node) override;
0087     void visitReturn(ReturnAst* node) override;
0088     void visitCode(CodeAst* node) override;
0089     void visitCall(CallAst* node) override;
0090     void visitYield(YieldAst* node) override;
0091     void visitWithItem(WithItemAst* node) override;
0092     void visitLambda(LambdaAst* node) override;
0093     void visitComprehension(ComprehensionAst* node) override;
0094     void visitGlobal(GlobalAst* node) override;
0095     void visitAssertion(AssertionAst* node) override;
0096     void visitIf(IfAst* node) override;
0097     void visitString(StringAst* node) override;
0098     void visitMatch(MatchAst* node) override;
0099     void visitNode(Ast* node) override;
0100 
0101 protected:
0102     enum VisitVariableFlags {
0103         NoVisitVariableFlags = 0x0,
0104         AbortIfReopenMismatch = 0x1
0105     };
0106     /// Visitor helper functions
0107     template<typename T> T* visitVariableDeclaration(Python::Ast* node, Declaration* previous=nullptr,
0108                                                      AbstractType::Ptr type = AbstractType::Ptr(),
0109                                                      VisitVariableFlags flags=NoVisitVariableFlags);
0110     template<typename T> T* visitVariableDeclaration(Identifier* node, Declaration* previous=nullptr,
0111                                                      AbstractType::Ptr type = AbstractType::Ptr(),
0112                                                      VisitVariableFlags flags=NoVisitVariableFlags);
0113 
0114 protected:
0115     /**
0116      * @brief Applies docstring hints, such as "addsType"
0117      * @param node the called function
0118      * @param function the declaration which belongs to @p node
0119      *
0120      * Used for example in a = []; a.append(3) to set the type of a to "list of int".
0121      */
0122     void applyDocstringHints(CallAst* node, Python::FunctionDeclaration::Ptr function);
0123 
0124     /**
0125      * @brief Try to deduce types of function arguments from a call and stores it in the duchain
0126      * @param node the called function or class (i.e. constructor)
0127      * @param called the declaration which belongs to @p node
0128      *
0129      * Used for example in def f(x): pass; a = f(3) to set the type of x to "int"
0130      */
0131     void addArgumentTypeHints(CallAst* node, DeclarationPointer called);
0132 
0133     /**
0134      * @brief Adjust the type of foo in an expression like assert isinstance(fooinstance, Foo)
0135      * Does nothing if the given expression isn't of any of the forms
0136      *    a) isinstance(fooinstance, Foo)
0137      *    b) type(fooinstance) == Foo */
0138     void adjustForTypecheck(ExpressionAst* check, bool useUnsure);
0139     /// Helper for the above
0140     void adjustExpressionsForTypecheck(ExpressionAst* adjust, ExpressionAst* from, bool useUnsure);
0141 
0142     /// Represents a single source type in a tuple assignment.
0143     struct SourceType {
0144         AbstractType::Ptr type;
0145         DeclarationPointer declaration;
0146         bool isAlias;
0147     };
0148 
0149     /** @brief If sourceType is a container that can be unpacked into outTypes, do so. */
0150     void tryUnpackType(AbstractType::Ptr sourceType, QVector<AbstractType::Ptr>& outTypes, int starred);
0151 
0152     /**
0153       * @brief Handle a variable assignment to @p name and give it the type @p element.
0154       */
0155     void assignToName(NameAst* name, const SourceType& element);
0156 
0157     /**
0158      * @brief Handle assignment to subscript @p subscript with rhs type @p element.
0159      */
0160     void assignToSubscript(SubscriptAst* subscript, const SourceType& element);
0161 
0162     /**
0163      * @brief Handle assignment to an attribute @p attribute with rhs type @p element.
0164      */
0165     void assignToAttribute(AttributeAst* attribute, const SourceType& element);
0166 
0167     /**
0168      * @brief Handle assignment to a target @p target with rhs type @p element.
0169      */
0170     void assignToTuple(TupleAst* tuple, const SourceType& element);
0171 
0172     /**
0173      * @brief Handle assignment to a target @p target with rhs type @p element.
0174      */
0175     void assignToUnknown(ExpressionAst* target, const AbstractType::Ptr type);
0176     void assignToUnknown(ExpressionAst* target, const SourceType& element);
0177 
0178     /**
0179      * @brief Find all existing declarations for the identifier @p node
0180      */
0181     QList<Declaration*> existingDeclarationsForNode(Identifier* node);
0182 
0183     enum FitDeclarationType {
0184         NoTypeRequired,
0185         InstanceDeclarationType,
0186         AliasDeclarationType,
0187         FunctionDeclarationType
0188     };
0189     FitDeclarationType kindForType(AbstractType::Ptr type, bool isAlias = false);
0190 
0191     /**
0192      * @brief schedule an object to be deleted when the declaration builder is destroyed
0193      * this is used to bypass the automated duchain cleanup for imports */
0194     void scheduleForDeletion(DUChainBase* d, bool doschedule = true);
0195 
0196     /**
0197      * @brief python-specific version of openDeclaration which scans for existing declarations in previous versions of
0198      *        this top-context in a more intelligent way.
0199      * Use this in normal declaratonbuilder code if you can't use visitVariableDeclaration. */
0200     template<typename T> T* eventuallyReopenDeclaration(Python::Identifier* name, FitDeclarationType mustFitType);
0201 
0202     template<typename T> QList<Declaration*> reopenFittingDeclaration(QList<Declaration*> declarations,
0203                                                                       FitDeclarationType mustFitType,
0204                                                                       RangeInRevision updateRangeTo, Declaration** ok);
0205 
0206     /**
0207      * @brief Create a declaration for an import statement.
0208      *
0209      * @param dottedName The dotted name of the module, like "random.randint".
0210      * @param declarationIdentifier provides the name and range
0211      * @param rangeNode can be used to override the declarationIdentifier's range, if required. Defaults to 0.
0212      * @param problemEncountered the encountered problem, if there's any
0213      * @return :Declaration* the declaration created, or 0 if none was found.
0214      **/
0215     Declaration* createModuleImportDeclaration(QString dottedName, QString declarationName, Python::Identifier* declarationIdentifier,
0216                                                ProblemPointer& problemEncountered, Python::Ast* rangeNode = nullptr);
0217     /**
0218      * @brief Create a tree of declarations for the specified list.
0219      * Give the list ["foo","bar","baz"], and you'll get a declaration "foo" containing "bar" in its internal context,
0220      * "bar" containing "baz" etc.
0221      * This is used in import handling.
0222      * This function automatically updates existing declaration trees to the maximum level possible! Thus,
0223      * if you call this with ["foo", "bar"], then ["foo", "baz"], "baz" will be added to "foo".
0224      *
0225      * @warning The DUChain must not be locked when this is called.
0226      *
0227      * @param nameComponents the list of names to create declarations for
0228      * @param declarationIdentifier provides the name and range
0229      * @param innerCtx the internalContext() to set on the last created declaration. Either this or aliasDeclaration must be provided!
0230      * @param aliasDeclaration the declaration to alias with the last created declaration
0231      * @param range can be used to override the declarationIdentifier's range, if required. Defaults to an invalid range.
0232      * @return :Declaration* the top level declaration created
0233      **/
0234     Declaration* createDeclarationTree(const QStringList& nameComponents, Identifier* declarationIdentifier,
0235                                        const ReferencedTopDUContext& innerCtx, Declaration* aliasDeclaration = nullptr,
0236                                        const RangeInRevision& range = RangeInRevision::invalid());
0237 
0238     /**
0239      * @brief Find a declaration specified by "foo.bar.baz" in the given top context.
0240      *
0241      * @param dottedNameIdentifier string list of module names, starting with the most general one.
0242      * @param ctx top context to search
0243      * @return :Declaration* declaration if found, 0 otherwise.
0244      *
0245      * This will traverse nested classes and properties until the list of passed names is exhausted.
0246      * @warning The DUChain must not be locked.
0247      **/
0248     Declaration* findDeclarationInContext(QStringList dottedNameIdentifier, TopDUContext* ctx) const;
0249 
0250 private:
0251     template<class T> T* openDeclaration(Identifier* name, DeclarationFlags flags = NoFlags)
0252     {
0253         T* decl = DeclarationBuilderBase::openDeclaration<T>(KDevelop::Identifier(name->value),
0254                                                              editorFindRange(name, name), flags);
0255         decl->setAlwaysForceDirect(true);
0256         return decl;
0257     };
0258     void closeDeclaration() override;
0259 
0260 private:
0261     /// HACK: List of items to delete after parsing finishes, to work around the built-in cleanup logic
0262     QList<DUChainBase*> m_scheduledForDeletion;
0263     QScopedPointer<CorrectionHelper> m_correctionHelper;
0264     int m_ownPriority = 0;
0265     QVector<StructureType::Ptr> m_currentClassTypes;
0266     // missing modules, for not reporting them as unknown variables
0267     QVector<IndexedString> m_missingModules;
0268 
0269     StringAst* m_lastComment = nullptr;
0270 };
0271 
0272 }
0273 
0274 #endif
0275 // kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on