1 /*
2     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
3     SPDX-FileCopyrightText: 2007-2009 David Nolden <david.nolden.kdevelop@art-master.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #ifndef KDEVPLATFORM_TOPDUCONTEXT_H
9 #define KDEVPLATFORM_TOPDUCONTEXT_H
10 
11 #include "ducontext.h"
12 #include <language/util/setrepository.h>
13 #include <QMetaType>
14 
15 template <class T>
16 class QExplicitlySharedDataPointer;
17 
18 namespace KDevelop {
19 class IAstContainer;
20 class QualifiedIdentifier;
21 class DUChain;
22 class ParsingEnvironmentFile;
23 class TopDUContextData;
24 class TopDUContextLocalPrivate;
25 class IndexedTopDUContext;
26 //   class TopDUContextDynamicData;
27 class Problem;
28 class DeclarationChecker;
29 class TopDUContext;
30 
31 struct KDEVPLATFORMLANGUAGE_EXPORT RecursiveImportRepository
32 {
33     static Utils::BasicSetRepository* repository();
34 };
35 
36 ///Maps an imported top-context to a pair:
37 ///1. The distance to the top-context, and 2. The next step towards the top-context
38 ///in the chain.
39 using RecursiveImports = QHash<const TopDUContext*, QPair<int, const TopDUContext*>>;
40 
41 using TopDUContextPointer = DUChainPointer<TopDUContext>;
42 
43 using ProblemPointer = QExplicitlySharedDataPointer<Problem>;
44 
45 ///KDevelop can unload unused top-context at any time. To prevent unloading,
46 ///keep a ReferencedTopDUContext.
47 class KDEVPLATFORMLANGUAGE_EXPORT ReferencedTopDUContext
48 {
49 public:
50     ReferencedTopDUContext(TopDUContext* context = nullptr);
51     ReferencedTopDUContext(const ReferencedTopDUContext& rhs);
52     ~ReferencedTopDUContext();
53 
54     ReferencedTopDUContext& operator=(const ReferencedTopDUContext& rhs);
55 
data()56     inline TopDUContext* data() const
57     {
58         return m_topContext;
59     }
60 
61     inline operator TopDUContext*() const {
62         return m_topContext;
63     }
64 
65     inline bool operator==(const ReferencedTopDUContext& rhs) const
66     {
67         return m_topContext == rhs.m_topContext;
68     }
69 
70     inline bool operator!=(const ReferencedTopDUContext& rhs) const
71     {
72         return m_topContext != rhs.m_topContext;
73     }
74 
75     inline TopDUContext* operator->() const
76     {
77         return m_topContext;
78     }
79 
hash()80     inline uint hash() const
81     {
82         return ( uint )((( quint64 )m_topContext) * 37);
83     }
84 
85 private:
86     TopDUContext* m_topContext;
87 };
88 
89 /**
90  * The top context in a definition-use chain for one source file.
91  *
92  * Implements SymbolTable lookups and locking for the chain.
93  *
94  * Contexts and Classes can only be found through TopDUContext if they are in the symbol table.
95  * @see DUContext::setInSymbolTable, Declaration::setInSymbolTable
96  *
97  * \todo move the registration with DUChain here
98  *
99  * @warning Do not delete top-contexts directly, use DUChain::removeDocumentChain instead.
100  */
101 class KDEVPLATFORMLANGUAGE_EXPORT TopDUContext
102     : public DUContext
103 {
104 public:
105     explicit TopDUContext(const IndexedString& url, const RangeInRevision& range,
106                           ParsingEnvironmentFile* file = nullptr);
107     explicit TopDUContext(TopDUContextData& data);
108 
109     TopDUContext& operator=(const TopDUContext& rhs) = delete;
110 
111     TopDUContext* topContext() const override;
112 
113     ///Returns an indexed representation of this top-context. Indexed representations stay valid even if the top-context is unloaded.
114     IndexedTopDUContext indexed() const;
115 
116     uint ownIndex() const;
117 
118     IndexedString url() const override;
119 
120     /**
121      * @see ParsingEnvironmentFile
122      * May return zero if no file was set.
123      * */
124     QExplicitlySharedDataPointer<ParsingEnvironmentFile> parsingEnvironmentFile() const;
125 
126     /// Returns true if this object is being deleted, otherwise false.
127     bool deleting() const;
128 
129     /// Returns true if this object is registered in the du-chain. If it is not, all sub-objects(context, declarations, etc.) can be changed
130     bool inDUChain() const override;
131     /// This flag is only used by DUChain, never change it from outside.
132     void setInDuChain(bool);
133 
134     /// Whether this top-context has a stored version on disk
135     bool isOnDisk() const;
136 
137     /**
138      * Returns a list of all problems encountered while parsing this top-context.
139      * Does not include the problems of imported contexts.
140      * */
141     QList<ProblemPointer> problems() const;
142 
143     /**
144      * Add a parsing-problem to this context.
145      *
146      * \note you must be holding a write lock when you access this function.
147      * */
148     void addProblem(const ProblemPointer& problem);
149 
150     /**
151      * Clear the list of problems
152      *
153      * \note you must be holding a write lock when you access this function.
154      */
155     void clearProblems();
156 
157     /**
158      * Set the list of problems, replacing all existing ones.
159      *
160      * \note you must be holding a write lock when you access this function.
161      */
162     void setProblems(const QList<ProblemPointer>& pointers);
163 
164     /**
165      * Determine if this chain imports another chain recursively.
166      *
167      * This uses the imports-cache for speedup if it is available, thus it is not necessarily 100% correct
168      * if the cache is not up-to-date.
169      *
170      * \note you must be holding a read but not a write chain lock when you access this function.
171      */
172     bool imports(const DUContext* origin, const CursorInRevision& position) const override;
173 
174     enum {
175         Identity = 4
176     };
177 
178     enum Feature : quint16 {
179         ///Top-context features standard that can be requested from the duchain, and that are stored in the features() member.
180         Empty = 0, //Only the top-context structure (imports etc.) is built, but no declarations and no contexts
181         SimplifiedVisibleDeclarationsAndContexts = 2, //The top-context should only contain publically simplified accessible declarations and contexts, without doing type look-up,
182         //without extended information like function-argument declarations, etc., imported contexts can be parsed with 'Empty' features
183         //This flag essentially leads to a ctags-like processing level.
184         VisibleDeclarationsAndContexts = SimplifiedVisibleDeclarationsAndContexts + 4, //Default: The top-context should only contain publically accessible declarations and contexts
185         AllDeclarationsAndContexts = VisibleDeclarationsAndContexts + 8, //The top-context should also contain non-public declarations and contexts, but no uses
186         AllDeclarationsContextsAndUses = 16 + AllDeclarationsAndContexts, //The top-context should contain uses and all declarations + contexts
187         AST = 32,         //Signalizes that the ast() should be filled
188         AllDeclarationsContextsUsesAndAST = AST | AllDeclarationsContextsAndUses, //Convenience flag, combining AST and AllDeclarationsContextsAndUses
189 
190         ///Additional update-flags that have a special meaning during updating, but are not set stored into a top-context
191         Recursive = 64, //Request the given features on all recursively imported contexts. Only the features are applied recursively (including AST)
192         ForceUpdate = 128, //Enforce updating the top-context
193         ForceUpdateRecursive = ForceUpdate | 256, //Enforce updating the top-context and all its imports
194 
195         ///You can define own language-dependent features behind this flag
196         LastFeature = 512
197     };
198     Q_DECLARE_FLAGS(Features, Feature)
199 
200     ///Returns the currently active features of this top-context. The features will include AST if ast() is valid.
201     Features features() const;
202     ///Set the features of this top-context. These features are ignored: AST, ForceUpdate, and ForceUpdateRecursive.
203     void setFeatures(Features);
204 
205     /**
206      * Retrieves or creates a local index that is to be used for referencing the given @param declaration
207      * in local uses. Also registers this context as a user of the declaration.
208      * @param create If this is false, only already registered indices will be returned.
209      *               If the declaration is not registered, std::numeric_limits<int>::max() is returned
210      *
211      * The duchain must be write-locked if create is true, else it must at least be read-locked.
212      * */
213     int indexForUsedDeclaration(Declaration* declaration, bool create = true);
214 
215     /**
216      * Tries to retrieve the used declaration
217      * @param declarationIndex The index of the declaration which have to be retrieved
218      * */
219     Declaration* usedDeclarationForIndex(unsigned int declarationIndex) const;
220 
221     /**
222      * You can use this before you rebuild all uses. This does not affect any uses directly,
223      * it only invalidates the mapping of declarationIndices to Declarations.
224      *
225      * usedDeclarationForIndex(..) must not be called until the use has gotten a new index through
226      * indexForUsedDeclaration(..).
227      * */
228     void clearUsedDeclarationIndices();
229 
230     /**
231      * Recursively deletes all contained uses, declaration-indices, etc.
232      */
233     void deleteUsesRecursively() override;
234 
235     /**
236      * Returns the AST Container, that contains the AST created during parsing.
237      * This is only created if you request the AST feature for parsing.
238      * It may be discarded at any time. Every update without the AST feature will discard it.
239      * The actual contents is language-specific.
240      *
241      * @todo Figure out logic to get rid of AST when it is not needed/useful
242      */
243     QExplicitlySharedDataPointer<IAstContainer> ast() const;
244 
245     /**
246      * Sets the AST Container.
247      */
248     void setAst(const QExplicitlySharedDataPointer<IAstContainer>& ast);
249 
250     /**
251      * Utility function to clear the AST Container
252      */
253     void clearAst();
254 
255     ///@param temporary If this is true, importers of this context will not be notified of the new imports. This greatly increases performance while removing the context,
256     ///but creates in inconsistent import-structure. Therefore it is only suitable for temporary imports. These imports will not be visible from contexts that import this one.
257     ///When this top-context does not own its private data, the import is added locally only to this context, not into the shared data.
258     void addImportedParentContext(DUContext* context,
259                                   const CursorInRevision& position = CursorInRevision(), bool anonymous = false,
260                                   bool temporary = false) override;
261     ///Use this for mass-adding of imported contexts, it is faster than adding them individually.
262     ///@param temporary If this is true, importers of this context will not be notified of the new imports. This greatly increases performance while removing the context,
263     ///but creates in inconsistent import-structure. Therefore it is only suitable for temporary imports. These imports will not be visible from contexts that import this one.
264     ///When this top-context does not own its private data, the import is added locally only to this context, not into the shared data.
265     virtual void addImportedParentContexts(const QVector<QPair<TopDUContext*, CursorInRevision>>& contexts,
266                                            bool temporary = false);
267 
268     ///When this top-context does not own its private data, the import is removed locally only from this context, not from the shared data.
269     void removeImportedParentContext(DUContext* context) override;
270     ///Use this for mass-removing of imported contexts, it is faster than removing them individually.
271     ///When this top-context does not own its private data, the import is removed locally only from this context, not from the shared data.
272     virtual void removeImportedParentContexts(const QList<TopDUContext*>& contexts);
273 
274     ///When this top-context does not own its private data, only the local imports of this context are removed, not those from the shared data.
275     void clearImportedParentContexts() override;
276 
277     using IndexedRecursiveImports = Utils::StorableSet<IndexedTopDUContext, IndexedTopDUContextIndexConversion, RecursiveImportRepository,
278         true>;
279 
280     QVector<Import> importedParentContexts() const override;
281 
282     QVector<DUContext*> importers() const override;
283 
284     ///Returns all currently loade importers
285     virtual QList<DUContext*> loadedImporters() const;
286 
287     CursorInRevision importPosition(const DUContext* target) const override;
288 
289     ///Returns the set of all recursively imported top-contexts. If import-caching is used, this returns the cached set.
290     ///The list also contains this context itself. This set is used to determine declaration-visibility from within this top-context.
291     const IndexedRecursiveImports& recursiveImportIndices() const;
292 
293     /**
294      * Updates the cache of recursive imports. When you call this, from that moment on the set returned by recursiveImportIndices() is fixed, until
295      * you call it again to update them. If your language has a very complex often-changing import-structure,
296      * like for example in the case of C++, it is recommended to call this during while parsing, instead of using
297      * the expensive builtin implicit mechanism.
298      * Note that if you use caching, you _must_ call this before you see any visibility-effect after adding imports.
299      *
300      * Using import-caching has another big advantage: A top-context can be loaded without loading all its imports.
301      *
302      * Note: This is relatively expensive since it requires loading all imported contexts.
303      *
304      * When this is called, the top-context must already be registered in the duchain.
305      */
306     void updateImportsCache();
307 
308     bool usingImportsCache() const;
309 
310     bool findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position,
311                                   const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source,
312                                   SearchFlags flags, uint depth) const override;
313 
314 protected:
315     void setParsingEnvironmentFile(ParsingEnvironmentFile*);
316 
317     /**
318      * Does the same as DUContext::updateAliases, except that it uses the symbol-store, and processes the whole identifier.
319      * @param canBeNamespace whether the searched identifier may be a namespace.
320      * If this is true, namespace-aliasing is applied to the last elements of the identifiers.
321      * */
322     template <class Acceptor>
323     void applyAliases(const SearchItem::PtrList& identifiers, Acceptor& accept, const CursorInRevision& position,
324                       bool canBeNamespace) const;
325 
326 protected:
327     ~TopDUContext() override;
328 
329     void clearFeaturesSatisfied();
330     void rebuildDynamicData(DUContext* parent, uint ownIndex) override;
331     //Must be called after all imported top-contexts were loaded into the du-chain
332     void rebuildDynamicImportStructure();
333 
334     struct AliasChainElement;
335     struct FindDeclarationsAcceptor;
336     struct DeclarationChecker;
337     struct ApplyAliasesBuddyInfo;
338 
339     template <class Acceptor>
340     bool applyAliases(const QualifiedIdentifier& previous, const SearchItem::Ptr& identifier, Acceptor& acceptor,
341                       const CursorInRevision& position, bool canBeNamespace, ApplyAliasesBuddyInfo* buddy,
342                       uint recursionDepth) const;
343     //Same as imports, without the slow access-check, for internal usage
344     bool importsPrivate(const DUContext* origin, const CursorInRevision& position) const;
345     DUCHAIN_DECLARE_DATA(TopDUContext)
346 
347     ///Called by DUChain::removeDocumentChain to destroy this top-context.
348     void deleteSelf();
349 
350     //Most of these classes need access to m_dynamicData
351     friend class DUChain;
352     friend class DUChainPrivate;
353     friend class TopDUContextData;
354     friend class TopDUContextLocalPrivate;
355     friend class TopDUContextDynamicData;
356     friend class Declaration;
357     friend class DUContext;
358     friend class Problem;
359     friend class IndexedDeclaration;
360     friend class IndexedDUContext;
361     friend class LocalIndexedDeclaration;
362     friend class LocalIndexedDUContext;
363     friend class LocalIndexedProblem;
364     friend class DeclarationId;
365     friend class ParsingEnvironmentFile;
366 
367     TopDUContextLocalPrivate* m_local;
368 
369     class TopDUContextDynamicData* m_dynamicData;
370 };
371 
372 /**
373  * Returns all uses of the given declaration within this top-context and all sub-contexts
374  * */
375 KDEVPLATFORMLANGUAGE_EXPORT QVector<RangeInRevision> allUses(TopDUContext* context, Declaration* declaration,
376                                                              bool noEmptyRanges = false);
377 
qHash(const ReferencedTopDUContext & ctx)378 inline uint qHash(const ReferencedTopDUContext& ctx)
379 {
380     return ctx.hash();
381 }
382 
383 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
384 Q_DECLARE_OPERATORS_FOR_FLAGS(TopDUContext::Features)
385 #endif
386 
387 }
388 
389 #if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
390 Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::TopDUContext::Features)
391 #endif
392 
393 Q_DECLARE_TYPEINFO(KDevelop::ReferencedTopDUContext, Q_MOVABLE_TYPE);
394 Q_DECLARE_METATYPE(KDevelop::ReferencedTopDUContext)
395 
396 #endif // KDEVPLATFORM_TOPDUCONTEXT_H
397