1 /* 2 SPDX-FileCopyrightText: 2006-2008 Hamish Rodda <rodda@kde.org> 3 4 SPDX-License-Identifier: LGPL-2.0-only 5 */ 6 7 #ifndef KDEVPLATFORM_ABSTRACTUSEBUILDER_H 8 #define KDEVPLATFORM_ABSTRACTUSEBUILDER_H 9 10 #include "../declaration.h" 11 #include "../use.h" 12 #include "../topducontext.h" 13 #include "../duchain.h" 14 #include "../duchainlock.h" 15 16 #include <util/stack.h> 17 18 namespace KDevelop { 19 /** 20 * \short Abstract definition-use chain use builder class 21 * 22 * The AbstractUseBuilder is a convenience class template for creating customized 23 * definition-use chain use builders from an AST. It simplifies creating or 24 * modifying existing \ref Use "Uses" 25 * 26 * \author Hamish Rodda \<rodda@kde.org\> 27 */ 28 template <typename T, typename NameT, typename LanguageSpecificUseBuilderBase> 29 class AbstractUseBuilder 30 : public LanguageSpecificUseBuilderBase 31 { 32 public: 33 /// Constructor. 34 template <class ParseSession> AbstractUseBuilder(ParseSession * session)35 AbstractUseBuilder(ParseSession* session) : LanguageSpecificUseBuilderBase(session) 36 , m_finishContext(true) 37 { 38 } 39 AbstractUseBuilder()40 AbstractUseBuilder() 41 : m_finishContext(true) 42 { 43 } 44 45 /** 46 * Iterate an existing duchain, and add, remove or modify uses as determined 47 * from the ast. 48 * 49 * \param node AST node to start visiting. 50 */ buildUses(T * node)51 void buildUses(T* node) 52 { 53 auto* top = dynamic_cast<TopDUContext*>(this->contextFromNode(node)); 54 55 if (top) { 56 DUChainWriteLocker lock(DUChain::lock()); 57 top->clearUsedDeclarationIndices(); 58 if (top->features() & TopDUContext::AllDeclarationsContextsAndUses) 59 LanguageSpecificUseBuilderBase::setRecompiling(true); 60 } 61 62 LanguageSpecificUseBuilderBase::supportBuild(node); 63 } 64 65 protected: 66 67 struct ContextUseTracker 68 { 69 QVector<KDevelop::Use> createUses; 70 }; 71 72 /** 73 * Register a new use at the AST node @p name. 74 * 75 * @param name AST node which both represents a use and the identifier for the declaration which is being used. 76 */ newUse(NameT * name)77 void newUse(NameT* name) 78 { 79 QualifiedIdentifier id = this->identifierForNode(name); 80 81 RangeInRevision newRange = this->editorFindRange(name, name); 82 83 DUChainReadLocker lock(DUChain::lock()); 84 QList<Declaration*> declarations = LanguageSpecificUseBuilderBase::currentContext()->findDeclarations(id, 85 newRange.start); 86 for (Declaration* declaration : qAsConst(declarations)) { 87 if (!declaration->isForwardDeclaration()) { 88 declarations.clear(); 89 declarations.append(declaration); 90 break; 91 } 92 } 93 94 // If we don't break, there's no non-forward declaration 95 96 lock.unlock(); 97 newUse(newRange, !declarations.isEmpty() ? DeclarationPointer(declarations.first()) : DeclarationPointer()); 98 } 99 100 ///@todo Work this over! We must not pass around "Declaration*" values if the duchain is not locked. 101 102 /** 103 * Register a new use for a \a declaration with a \a node. 104 * 105 * \param node Node which encompasses the use. 106 * \param declaration Declaration which is being used. May be null when a declaration cannot be found for the use. 107 */ newUse(T * node,const KDevelop::DeclarationPointer & declaration)108 void newUse(T* node, const KDevelop::DeclarationPointer& declaration) 109 { 110 newUse(this->editorFindRange(node, node), declaration); 111 } 112 113 /** 114 * Register a new use. 115 * 116 * \param newRange Text range which encompasses the use. 117 * \param _declaration Declaration which is being used. May be null when a declaration cannot be found for the use. 118 */ newUse(const RangeInRevision & newRange,const DeclarationPointer & _declaration)119 void newUse(const RangeInRevision& newRange, const DeclarationPointer& _declaration) 120 { 121 DUChainWriteLocker lock(DUChain::lock()); 122 Declaration* declaration = _declaration.data(); 123 124 if (!declaration) 125 return; // The declaration was deleted in the meantime 126 127 int declarationIndex = LanguageSpecificUseBuilderBase::currentContext()->topContext()->indexForUsedDeclaration( 128 declaration); 129 int contextUpSteps = 0; //We've got to use the stack here, and not parentContext(), because the order may be different 130 131 { 132 /* 133 * We need to find a context that this use fits into, which must not necessarily be the current one. 134 * The reason are macros like SOME_MACRO(SomeClass), where SomeClass is expanded to be within a 135 * sub-context that comes from the macro. That sub-context will have a very small range, and will most 136 * probably not be the range of the actual "SomeClass" text, so the "SomeClass" use has to be moved 137 * into the context that surrounds the SOME_MACRO invocation. 138 * */ 139 DUContext* newContext = LanguageSpecificUseBuilderBase::currentContext(); 140 while (!newContext->range().contains(newRange) && 141 contextUpSteps < (LanguageSpecificUseBuilderBase::contextStack().size() - 1)) { 142 ++contextUpSteps; 143 newContext = 144 LanguageSpecificUseBuilderBase::contextStack()[LanguageSpecificUseBuilderBase::contextStack().size() 145 - 1 - contextUpSteps]; 146 } 147 148 if (contextUpSteps) { 149 m_finishContext = false; 150 openContext(newContext); 151 m_finishContext = true; 152 currentUseTracker() = m_trackerStack.at(m_trackerStack.size() - contextUpSteps - 2); 153 } 154 155 currentUseTracker().createUses << KDevelop::Use(newRange, declarationIndex); 156 } 157 158 if (contextUpSteps) { 159 Q_ASSERT( 160 m_contexts[m_trackerStack.size() - contextUpSteps - 2] == 161 LanguageSpecificUseBuilderBase::currentContext()); 162 m_trackerStack[m_trackerStack.size() - contextUpSteps - 2] = currentUseTracker(); 163 m_finishContext = false; 164 closeContext(); 165 m_finishContext = true; 166 } 167 } 168 169 /** 170 * Reimplementation of openContext, to track which uses should be assigned to which context. 171 */ openContext(KDevelop::DUContext * newContext)172 void openContext(KDevelop::DUContext* newContext) override 173 { 174 LanguageSpecificUseBuilderBase::openContext(newContext); 175 176 ContextUseTracker newTracker; 177 m_trackerStack.push(newTracker); 178 m_contexts.push(newContext); 179 } 180 181 /** 182 * Reimplementation of closeContext, to track which uses should be assigned to which context. 183 */ closeContext()184 void closeContext() override 185 { 186 if (m_finishContext) { 187 DUChainWriteLocker lock(DUChain::lock()); 188 189 this->currentContext()->deleteUses(); 190 191 ContextUseTracker& tracker(currentUseTracker()); 192 for (auto& createUse : tracker.createUses) { 193 this->currentContext()->createUse(createUse.m_declarationIndex, 194 createUse.m_range); 195 } 196 } 197 198 LanguageSpecificUseBuilderBase::closeContext(); 199 200 m_trackerStack.pop(); 201 m_contexts.pop(); 202 } 203 204 private: currentUseTracker()205 inline ContextUseTracker& currentUseTracker() { return m_trackerStack.top(); } 206 Stack<ContextUseTracker> m_trackerStack; 207 Stack<KDevelop::DUContext*> m_contexts; 208 209 //Whether not encountered uses should be deleted during closeContext() 210 bool m_finishContext; 211 }; 212 } 213 214 #endif // KDEVPLATFORM_ABSTRACTUSEBUILDER_H 215