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