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