File: abstractcontextbuilder.h

package info (click to toggle)
kdevelop 4%3A24.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 71,888 kB
  • sloc: cpp: 290,869; python: 3,626; javascript: 3,518; sh: 1,316; ansic: 703; xml: 401; php: 95; lisp: 66; makefile: 31; sed: 12
file content (652 lines) | stat: -rw-r--r-- 22,699 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
/*
    SPDX-FileCopyrightText: 2008 Andreas Pakulat <apaku@gmx.de>
    SPDX-FileCopyrightText: 2006 Roberto Raggi <roberto@kdevelop.org>
    SPDX-FileCopyrightText: 2006-2008 Hamish Rodda <rodda@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#ifndef KDEVPLATFORM_ABSTRACTABSTRACTCONTEXTBUILDER_H
#define KDEVPLATFORM_ABSTRACTABSTRACTCONTEXTBUILDER_H

#include <climits>

#include "../topducontext.h"
#include "../duchainpointer.h"
#include "../duchainlock.h"
#include "../duchain.h"
#include "../ducontext.h"
#include "../identifier.h"
#include "../parsingenvironment.h"

#include <serialization/indexedstring.h>
#include <util/stack.h>

namespace KDevelop {
/**
 * \short Abstract definition-use chain context builder class
 *
 * The AbstractContextBuilder is a convenience class template for creating customized
 * definition-use chain context builders from an AST.  It simplifies:
 * - creating or modifying an existing DUContext tree
 * - following a DUContext tree for second and subsequent passes, if required
 * - opening and closing DUContext instances
 * - tracking which DUContext instances are still present when recompiling, and removing DUContexts which no longer exist in the source code.
 *
 * \author Hamish Rodda \<rodda@kde.org\>
 */
template <typename T, typename NameT>
class AbstractContextBuilder
{
public:
    /// Constructor.
    AbstractContextBuilder() : m_compilingContexts(false)
        , m_recompiling(false)
        , m_lastContext(nullptr)
    {
    }

    virtual ~AbstractContextBuilder()
    {
    }

    /**
     * Entry point for building a definition-use chain with this builder.
     *
     * This function determines whether we are updating a chain, or creating a new one.  If we are
     * creating a new chain, a new TopDUContext is created and registered with DUChain.
     *
     * \param url Url of the document being parsed.
     * \param node AST node to start building from.
     * \param updateContext TopDUContext to update if a duchain was previously created for this url, otherwise pass a null pointer.
     *
     * \returns the newly created or updated TopDUContext pointer.
     */
    virtual ReferencedTopDUContext build(const IndexedString& url, T* node,
                                         const ReferencedTopDUContext& updateContext
                                             = ReferencedTopDUContext())
    {
        m_compilingContexts = true;
        m_url = url;

        ReferencedTopDUContext top;
        {
            DUChainWriteLocker lock(DUChain::lock());
            top = updateContext.data();

            if (top) {
                m_recompiling = true;
                Q_ASSERT(top->type() == DUContext::Global);
                Q_ASSERT(DUChain::self()->chainForIndex(top->ownIndex()) == top);
            } else
            {
                top = newTopContext(RangeInRevision(CursorInRevision(0, 0), CursorInRevision(INT_MAX, INT_MAX)));
                DUChain::self()->addDocumentChain(top);
                top->setType(DUContext::Global);
            }

            setEncountered(top);
            setContextOnNode(node, top);
        }

        supportBuild(node, top);

        m_compilingContexts = false;
        return top;
    }

protected:
    /**
     * Support another builder by tracking the current context.
     * @param node the given node.
     * @param context the context to use. Must be set when the given node has no context. When it has one attached, this parameter is not needed.
     */
    virtual void supportBuild(T* node, DUContext* context = nullptr)
    {
        if (!context)
            context = contextFromNode(node);

        Q_ASSERT(context);

        openContext(context);

        startVisiting(node);

        closeContext();

        Q_ASSERT(m_contextStack.isEmpty());
    }

    /**
     * Entry point to your visitor.  Reimplement and call the appropriate visit function.
     *
     * \param node AST node to visit.
     */
    virtual void startVisiting(T* node) = 0;

    /**
     * Associate a \a context with a given AST \a node.  Once called on a \a node, the
     * contextFromNode() function should return this \a context when called.
     *
     * \param node AST node to associate
     * \param context DUContext to associate
     */
    virtual void setContextOnNode(T* node, DUContext* context) = 0;

    /**
     * Retrieve an associated DUContext from the given \a node.  Used on second and
     * subsequent passes of the context builder (for supporting other builds)
     *
     * \param node AST node which was previously associated
     * \returns the DUContext which was previously associated
     */
    virtual DUContext* contextFromNode(T* node) = 0;

    /**
     * Retrieves a text range from the given nodes.
     *
     * As editor integrators have to be extended to determine ranges from AST nodes,
     * this function must be reimplemented to allow generic retrieving of rangs from nodes.
     *
     * \param fromNode the AST node to start from (on the start boundary)
     * \param toNode the AST node to end at (on the end boundary)
     *
     * \returns the text range encompassing the given AST node(s)
     */
    virtual RangeInRevision editorFindRange(T* fromNode, T* toNode) = 0;

    /**
     * Retrieve a text range for the given nodes.  This is a special function required
     * by c++ support as a different range may need to be retrieved depending on
     * whether macros are involved.  It is not usually required to implement this
     * function separately to editorFindRange() for other languages.
     *
     * \param fromNode the AST node to start from (on the start boundary)
     * \param toNode the AST node to end at (on the end boundary)
     *
     * \returns the text range encompassing the given AST node(s)
     */
    virtual RangeInRevision editorFindRangeForContext(T* fromNode, T* toNode)
    {
        return editorFindRange(fromNode, toNode);
    }

    /**
     * Determine the QualifiedIdentifier which corresponds to the given ast \a node.
     *
     * \param node ast node which represents an identifier
     * \return the qualified identifier determined from \a node
     */
    virtual QualifiedIdentifier identifierForNode(NameT* node) = 0;

    /**
     * Create a new DUContext from the given \a range.
     *
     * This exists so that you can create custom DUContext subclasses for your
     * language if you need to.
     *
     * \param range range for the new context to encompass
     * \returns the newly created context
     */
    virtual DUContext* newContext(const RangeInRevision& range)
    {
        return new DUContext(range, currentContext());
    }

    /**
     * Create a new TopDUContext from the given \a range.
     *
     * This exists so that you can create custom TopDUContext subclasses for your
     * language if you need to.
     *
     * \returns the newly created context
     */
    virtual TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file = nullptr)
    {
        return new TopDUContext(m_url, range, file);
    }

    /// Determine the currently open context. \returns the current context.
    inline DUContext* currentContext() const { return m_contextStack.top(); }
    /// Determine the last closed context. \returns the last closed context.
    inline DUContext* lastContext() const { return m_lastContext; }
    /// Clears the last closed context.
    inline void clearLastContext() { m_lastContext = nullptr; }

    inline void setLastContext(DUContext* context) { m_lastContext = context; }

    TopDUContext* topContext() const
    {
        return currentContext()->topContext();
    }

    /**
     * Determine if we are recompiling an existing definition-use chain, or if
     * a new chain is being created from scratch.
     *
     * \returns true if an existing duchain is being updated, otherwise false.
     */
    inline bool recompiling() const { return m_recompiling; }

    /**
     * Tell the context builder whether we are recompiling an existing definition-use chain, or if
     * a new chain is being created from scratch.
     *
     * \param recomp set to true if an existing duchain is being updated, otherwise false.
     */
    inline void setRecompiling(bool recomp) { m_recompiling = recomp; }

    /**
     * Determine whether this pass will create DUContext instances.
     *
     * On the first pass of definition-use chain compiling, DUContext instances
     * are created to represent contexts in the source code.  These contexts are
     * associated with their AST nodes at the time (see setContextOnNode()).
     *
     * On second and subsequent passes, the contexts already exist and thus can be
     * retrieved through contextFromNode().
     *
     * \returns true if compiling contexts (ie. 1st pass), otherwise false.
     */
    inline bool compilingContexts() const { return m_compilingContexts; }

    /**
     * Sets whether we need to create ducontexts, ie. if this is the first pass.
     *
     * \sa compilingContexts()
     */
    inline void setCompilingContexts(bool compilingContexts) { m_compilingContexts = compilingContexts; }

    /**
     * Create child contexts for only a portion of the document.
     *
     * \param node The AST node which corresponds to the context to parse
     * \param parent The DUContext which encompasses the \a node.
     * \returns The DUContext which was reparsed, ie. \a parent.
     */
    DUContext* buildSubContexts(T* node, DUContext* parent)
    {
        //     m_compilingContexts = true;
        //     m_recompiling = false;
        setContextOnNode(node, parent);
        {
            openContext(contextFromNode(node));
            startVisiting(node);
            closeContext();
        }

        m_compilingContexts = false;

        if (contextFromNode(node) == parent) {
            qDebug() <<
                "Error in AbstractContextBuilder::buildSubContexts(...): du-context was not replaced with new one";
            DUChainWriteLocker lock(DUChain::lock());
            deleteContextOnNode(node);
        }

        return contextFromNode(node);
    }

    /**
     * Delete the DUContext which is associated with the given \a node,
     * and remove the association.
     *
     * \param node Node which is associated with the context to delete.
     */
    void deleteContextOnNode(T* node)
    {
        delete contextFromNode(node);
        setContextOnNode(node, nullptr);
    }

    /**
     * Open a context, and create / update it if necessary.
     *
     * \param rangeNode The range which encompasses the context.
     * \param type The type of context to open.
     * \param identifier The range which encompasses the name of this context, if one exists.
     * \returns the opened context.
     */
    DUContext* openContext(T* rangeNode, DUContext::ContextType type, NameT* identifier = nullptr)
    {
        if (m_compilingContexts) {
            DUContext* ret = openContextInternal(editorFindRangeForContext(rangeNode,
                                                                           rangeNode), type,
                                                 identifier ? identifierForNode(identifier) : QualifiedIdentifier());
            setContextOnNode(rangeNode, ret);
            return ret;
        } else
        {
            openContext(contextFromNode(rangeNode));
            return currentContext();
        }
    }

    /**
     * Open a context, and create / update it if necessary.
     *
     * \param node The range to associate with the context.
     * \param range A custom range which the context should encompass.
     * \param type The type of context to open.
     * \param identifier The range which encompasses the name of this context, if one exists.
     * \returns the opened context.
     */
    DUContext* openContext(T* node, const RangeInRevision& range, DUContext::ContextType type,
                           NameT* identifier = nullptr)
    {
        if (m_compilingContexts) {
            DUContext* ret = openContextInternal(range, type, identifier ? identifierForNode(
                                                     identifier) : QualifiedIdentifier());
            setContextOnNode(node, ret);
            return ret;
        } else {
            openContext(contextFromNode(node));
            return currentContext();
        }
    }

    /**
     * Open a context, and create / update it if necessary.
     *
     * \param node The range to associate with the context.
     * \param range A custom range which the context should encompass.
     * \param type The type of context to open.
     * \param id The identifier for this context
     * \returns the opened context.
     */
    DUContext* openContext(T* node, const RangeInRevision& range, DUContext::ContextType type,
                           const QualifiedIdentifier& id)
    {
        if (m_compilingContexts) {
            DUContext* ret = openContextInternal(range, type, id);
            setContextOnNode(node, ret);
            return ret;
        } else {
            openContext(contextFromNode(node));
            return currentContext();
        }
    }

    /**
     * Open a context, and create / update it if necessary.
     *
     * \param rangeNode The range which encompasses the context.
     * \param type The type of context to open.
     * \param identifier The identifier which corresponds to the context.
     * \returns the opened context.
     */
    DUContext* openContext(T* rangeNode, DUContext::ContextType type, const QualifiedIdentifier& identifier)
    {
        if (m_compilingContexts) {
            DUContext* ret = openContextInternal(editorFindRangeForContext(rangeNode, rangeNode), type, identifier);
            setContextOnNode(rangeNode, ret);
            return ret;
        } else
        {
            openContext(contextFromNode(rangeNode));
            return currentContext();
        }
    }

    /**
     * Open a context, and create / update it if necessary.
     *
     * \param fromRange The range which starts the context.
     * \param toRange The range which ends the context.
     * \param type The type of context to open.
     * \param identifier The identifier which corresponds to the context.
     * \returns the opened context.
     */
    DUContext* openContext(T* fromRange, T* toRange, DUContext::ContextType type,
                           const QualifiedIdentifier& identifier = QualifiedIdentifier())
    {
        if (m_compilingContexts) {
            DUContext* ret = openContextInternal(editorFindRangeForContext(fromRange, toRange), type, identifier);
            setContextOnNode(fromRange, ret);
            return ret;
        } else
        {
            openContext(contextFromNode(fromRange));
            return currentContext();
        }
    }

    /**
     * Open a newly created or previously existing context.
     *
     * The open context is put on the context stack, and becomes the new
     * currentContext().
     *
     * \warning When you call this, you also have to open a range! If you want to re-use
     * the range associated to the context, use injectContext
     *
     * \param newContext Context to open.
     */
    virtual void openContext(DUContext* newContext)
    {
        m_contextStack.push(newContext);
        m_nextContextStack.push(0);
    }

    /**
     * This can be used to temporarily change the current context.
     * \param ctx The context to be injected
     * */
    void injectContext(DUContext* ctx)
    {
        openContext(ctx);
    }

    /**
     * Use this to close the context previously injected with injectContext.
     * */
    void closeInjectedContext()
    {
        m_contextStack.pop();
        m_nextContextStack.pop();
    }

    /**
     * Close the current DUContext.  When recompiling, this function will remove any
     * contexts that were not encountered in this passing run.
     * \note The DUChain write lock is already held here.
     */
    virtual void closeContext()
    {
        {
            DUChainWriteLocker lock(DUChain::lock());
            //Remove all slaves that were not encountered while parsing
            if (m_compilingContexts)
                currentContext()->cleanIfNotEncountered(m_encountered);
            setEncountered(currentContext());

            m_lastContext = currentContext();
        }

        m_contextStack.pop();
        m_nextContextStack.pop();
    }

    /**
     * Remember that a specific item has been encountered while parsing.
     * All items that are not encountered will be deleted at some stage.
     *
     * \param item duchain item that was encountered.
     * */
    void setEncountered(DUChainBase* item)
    {
        m_encountered.insert(item);
    }

    /**
     * Determine whether the given \a item is in the set of encountered items.
     *
     * @return true if the \a item has been encountered, otherwise false.
     * */
    bool wasEncountered(DUChainBase* item)
    {
        return m_encountered.contains(item);
    }

    /**
     * Set the current identifier to \a id.
     *
     * \param id the new current identifier.
     */
    void setIdentifier(const QString& id)
    {
        m_identifier = Identifier(id);
        m_qIdentifier.push(m_identifier);
    }

    /**
     * Determine the current identifier.
     * \returns the current identifier.
     */
    QualifiedIdentifier qualifiedIdentifier() const
    {
        return m_qIdentifier;
    }

    /**
     * Clears the current identifier.
     */
    void clearQualifiedIdentifier()
    {
        m_qIdentifier.clear();
    }

    /**
     * Retrieve the current context stack.  This function is not expected
     * to be used often and may be phased out.
     *
     * \todo Audit whether access to the context stack is still required, and provide
     *       replacement functionality if possible.
     */
    const Stack<DUContext*>& contextStack() const
    {
        return m_contextStack;
    }

    /**
     * Access the index of the child context which has been encountered.
     *
     * \todo further delineate the role of this function and rename / document better.
     * \todo make private again?
     */
    int& nextContextIndex()
    {
        return m_nextContextStack.top();
    }

    /**
     * Open a context, either creating it if it does not exist, or referencing a previously existing
     * context if already encountered in a previous duchain parse run (when recompiling()).
     *
     * \param range The range of the context.
     * \param type The type of context to create.
     * \param identifier The identifier which corresponds to the context.
     * \returns the opened context.
     */

    virtual DUContext* openContextInternal(const RangeInRevision& range, DUContext::ContextType type,
                                           const QualifiedIdentifier& identifier)
    {
        Q_ASSERT(m_compilingContexts);
        DUContext* ret = nullptr;

        {
            if (recompiling()) {
                DUChainReadLocker readLock(DUChain::lock());
                const QVector<DUContext*>& childContexts = currentContext()->childContexts();

                int currentIndex = nextContextIndex();
                const auto indexedIdentifier = IndexedQualifiedIdentifier(identifier);

                for (; currentIndex < childContexts.count(); ++currentIndex) {
                    DUContext* child = childContexts.at(currentIndex);
                    RangeInRevision childRange = child->range();

                    if (child->type() != type) {
                        continue;
                    }
                    // We cannot update a contexts local scope identifier, that will break many other parts, like e.g.
                    // the CodeModel of child contexts or declarations.
                    // For unnamed child-ranges, we still do range-comparison, because we cannot distinguish them in other ways
                    if ((!identifier.isEmpty() && child->indexedLocalScopeIdentifier() == indexedIdentifier)
                        || (identifier.isEmpty() && child->indexedLocalScopeIdentifier().isEmpty() &&
                            !childRange.isEmpty() && childRange == range)) {
                        // Match
                        ret = child;
                        readLock.unlock();
                        DUChainWriteLocker writeLock(DUChain::lock());

                        ret->clearImportedParentContexts();
                        ++currentIndex;
                        break;
                    }
                }

                if (ret)
                    nextContextIndex() = currentIndex; //If we had a match, jump forward to that position
                ///@todo We should also somehow make sure we don't get quadratic worst-case effort while updating.
            }

            if (!ret) {
                DUChainWriteLocker writeLock(DUChain::lock());

                ret = newContext(range);
                ret->setType(type);

                if (!identifier.isEmpty())
                    ret->setLocalScopeIdentifier(identifier);

                setInSymbolTable(ret);
            } else {
                DUChainWriteLocker writeLock(DUChain::lock());
                Q_ASSERT(ret->localScopeIdentifier() == identifier);
                if (ret->parentContext())
                    ret->setRange(range);
            }
        }

        m_encountered.insert(ret);
        openContext(ret);
        return ret;
    }

    ///This function should call context->setInSymbolTable(..) with an appropriate decision. The duchain is write-locked when this is called.
    virtual void setInSymbolTable(DUContext* context)
    {
        if (!context->parentContext()->inSymbolTable()) {
            context->setInSymbolTable(false);
            return;
        }
        DUContext::ContextType type = context->type();
        context->setInSymbolTable(
            type == DUContext::Class || type == DUContext::Namespace || type == DUContext::Global || type == DUContext::Helper ||
            type == DUContext::Enum);
    }

    /// @returns the current url/path ot the document we are parsing
    IndexedString document() const
    {
        return m_url;
    }

private:

    Identifier m_identifier;
    IndexedString m_url;
    QualifiedIdentifier m_qIdentifier;
    bool m_compilingContexts : 1;
    bool m_recompiling : 1;
    Stack<int> m_nextContextStack;
    DUContext* m_lastContext;
    //Here all valid declarations/uses/... will be collected
    QSet<DUChainBase*> m_encountered;
    Stack<DUContext*> m_contextStack;
};
}

#endif