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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Piyush verma <piyush.verma@gmail.com>
0003     SPDX-FileCopyrightText: 2010-2013 Sven Brauch <svenbrauch@googlemail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "usebuilder.h"
0009 
0010 #include <QDebug>
0011 #include "duchaindebug.h"
0012 
0013 #include <language/duchain/declaration.h>
0014 #include <language/duchain/use.h>
0015 #include <language/duchain/topducontext.h>
0016 #include <language/duchain/duchain.h>
0017 #include <language/duchain/duchainlock.h>
0018 #include <language/duchain/types/structuretype.h>
0019 
0020 #include "parsesession.h"
0021 #include "pythoneditorintegrator.h"
0022 #include "ast.h"
0023 #include "expressionvisitor.h"
0024 #include "helpers.h"
0025 
0026 using namespace KTextEditor;
0027 using namespace KDevelop;
0028 
0029 namespace Python {
0030 
0031 UseBuilder::UseBuilder(PythonEditorIntegrator* editor, QVector<IndexedString> ignoreVariables)
0032     : UseBuilderBase()
0033     , m_errorReportingEnabled(true)
0034     , m_ignoreVariables(ignoreVariables)
0035 {
0036     setEditor(editor);
0037 }
0038 
0039 DUContext* UseBuilder::contextAtOrCurrent(const CursorInRevision& pos)
0040 {
0041     DUContext* context = nullptr;
0042     {
0043         DUChainReadLocker lock;
0044         context = topContext()->findContextAt(pos, true);
0045     }
0046     if ( ! context ) {
0047         context = currentContext();
0048     }
0049     return context;
0050 }
0051 
0052 void UseBuilder::useHiddenMethod(ExpressionAst* value, Declaration* function) {
0053     if ( !function || function->topContext() == Helper::getDocumentationFileContext() ) {
0054         // Don't add a use for e.g. `list.__getitem__` in `foo[0]`, no-one cares.
0055         return;
0056     }
0057     RangeInRevision useRange;
0058     // TODO fixme! this does not necessarily use the opening bracket as it should
0059     useRange.start = CursorInRevision(value->endLine, value->endCol + 1);
0060     useRange.end = CursorInRevision(value->endLine, value->endCol + 2);
0061     if ( function && function->isFunctionDeclaration() ) {
0062         UseBuilderBase::newUse(useRange, DeclarationPointer(function));
0063     }
0064 }
0065 
0066 void UseBuilder::visitName(NameAst* node)
0067 {
0068     DUContext* context = contextAtOrCurrent(editorFindPositionSafe(node));
0069     Declaration* declaration = Helper::declarationForName(node, editorFindPositionSafe(node),
0070                                                           DUChainPointer<const DUContext>(context));
0071 
0072     Q_ASSERT(node->identifier);
0073     RangeInRevision useRange = rangeForNode(node->identifier, true);
0074 
0075     if ( declaration && declaration->range() == useRange ) return;
0076 
0077     if ( ! declaration && m_errorReportingEnabled ) {
0078         if ( ! m_ignoreVariables.contains(IndexedString(node->identifier->value)) ) {
0079             KDevelop::Problem *p = new KDevelop::Problem();
0080             p->setFinalLocation(DocumentRange(currentlyParsedDocument(), useRange.castToSimpleRange())); // TODO ok?
0081             p->setSource(KDevelop::IProblem::SemanticAnalysis);
0082             p->setSeverity(KDevelop::IProblem::Hint);
0083             p->setDescription(i18n("Undefined variable: %1", node->identifier->value));
0084             {
0085                 DUChainWriteLocker wlock(DUChain::lock());
0086                 ProblemPointer ptr(p);
0087                 topContext()->addProblem(ptr);
0088             }
0089         }
0090     }
0091     UseBuilderBase::newUse(useRange, DeclarationPointer(declaration));
0092 }
0093 
0094 void UseBuilder::visitCall(CallAst* node)
0095 {
0096     UseBuilderBase::visitCall(node);
0097     DUContext* context = contextAtOrCurrent(editorFindPositionSafe(node));
0098     ExpressionVisitor v(context);
0099     v.visitNode(node->function);
0100     if ( auto classType = v.lastType().dynamicCast<StructureType>() ) {
0101         DUChainReadLocker lock;
0102         // This is either __init__() or __call__(): `a = Foo()` or `b = a()`.
0103         auto function = Helper::functionForCalled(classType->declaration(topContext()), v.isAlias());
0104         lock.unlock();
0105         useHiddenMethod(node->function, function.declaration);
0106     }
0107 }
0108 
0109 void UseBuilder::visitAttribute(AttributeAst* node)
0110 {
0111     UseBuilderBase::visitAttribute(node);
0112 
0113     DUContext* context = contextAtOrCurrent(editorFindPositionSafe(node));
0114     ExpressionVisitor v(context);
0115     v.visitNode(node);
0116     RangeInRevision useRange(node->attribute->startLine, node->attribute->startCol,
0117                              node->attribute->endLine, node->attribute->endCol + 1);
0118     
0119     DeclarationPointer declaration = v.lastDeclaration();
0120     DUChainWriteLocker wlock;
0121     if ( declaration && declaration->range() == useRange ) {
0122         // this is the declaration, don't build a use for it
0123         return;
0124     }
0125     if ( ! declaration && v.isConfident() && ( ! v.lastType() || Helper::isUsefulType(v.lastType()) ) ) {
0126         KDevelop::Problem *p = new KDevelop::Problem();
0127         p->setFinalLocation(DocumentRange(currentlyParsedDocument(), useRange.castToSimpleRange()));
0128         p->setSource(KDevelop::IProblem::SemanticAnalysis);
0129         p->setSeverity(KDevelop::IProblem::Hint);
0130         p->setDescription(i18n("Attribute \"%1\" not found on accessed object", node->attribute->value));
0131         ProblemPointer ptr(p);
0132         topContext()->addProblem(ptr);
0133     }
0134     UseBuilderBase::newUse(useRange, declaration);
0135 }
0136 
0137 void UseBuilder::visitSubscript(SubscriptAst* node) {
0138     UseBuilderBase::visitSubscript(node);
0139     DUContext* context = contextAtOrCurrent(editorFindPositionSafe(node->value));
0140     ExpressionVisitor v(context);
0141     v.visitNode(node->value);
0142 
0143     static const IndexedIdentifier getitemIdentifier(KDevelop::Identifier("__getitem__"));
0144     static const IndexedIdentifier setitemIdentifier(KDevelop::Identifier("__setitem__"));
0145 
0146     bool isAugTarget = (node->parent->astType == Ast::AugmentedAssignmentAstType &&
0147                         static_cast<AugmentedAssignmentAst*>(node->parent)->target == node);
0148 
0149     // e.g `a[0] += 2` uses both __getitem__ and __setitem__.
0150     if (isAugTarget || node->context == ExpressionAst::Context::Load) {
0151         DUChainReadLocker lock;
0152         auto getItemFunc = Helper::accessAttribute(v.lastType(), getitemIdentifier, context->topContext());
0153         lock.unlock();
0154         useHiddenMethod(node->value, getItemFunc);
0155     }
0156     if ( node->context == ExpressionAst::Context::Store ) {
0157         DUChainReadLocker lock;
0158         auto setItemFunc = Helper::accessAttribute(v.lastType(), setitemIdentifier, context->topContext());
0159         lock.unlock();
0160         useHiddenMethod(node->value, setItemFunc);
0161     }
0162 }
0163 
0164 void UseBuilder::visitMatchAs(MatchAsAst* node)
0165 {
0166     DUContext* context = contextAtOrCurrent(editorFindPositionSafe(node));
0167     if (!node->name) {
0168         return;
0169     }
0170     Declaration* declaration = Helper::declarationForName(node->name->value, editorFindPositionSafe(node),
0171                                                           DUChainPointer<const DUContext>(context));
0172 
0173     RangeInRevision useRange = rangeForNode(node->name, true);
0174     if ( declaration && declaration->range() == useRange )
0175         return;
0176 
0177     UseBuilderBase::newUse(useRange, DeclarationPointer(declaration));
0178 }
0179 
0180 ParseSession *UseBuilder::parseSession() const
0181 {
0182     return m_session;
0183 }
0184 
0185 }
0186 // kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on