context.cpp 47.2 KB
Newer Older
1
2
3
/*
 * This file is part of KDevelop
 * Copyright 2014 Milian Wolff <mail@milianw.de>
Sergey Kalinichev's avatar
Sergey Kalinichev committed
4
 * Copyright 2015 Sergey Kalinichev <kalinichev.so.0@gmail.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License or (at your option) version 3 or any later version
 * accepted by the membership of KDE e.V. (or its successor approved
 * by the membership of KDE e.V.), which shall act as a proxy
 * defined in Section 14 of version 3 of the license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "context.h"

25
#include <QRegularExpression>
26
#include <QStandardPaths>
27

28
29
30
#include <interfaces/icore.h>
#include <interfaces/idocumentcontroller.h>

31
32
33
#include <language/duchain/duchainlock.h>
#include <language/duchain/ducontext.h>
#include <language/duchain/topducontext.h>
34
#include <language/duchain/declaration.h>
35
#include <language/duchain/classmemberdeclaration.h>
36
#include <language/duchain/classdeclaration.h>
37
#include <language/duchain/duchainutils.h>
38
#include <language/duchain/persistentsymboltable.h>
39
#include <language/duchain/types/integraltype.h>
40
#include <language/duchain/types/functiontype.h>
41
#include <language/duchain/types/pointertype.h>
42
#include <language/duchain/types/typealiastype.h>
43
#include <language/duchain/types/typeutils.h>
44
#include <language/codecompletion/codecompletionmodel.h>
45
#include <language/codecompletion/normaldeclarationcompletionitem.h>
46
#include <util/foregroundlock.h>
47

48
#include "../util/clangdebug.h"
49
#include "../util/clangtypes.h"
50
#include "../util/clangutils.h"
51
#include "../duchain/clangdiagnosticevaluator.h"
52
#include "../duchain/parsesession.h"
53
#include "../duchain/duchainutils.h"
54
#include "../duchain/navigationwidget.h"
55
#include "../clangsettings/clangsettingsmanager.h"
56

Kevin Funk's avatar
Kevin Funk committed
57
#include <functional>
58
#include <memory>
Kevin Funk's avatar
Kevin Funk committed
59

60
#include <KTextEditor/Document>
61
#include <KTextEditor/View>
62
63
64
65

using namespace KDevelop;

namespace {
Kevin Funk's avatar
Kevin Funk committed
66
67
/// Maximum return-type string length in completion items
const int MAX_RETURN_TYPE_STRING_LENGTH = 20;
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
/// Priority of code-completion results. NOTE: Keep in sync with Clang code base.
enum CodeCompletionPriority {
  /// Priority for the next initialization in a constructor initializer list.
  CCP_NextInitializer = 7,
  /// Priority for an enumeration constant inside a switch whose condition is of the enumeration type.
  CCP_EnumInCase = 7,

  CCP_LocalDeclarationMatch = 8,

  CCP_DeclarationMatch = 12,

  CCP_LocalDeclarationSimiliar = 17,
  /// Priority for a send-to-super completion.
  CCP_SuperCompletion = 20,

  CCP_DeclarationSimiliar = 25,
  /// Priority for a declaration that is in the local scope.
  CCP_LocalDeclaration = 34,
  /// Priority for a member declaration found from the current method or member function.
  CCP_MemberDeclaration = 35,
  /// Priority for a language keyword (that isn't any of the other categories).
  CCP_Keyword = 40,
  /// Priority for a code pattern.
  CCP_CodePattern = 40,
  /// Priority for a non-type declaration.
  CCP_Declaration = 50,
  /// Priority for a type.
  CCP_Type = CCP_Declaration,
  /// Priority for a constant value (e.g., enumerator).
  CCP_Constant = 65,
  /// Priority for a preprocessor macro.
  CCP_Macro = 70,
  /// Priority for a nested-name-specifier.
  CCP_NestedNameSpecifier = 75,
  /// Priority for a result that isn't likely to be what the user wants, but is included for completeness.
  CCP_Unlikely = 80
};

107
108
109
110
111
/**
 * Common base class for Clang code completion items.
 */
template<class Base>
class CompletionItem : public Base
112
113
{
public:
114
    CompletionItem(const QString& display, const QString& prefix)
115
116
117
        : Base()
        , m_display(display)
        , m_prefix(prefix)
118
        , m_unimportant(false)
119
120
    {
    }
121

122
    ~CompletionItem() override = default;
123

124
125
126
127
128
129
130
131
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* /*model*/) const override
    {
        if (role == Qt::DisplayRole) {
            if (index.column() == CodeCompletionModel::Prefix) {
                return m_prefix;
            } else if (index.column() == CodeCompletionModel::Name) {
                return m_display;
            }
132
        }
133
        return {};
134
    }
135

136
137
138
139
140
    void markAsUnimportant()
    {
        m_unimportant = true;
    }

141
protected:
142
143
    QString m_display;
    QString m_prefix;
144
    bool m_unimportant;
145
146
};

147
148
149
class OverrideItem : public CompletionItem<CompletionTreeItem>
{
public:
Kevin Funk's avatar
Kevin Funk committed
150
    OverrideItem(const QString& nameAndParams, const QString& returnType)
151
152
        : CompletionItem<KDevelop::CompletionTreeItem>(
              nameAndParams,
153
              i18n("Override %1", returnType)
154
          )
155
        , m_returnType(returnType)
156
157
158
159
160
161
162
    {
    }

    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
        if (role == Qt::DecorationRole) {
            if (index.column() == KTextEditor::CodeCompletionModel::Icon) {
163
                static const QIcon icon = QIcon::fromTheme(QStringLiteral("CTparents"));
164
165
166
167
168
                return icon;
            }
        }
        return CompletionItem<CompletionTreeItem>::data(index, role, model);
    }
169
170
171

    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
    {
172
        view->document()->replaceText(word, m_returnType + QLatin1Char(' ') + m_display.replace(QRegularExpression(QStringLiteral("\\s*=\\s*0")), QString()) + QLatin1String(" override;"));
173
174
175
176
    }

private:
    QString m_returnType;
177
178
};

179
180
181
182
/**
 * Specialized completion item class for items which are represented by a Declaration
 */
class DeclarationItem : public CompletionItem<NormalDeclarationCompletionItem>
183
{
184
185
public:
    DeclarationItem(Declaration* dec, const QString& display, const QString& prefix, const QString& replacement)
186
187
        : CompletionItem<NormalDeclarationCompletionItem>(display, prefix)
        , m_replacement(replacement)
188
189
    {
        m_declaration = dec;
190
    }
191
192
193

    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
Kevin Funk's avatar
Kevin Funk committed
194
195
        if (role == CodeCompletionModel::MatchQuality && m_matchQuality) {
            return m_matchQuality;
196
197
        }

198
199
200
201
202
        auto ret = CompletionItem<NormalDeclarationCompletionItem>::data(index, role, model);
        if (ret.isValid()) {
            return ret;
        }
        return NormalDeclarationCompletionItem::data(index, role, model);
203
    }
204

Kevin Funk's avatar
Kevin Funk committed
205
    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
206
207
208
209
210
211
212
213
214
    {
        QString repl = m_replacement;
        DUChainReadLocker lock;

        if(!m_declaration){
            return;
        }

        if(m_declaration->isFunctionDeclaration()) {
215
216
217
218
            const auto functionType = m_declaration->type<FunctionType>();
            if (!functionType)
                return;

219
            auto doc = view->document();
220
221
222
223
224
225
226
227
228
229
230
231
232

            // Function pointer?
            bool funcptr = false;
            const auto line = doc->line(word.start().line());
            auto pos = word.end().column() - 1;
            while ( pos > 0 && (line.at(pos).isLetterOrNumber() || line.at(pos) == QLatin1Char(':')) ) {
                pos--;
                if ( line.at(pos) == QLatin1Char('&') ) {
                    funcptr = true;
                    break;
                }
            }

233
234
            auto restEmpty = doc->characterAt(word.end() + KTextEditor::Cursor{0, 1}) == QChar();

235
            bool didAddParentheses = false;
236
            if ( !funcptr && doc->characterAt(word.end()) != QLatin1Char('(') ) {
237
                repl += QLatin1String("()");
238
                didAddParentheses = true;
239
            }
Kevin Funk's avatar
Kevin Funk committed
240
            view->document()->replaceText(word, repl);
241
            if (functionType->indexedArgumentsSize() && didAddParentheses) {
Kevin Funk's avatar
Kevin Funk committed
242
                view->setCursorPosition(word.start() + KTextEditor::Cursor(0, repl.size() - 1));
243
            }
244
            auto returnTypeIntegral = functionType->returnType().cast<IntegralType>();
245
246
            if ( restEmpty && !funcptr && returnTypeIntegral && returnTypeIntegral->dataType() == IntegralType::TypeVoid ) {
                // function returns void and rest of line is empty -- nothing can be done with the result
247
                if (functionType->indexedArgumentsSize() ) {
248
249
250
251
252
253
254
255
256
                    // we placed the cursor inside the ()
                    view->document()->insertText(view->cursorPosition() + KTextEditor::Cursor(0, 1), QStringLiteral(";"));
                }
                else {
                    // we placed the cursor after the ()
                    view->document()->insertText(view->cursorPosition(), QStringLiteral(";"));
                    view->setCursorPosition(view->cursorPosition() + KTextEditor::Cursor{0, 1});
                }
            }
257
        } else {
Kevin Funk's avatar
Kevin Funk committed
258
            view->document()->replaceText(word, repl);
259
260
261
        }
    }

262
263
264
265
266
267
268
    bool createsExpandingWidget() const override
    {
        return true;
    }

    QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override
    {
269
        return new ClangNavigationWidget(m_declaration, KDevelop::AbstractNavigationWidget::EmbeddableWidget);
270
    }
271

Kevin Funk's avatar
Kevin Funk committed
272
273
274
275
276
    int matchQuality() const
    {
        return m_matchQuality;
    }

277
    ///Sets match quality from 0 to 10. 10 is the best fit.
Kevin Funk's avatar
Kevin Funk committed
278
    void setMatchQuality(int value)
279
    {
Kevin Funk's avatar
Kevin Funk committed
280
        m_matchQuality = value;
281
282
    }

283
284
285
286
287
    void setInheritanceDepth(int depth)
    {
        m_inheritanceDepth = depth;
    }

288
289
290
291
292
293
294
295
296
297
    int argumentHintDepth() const override
    {
        return m_depth;
    }

    void setArgumentHintDepth(int depth)
    {
        m_depth = depth;
    }

298
protected:
Kevin Funk's avatar
Kevin Funk committed
299
    int m_matchQuality = 0;
300
    int m_depth = 0;
301
    QString m_replacement;
302
};
303

304
305
306
307
308
309
310
311
312
313
314
315
316
class ImplementsItem : public DeclarationItem
{
public:
    static QString replacement(const FuncImplementInfo& info)
    {
        QString replacement = info.templatePrefix;
        if (!info.isDestructor && !info.isConstructor) {
            replacement += info.returnType + QLatin1Char(' ');
        }
        replacement += info.prototype + QLatin1String("\n{\n}\n");
        return replacement;
    }

317
    explicit ImplementsItem(const FuncImplementInfo& item)
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
        : DeclarationItem(item.declaration.data(), item.prototype,
            i18n("Implement %1", item.isConstructor ? QStringLiteral("<constructor>") :
                                   item.isDestructor ? QStringLiteral("<destructor>") : item.returnType),
            replacement(item)
          )
    {
    }

    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
        if (index.column() == CodeCompletionModel::Arguments) {
            // our display string already contains the arguments
            return {};
        }
        return DeclarationItem::data(index, role, model);
    }

    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
    {
        view->document()->replaceText(word, m_replacement);
    }
};
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355

class ArgumentHintItem : public DeclarationItem
{
public:
    struct CurrentArgumentRange
    {
        int start;
        int end;
    };

    ArgumentHintItem(Declaration* decl,  const QString& prefix, const QString& name, const QString& arguments, const CurrentArgumentRange& range)
        : DeclarationItem(decl, name, prefix, {})
        , m_range(range)
        , m_arguments(arguments)
    {}

Milian Wolff's avatar
Milian Wolff committed
356
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
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
    {
        if (role == CodeCompletionModel::CustomHighlight && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
            QList<QVariant> highlighting;
            highlighting << QVariant(m_range.start);
            highlighting << QVariant(m_range.end);
            QTextCharFormat boldFormat;
            boldFormat.setFontWeight(QFont::Bold);
            highlighting << boldFormat;
            return highlighting;
        }

        if (role == CodeCompletionModel::HighlightingMethod && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
            return QVariant(CodeCompletionModel::CustomHighlighting);
        }

        if (index.column() == CodeCompletionModel::Arguments && !m_declaration) {
            return m_arguments;
        }

        return DeclarationItem::data(index, role, model);
    }

private:
    CurrentArgumentRange m_range;
    QString m_arguments;
};

384
385
386
/**
 * A minimalistic completion item for macros and such
 */
387
388
389
class SimpleItem : public CompletionItem<CompletionTreeItem>
{
public:
390
    SimpleItem(const QString& display, const QString& prefix, const QString& replacement, const QIcon& icon = QIcon())
391
392
        : CompletionItem<CompletionTreeItem>(display, prefix)
        , m_replacement(replacement)
393
        , m_icon(icon)
394
395
396
397
398
399
400
401
    {
    }

    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
    {
        view->document()->replaceText(word, m_replacement);
    }

402
403
404
405
406
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
        if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) {
            return m_icon;
        }
407
408
409
        if (role == KDevelop::CodeCompletionModel::UnimportantItemRole) {
            return m_unimportant;
        }
410
411
412
        return CompletionItem<CompletionTreeItem>::data(index, role, model);
    }

413
414
private:
    QString m_replacement;
415
    QIcon m_icon;
416
};
417

418
419
420
/**
 * Return true in case position @p position represents a cursor inside a comment
 */
Kevin Funk's avatar
Kevin Funk committed
421
bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position)
422
423
424
425
426
427
428
{
    if (!position.isValid()) {
        return false;
    }

    // TODO: This may get very slow for a large TU, investigate if we can improve this function
    auto begin = clang_getLocation(unit, file, 1, 1);
Kevin Funk's avatar
Kevin Funk committed
429
    auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1);
430
431
432
433
    CXSourceRange range = clang_getRange(begin, end);

    // tokenize the whole range from the start until 'position'
    // if we detect a comment token at this position, return true
434
435
    const ClangTokens tokens(unit, range);
    for (CXToken token : tokens) {
436
437
438
439
440
441
        CXTokenKind tokenKind = clang_getTokenKind(token);
        if (tokenKind != CXToken_Comment) {
            continue;
        }

        auto range = ClangRange(clang_getTokenExtent(unit, token));
Kevin Funk's avatar
Kevin Funk committed
442
        if (range.toRange().contains(position)) {
443
444
445
446
447
448
            return true;
        }
    }
    return false;
}

Kevin Funk's avatar
Kevin Funk committed
449
450
451
QString& elideStringRight(QString& str, int length)
{
    if (str.size() > length + 3) {
452
        return str.replace(length, str.size() - length, QStringLiteral("..."));
Kevin Funk's avatar
Kevin Funk committed
453
454
455
456
    }
    return str;
}

Kevin Funk's avatar
Kevin Funk committed
457
/**
458
 * @return Value suited for @ref CodeCompletionModel::MatchQuality in the range [0.0, 10.0] (the higher the better)
Kevin Funk's avatar
Kevin Funk committed
459
460
461
462
463
464
 *
 * See http://clang.llvm.org/doxygen/CodeCompleteConsumer_8h_source.html for list of priorities
 * They (currently) have a range from [-3, 80] (the lower, the better)
 */
int codeCompletionPriorityToMatchQuality(unsigned int completionPriority)
{
465
    return 10u - qBound(0u, completionPriority, 80u) / 8;
Kevin Funk's avatar
Kevin Funk committed
466
467
}

468
int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority)
469
{
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
    const auto modifier = 4;
    if (type) {
        const auto whichType = type->whichType();
        if (whichType == AbstractType::TypePointer || whichType == AbstractType::TypeReference) {
            // Clang considers all pointers as similar, this is not what we want.
            completionPriority += modifier;
        } else if (whichType == AbstractType::TypeStructure) {
            // Clang considers all classes as similar too...
            completionPriority += modifier;
        } else if (whichType == AbstractType::TypeDelayed) {
            completionPriority += modifier;
        } else if (whichType == AbstractType::TypeAlias) {
            auto aliasedType = type.cast<TypeAliasType>();
            return adjustPriorityForType(aliasedType ? aliasedType->type() : AbstractType::Ptr(), completionPriority);
        } else if (whichType == AbstractType::TypeFunction) {
            auto functionType = type.cast<FunctionType>();
            return adjustPriorityForType(functionType ? functionType->returnType() : AbstractType::Ptr(), completionPriority);
487
        }
488
489
    } else {
        completionPriority += modifier;
490
491
492
493
494
    }

    return completionPriority;
}

495
496
497
498
499
500
501
502
503
504
/// Adjusts priority for the @p decl
int adjustPriorityForDeclaration(Declaration* decl, unsigned int completionPriority)
{
    if(completionPriority < CCP_LocalDeclarationSimiliar || completionPriority > CCP_SuperCompletion){
        return completionPriority;
    }

    return adjustPriorityForType(decl->abstractType(), completionPriority);
}

505
506
507
/**
 * @return Whether the declaration represented by identifier @p identifier qualifies as completion result
 *
Kevin Funk's avatar
Kevin Funk committed
508
509
 * For example, we don't want to offer SomeClass::SomeClass as completion item to the user
 * (otherwise we'd end up generating code such as 's.SomeClass();')
510
511
512
513
514
515
516
517
518
519
520
521
522
523
 */
bool isValidCompletionIdentifier(const QualifiedIdentifier& identifier)
{
    const int count = identifier.count();
    if (identifier.count() < 2) {
        return true;
    }

    const Identifier scope = identifier.at(count-2);
    const Identifier id = identifier.last();
    if (scope == id) {
        return false; // is constructor
    }
    const QString idString = id.toString();
524
    if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) {
525
526
527
528
529
        return false; // is destructor
    }
    return true;
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
/**
 * @return Whether the declaration represented by identifier @p identifier qualifies as "special" completion result
 *
 * "Special" completion results are items that are likely not regularly used.
 *
 * Examples:
 * - 'SomeClass::operator=(const SomeClass&)'
 */
bool isValidSpecialCompletionIdentifier(const QualifiedIdentifier& identifier)
{
    if (identifier.count() < 2) {
        return false;
    }

    const Identifier id = identifier.last();
    const QString idString = id.toString();
546
    if (idString.startsWith(QLatin1String("operator="))) {
547
548
549
550
551
        return true; // is assignment operator
    }
    return false;
}

552
Declaration* findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, QSet<Declaration*>& handled)
553
{
554
    PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().getDeclarations(qid);
555

556
557
558
    const auto top = ctx->topContext();
    const auto& importedContexts = top->importedParentContexts();

559
    for (auto it = decl.iterator(); it; ++it) {
560
561
562
563
564
565
566
567
568
569
570
571
        // if the context is not included, then this match is not correct for our consideration
        // this fixes issues where we used to include matches from files that did not have
        // anything to do with the current TU, e.g. the main from a different file or stuff like that
        // it also reduces the chance of us picking up a function of the same name from somewhere else
        // also, this makes sure the context has the correct language and we don't get confused by stuff
        // from other language plugins
        if (std::none_of(importedContexts.begin(), importedContexts.end(), [it] (const DUContext::Import& import) {
            return import.topContextIndex() == it->indexedTopContext().index();
        })) {
            continue;
        }

572
        auto declaration = it->declaration();
573
574
575
576
577
578
579
        if (!declaration) {
            // Mitigate problems such as: Cannot load a top-context from file "/home/kfunk/.cache/kdevduchain/kdevelop-{foo}/topcontexts/6085"
            //  - the required language-support for handling ID 55 is probably not loaded
            qCWarning(KDEV_CLANG) << "Detected an invalid declaration for" << qid;
            continue;
        }

580
        if (declaration->kind() == Declaration::Instance && !declaration->isFunctionDeclaration()) {
581
582
            break;
        }
583
584
        if (!handled.contains(declaration)) {
            handled.insert(declaration);
585
            return declaration;
586
587
588
589
590
591
592
        }
    }

    const auto foundDeclarations = ctx->findDeclarations(qid, position);
    for (auto dec : foundDeclarations) {
        if (!handled.contains(dec)) {
            handled.insert(dec);
593
            return dec;
594
595
596
        }
    }

597
    return nullptr;
598
599
}

600
/// If any parent of this context is a class, the closest class declaration is returned, nullptr otherwise
601
Declaration* classDeclarationForContext(const DUContextPointer& context, const CursorInRevision& position)
602
603
604
605
606
607
{
    auto parent = context;
    while (parent) {
        if (parent->type() == DUContext::Class) {
            break;
        }
608
609
610
611
612
613
614
615

        if (auto owner = parent->owner()) {
            // Work-around for out-of-line methods. They have Helper context instead of Class context
            if (owner->context() && owner->context()->type() == DUContext::Helper) {
                auto qid = owner->qualifiedIdentifier();
                qid.pop();

                QSet<Declaration*> tmp;
616
                auto decl = findDeclaration(qid, context, position, tmp);
617
618
619
620
621
622
623

                if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
                    parent = decl->internalContext();
                    break;
                }
            }
        }
624
625
626
627
628
629
        parent = parent->parentContext();
    }

    return parent ? parent->owner() : nullptr;
}

630
631
632
class LookAheadItemMatcher
{
public:
633
    explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
        : m_topContext(ctx)
        , m_enabled(ClangSettingsManager::self()->codeCompletionSettings().lookAhead)
    {}

    /// Adds all local declarations for @p declaration into possible look-ahead items.
    void addDeclarations(Declaration* declaration)
    {
        if (!m_enabled) {
            return;
        }

        if (declaration->kind() != Declaration::Instance) {
            return;
        }

        auto type = typeForDeclaration(declaration);
        auto identifiedType = dynamic_cast<const IdentifiedType*>(type.data());
        if (!identifiedType) {
            return;
        }

        addDeclarationsForType(identifiedType, declaration);
    }

    /// Add type for matching. This type'll be used for filtering look-ahead items
    /// Only items with @p type will be returned through @sa matchedItems
    void addMatchedType(const IndexedType& type)
    {
        matchedTypes.insert(type);
    }

    /// @return look-ahead items that math given types. @sa addMatchedType
    QList<CompletionTreeItemPointer> matchedItems()
    {
        QList<CompletionTreeItemPointer> lookAheadItems;
669
670
        for (const auto& pair: possibleLookAheadDeclarations) {
            auto decl = pair.first;
671
            if (matchedTypes.contains(decl->indexedType())) {
672
673
674
675
                auto parent = pair.second;
                const QString access = parent->abstractType()->whichType() == AbstractType::TypePointer
                                 ? QStringLiteral("->") : QStringLiteral(".");
                const QString text = parent->identifier().toString() + access + decl->identifier().toString();
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
                auto item = new DeclarationItem(decl, text, {}, text);
                item->setMatchQuality(8);
                lookAheadItems.append(CompletionTreeItemPointer(item));
            }
        }

        return lookAheadItems;
    }

private:
    AbstractType::Ptr typeForDeclaration(const Declaration* decl)
    {
        return TypeUtils::targetType(decl->abstractType(), m_topContext.data());
    }

    void addDeclarationsForType(const IdentifiedType* identifiedType, Declaration* declaration)
    {
        if (auto typeDecl = identifiedType->declaration(m_topContext.data())) {
            if (dynamic_cast<ClassDeclaration*>(typeDecl->logicalDeclaration(m_topContext.data()))) {
                if (!typeDecl->internalContext()) {
                    return;
                }

                for (auto localDecl : typeDecl->internalContext()->localDeclarations()) {
                    if(localDecl->identifier().isEmpty()){
                        continue;
                    }

                    if(auto classMember = dynamic_cast<ClassMemberDeclaration*>(localDecl)){
                        // TODO: Also add protected/private members if completion is inside this class context.
                        if(classMember->accessPolicy() != Declaration::Public){
                            continue;
                        }
                    }

                    if(!declaration->abstractType()){
                        continue;
                    }

                    if (declaration->abstractType()->whichType() == AbstractType::TypeIntegral) {
                        if (auto integralType = declaration->abstractType().cast<IntegralType>()) {
                            if (integralType->dataType() == IntegralType::TypeVoid) {
                                continue;
                            }
                        }
                    }

723
                    possibleLookAheadDeclarations.insert({localDecl, declaration});
724
725
726
727
728
729
730
731
732
733
734
735
                }
            }
        }
    }

    // Declaration and it's context
    typedef QPair<Declaration*, Declaration*> DeclarationContext;

    /// Types of declarations that look-ahead completion items can have
    QSet<IndexedType> matchedTypes;

    // List of declarations that can be added to the Look Ahead group
736
737
    // Second declaration represents context
    QSet<DeclarationContext> possibleLookAheadDeclarations;
738
739
740
741
742
743

    TopDUContextPointer m_topContext;

    bool m_enabled;
};

744
745
746
747
748
749
750
751
752
753
754
struct MemberAccessReplacer : public QObject
{
    Q_OBJECT

public:
    enum Type {
        None,
        DotToArrow,
        ArrowToDot
    };

755
public Q_SLOTS:
756
757
758
759
760
761
762
763
764
765
766
767
    void replaceCurrentAccess(MemberAccessReplacer::Type type)
    {
        if (auto document = ICore::self()->documentController()->activeDocument()) {
            if (auto textDocument = document->textDocument()) {
                auto activeView = document->activeTextView();
                if (!activeView) {
                    return;
                }

                auto cursor = activeView->cursorPosition();

                QString oldAccess, newAccess;
768
                if (type == ArrowToDot) {
769
770
771
772
773
774
775
776
                    oldAccess = QStringLiteral("->");
                    newAccess = QStringLiteral(".");
                } else {
                    oldAccess = QStringLiteral(".");
                    newAccess = QStringLiteral("->");
                }

                auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor);
777
778
779
780
781
782
783
784

                // This code needed for testReplaceMemberAccess test
                // Maybe we should do a similar thing for '->' to '.' direction, but this is not so important
                while (textDocument->text(oldRange) == QLatin1String(" ") && oldRange.start().column() >= 0) {
                    oldRange = KTextEditor::Range({oldRange.start().line(), oldRange.start().column() - 1},
                                                  {oldRange.end().line(), oldRange.end().column() - 1});
                }

785
786
787
788
789
790
791
792
793
                if (oldRange.start().column() >= 0 && textDocument->text(oldRange) == oldAccess) {
                    textDocument->replaceText(oldRange, newAccess);
                }
            }
        }
    }
};
static MemberAccessReplacer s_memberAccessReplacer;

794
795
}

796
797
Q_DECLARE_METATYPE(MemberAccessReplacer::Type)

798
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
Kevin Funk's avatar
Kevin Funk committed
799
                                                       const ParseSessionData::Ptr& sessionData,
800
                                                       const QUrl& url,
Kevin Funk's avatar
Kevin Funk committed
801
                                                       const KTextEditor::Cursor& position,
802
803
                                                       const QString& text,
                                                       const QString& followingText
804
                                                      )
805
    : CodeCompletionContext(context, text + followingText, CursorInRevision::castFromSimpleCursor(position), 0)
806
    , m_results(nullptr, clang_disposeCodeCompleteResults)
Kevin Funk's avatar
Kevin Funk committed
807
    , m_parseSessionData(sessionData)
808
{
809
    qRegisterMetaType<MemberAccessReplacer::Type>();
810
811
    const QByteArray file = url.toLocalFile().toUtf8();
    ParseSession session(m_parseSessionData);
812
813
814
815
816
817
818
819

    QVector<UnsavedFile> otherUnsavedFiles;
    {
        ForegroundLock lock;
        otherUnsavedFiles = ClangUtils::unsavedFiles();
    }
    QVector<CXUnsavedFile> allUnsaved;

Kevin Funk's avatar
Kevin Funk committed
820
821
822
    {
        const unsigned int completeOptions = clang_defaultCodeCompleteOptions();

823
824
825
826
        CXUnsavedFile unsaved;
        unsaved.Filename = file.constData();
        const QByteArray content = m_text.toUtf8();
        unsaved.Contents = content.constData();
827
828
829
830
831
832
        unsaved.Length = content.size();

        for ( const auto& f : otherUnsavedFiles ) {
            allUnsaved.append(f.toClangApi());
        }
        allUnsaved.append(unsaved);
833

834
835
        m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
                        position.line() + 1, position.column() + 1,
836
                        allUnsaved.data(), allUnsaved.size(),
837
                        completeOptions));
Kevin Funk's avatar
Kevin Funk committed
838
839

        if (!m_results) {
840
            qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file;
841
            return;
Kevin Funk's avatar
Kevin Funk committed
842
        }
843

844
845
846
847
848
849
850
851
852
        auto numDiagnostics = clang_codeCompleteGetNumDiagnostics(m_results.get());
        for (uint i = 0; i < numDiagnostics; i++) {
            auto diagnostic = clang_codeCompleteGetDiagnostic(m_results.get(), i);
            auto diagnosticType = ClangDiagnosticEvaluator::diagnosticType(diagnostic);
            clang_disposeDiagnostic(diagnostic);
            if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithArrowProblem || diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) {
                MemberAccessReplacer::Type replacementType;
                if (diagnosticType == ClangDiagnosticEvaluator::ReplaceWithDotProblem) {
                    replacementType = MemberAccessReplacer::ArrowToDot;
853
854
                } else {
                    replacementType = MemberAccessReplacer::DotToArrow;
855
856
857
858
859
860
861
862
863
864
                }

                QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection,
                                          Q_ARG(MemberAccessReplacer::Type, replacementType));

                m_valid = false;
                return;
            }
        }

865
866
867
868
        auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros;
        if (!addMacros) {
            m_filters |= NoMacros;
        }
869
    }
870

871
872
873
874
875
876
877
878
879
880
881
882
    if (!m_results->NumResults) {
        const auto trimmedText = text.trimmed();
        if (trimmedText.endsWith(QLatin1Char('.'))) {
            // TODO: This shouldn't be needed if Clang provided diagnostic.
            // But it doesn't always do it, so let's try to manually determine whether '.' is used instead of '->'
            m_text = trimmedText.left(trimmedText.size() - 1);
            m_text += QStringLiteral("->");

            CXUnsavedFile unsaved;
            unsaved.Filename = file.constData();
            const QByteArray content = m_text.toUtf8();
            unsaved.Contents = content.constData();
883
884
            unsaved.Length = content.size();
            allUnsaved[allUnsaved.size() - 1] = unsaved;
885
886

            m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
887
                                                 position.line() + 1, position.column() + 1 + 1,
888
                                                 allUnsaved.data(), allUnsaved.size(),
889
890
891
892
893
894
895
896
897
898
899
900
                                                 clang_defaultCodeCompleteOptions()));

            if (m_results && m_results->NumResults) {
                QMetaObject::invokeMethod(&s_memberAccessReplacer, "replaceCurrentAccess", Qt::QueuedConnection,
                                          Q_ARG(MemberAccessReplacer::Type, MemberAccessReplacer::DotToArrow));
            }

            m_valid = false;
            return;
        }
    }

901
    // check 'isValidPosition' after parsing the new content
902
903
    auto clangFile = session.file(file);
    if (!isValidPosition(session.unit(), clangFile)) {
904
905
906
        m_valid = false;
        return;
    }
907

908
    m_completionHelper.computeCompletions(session, clangFile, position);
909
910
911
912
913
}

ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
914

915
bool ClangCodeCompletionContext::isValidPosition(CXTranslationUnit unit, CXFile file) const
916
{
917
    if (isInsideComment(unit, file, m_position.castToSimpleCursor())) {
918
        clangDebug() << "Invalid completion context: Inside comment";
919
920
921
        return false;
    }
    return true;
922
923
}

924
QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/)
925
{
926
927
928
929
    if (!m_valid || !m_duContext || !m_results) {
        return {};
    }

930
931
    const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position));

Kevin Funk's avatar
Kevin Funk committed
932
    /// Normal completion items, such as 'void Foo::foo()'
933
    QList<CompletionTreeItemPointer> items;
Kevin Funk's avatar
Kevin Funk committed
934
935
936
    /// Stuff like 'Foo& Foo::operator=(const Foo&)', etc. Not regularly used by our users.
    QList<CompletionTreeItemPointer> specialItems;
    /// Macros from the current context
937
    QList<CompletionTreeItemPointer> macros;
Kevin Funk's avatar
Kevin Funk committed
938
    /// Builtins reported by Clang
939
    QList<CompletionTreeItemPointer> builtin;
940

941
    QSet<Declaration*> handled;
942

943
944
    LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));

945
946
947
    // If ctx is/inside the Class context, this represents that context.
    const auto currentClassContext = classDeclarationForContext(ctx, m_position);

948
    clangDebug() << "Clang found" << m_results->NumResults << "completion results";
949

950
    for (uint i = 0; i < m_results->NumResults; ++i) {
951
952
953
954
        if (abort) {
            return {};
        }

955
        auto result = m_results->Results[i];
956
957

        const auto availability = clang_getCompletionAvailability(result.CompletionString);
958
        if (availability == CXAvailability_NotAvailable) {
959
960
961
            continue;
        }

Kevin Funk's avatar
Kevin Funk committed
962
963
964
965
966
        const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition;
        if (isMacroDefinition && m_filters & NoMacros) {
            continue;
        }

967
968
969
970
971
972
973
974
975
976
        const bool isBuiltin = (result.CursorKind == CXCursor_NotImplemented);
        if (isBuiltin && m_filters & NoBuiltins) {
            continue;
        }

        const bool isDeclaration = !isMacroDefinition && !isBuiltin;
        if (isDeclaration && m_filters & NoDeclarations) {
            continue;
        }

977
        if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) {
978
979
980
            continue;
        }

981
        // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items.
982
        QString typed;
Milian Wolff's avatar
Milian Wolff committed
983
        // the return type of a function e.g.
984
        QString resultType;
Milian Wolff's avatar
Milian Wolff committed
985
        // the replacement text when an item gets executed
986
        QString replacement;
987
988
989
990

        QString arguments;

        ArgumentHintItem::CurrentArgumentRange argumentRange;
Milian Wolff's avatar
Milian Wolff committed
991
992
        //BEGIN function signature parsing
        // nesting depth of parentheses
993
994
        int parenDepth = 0;
        enum FunctionSignatureState {
Milian Wolff's avatar
Milian Wolff committed
995
            // not yet inside the function signature
996
            Before,
Milian Wolff's avatar
Milian Wolff committed
997
            // any token is part of the function signature now
998
            Inside,
Milian Wolff's avatar
Milian Wolff committed
999
            // finished parsing the function signature
1000
1001
            After
        };
Milian Wolff's avatar
Milian Wolff committed
1002
        // current state
1003
        FunctionSignatureState signatureState = Before;
Milian Wolff's avatar
Milian Wolff committed
1004
        //END function signature parsing
Kevin Funk's avatar
Kevin Funk committed
1005

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
        std::function<void (CXCompletionString)> processChunks = [&] (CXCompletionString completionString) {
            const uint chunks = clang_getNumCompletionChunks(completionString);
            for (uint j = 0; j < chunks; ++j) {
                const auto kind = clang_getCompletionChunkKind(completionString, j);
                if (kind == CXCompletionChunk_Optional) {
                    completionString = clang_getCompletionChunkCompletionString(completionString, j);
                    if (completionString) {
                        processChunks(completionString);
                    }
                    continue;
1016
                }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1017
1018
1019
1020
1021
1022
1023
1024

                // We don't need function signature for declaration items, we can get it directly from the declaration. Also adding the function signature to the "display" would break the "Detailed completion" option.
                if (isDeclaration && !typed.isEmpty()) {
#if CINDEX_VERSION_MINOR >= 30
                    // TODO: When parent context for CXCursor_OverloadCandidate is fixed remove this check
                    if (result.CursorKind != CXCursor_OverloadCandidate) {
                        break;
                    }
1025
#else
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1026
                    break;
1027
#endif
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1028
                }
1029

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1030
                const QString string = ClangString(clang_getCompletionChunkText(completionString, j)).toString();
1031

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1032
                switch (kind) {
1033
1034
                case CXCompletionChunk_TypedText:
                    typed = string;
1035
                    replacement += string;
1036
1037
1038
1039
1040
                    break;
                case CXCompletionChunk_ResultType:
                    resultType = string;
                    continue;
                case CXCompletionChunk_Placeholder:
1041
                    if (signatureState == Inside) {
1042
                        arguments += string;
1043
                    }
1044
                    continue;
1045
1046
1047
1048
1049
1050
1051
1052
1053
                case CXCompletionChunk_LeftParen:
                    if (signatureState == Before && !parenDepth) {
                        signatureState = Inside;
                    }
                    parenDepth++;
                    break;
                case CXCompletionChunk_RightParen:
                    --parenDepth;
                    if (signatureState == Inside && !parenDepth) {
1054
                        arguments += QLatin1Char(')');
1055
1056
1057
                        signatureState = After;
                    }
                    break;
1058
1059
1060
1061
1062
                case CXCompletionChunk_Text:
#if CINDEX_VERSION_MINOR >= 30
                    if (result.CursorKind == CXCursor_OverloadCandidate) {
                        typed += string;
                    }
1063
1064
1065
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1066
1067
#endif
                    break;
1068
1069
1070
1071
                case CXCompletionChunk_CurrentParameter:
                    argumentRange.start = arguments.size();
                    argumentRange.end = string.size();
                    break;
1072
1073
                default:
                    break;
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1074
1075
1076
1077
                }
                if (signatureState == Inside) {
                    arguments += string;
                }
1078
            }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1079
1080
1081
        };

        processChunks(result.CompletionString);
1082

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1083
1084
1085
1086
1087
1088
#if CINDEX_VERSION_MINOR >= 30
        // TODO: No closing paren if default parameters present
        if (result.CursorKind == CXCursor_OverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) {
            arguments += QLatin1Char(')');
        }
#endif
Kevin Funk's avatar
Kevin Funk committed
1089
1090
        // ellide text to the right for overly long result types (templates especially)
        elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH);
1091

1092
1093
1094
        static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                                QStringLiteral("kdevelop/pics/namespace.png")));

Kevin Funk's avatar
Kevin Funk committed
1095
        if (isDeclaration) {
1096
1097
1098
1099
1100
1101
1102
1103
            const Identifier id(typed);
            QualifiedIdentifier qid;
            ClangString parent(clang_getCompletionParent(result.CompletionString, nullptr));
            if (parent.c_str() != nullptr) {
                qid = QualifiedIdentifier(parent.toString());
            }
            qid.push(id);

1104
1105
1106
1107
            if (!isValidCompletionIdentifier(qid)) {
                continue;
            }

1108
            auto found = findDeclaration(qid, ctx, m_position, handled);
1109

1110
            CompletionTreeItemPointer item;
1111
            if (found) {
1112
1113
1114
1115
1116
1117
1118
                // TODO: Bug in Clang: protected members from base classes not accessible in derived classes.
                if (availability == CXAvailability_NotAccessible) {
                    if (auto cl = dynamic_cast<ClassMemberDeclaration*>(found)) {
                        if (cl->accessPolicy() != Declaration::Protected) {
                            continue;
                        }

1119
                        auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position);
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130

                        uint steps = 10;
                        auto inheriters = DUChainUtils::getInheriters(declarationClassContext, steps);
                        if(!inheriters.contains(currentClassContext)){
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

1131
                auto declarationItem = new DeclarationItem(found, typed, resultType, replacement);
1132

1133
1134
                const unsigned int completionPriority = adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString));
                const bool bestMatch = completionPriority <= CCP_SuperCompletion;
1135
1136

                //don't set best match property for internal identifiers, also prefer declarations from current file
1137
1138
                const auto isInternal = found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__"));
                if (bestMatch && !isInternal ) {
Kevin Funk's avatar
Kevin Funk committed
1139
                    const int matchQuality = codeCompletionPriorityToMatchQuality(completionPriority);
1140
                    declarationItem->setMatchQuality(matchQuality);
1141
1142
1143

                    // TODO: LibClang missing API to determine expected code completion type.
                    lookAheadMatcher.addMatchedType(found->indexedType());
1144
1145
                } else {
                    declarationItem->setInheritanceDepth(completionPriority);
1146

1147
1148
                    lookAheadMatcher.addDeclarations(found);
                }
1149
1150
1151
                if ( isInternal ) {
                    declarationItem->markAsUnimportant();
                }
1152
1153
1154
1155
1156
1157
#if CINDEX_VERSION_MINOR >= 30
                if (result.CursorKind == CXCursor_OverloadCandidate) {
                    declarationItem->setArgumentHintDepth(1);
                }
#endif

1158
                item = declarationItem;
1159
            } else {
1160
1161
1162
#if CINDEX_VERSION_MINOR >= 30
                if (result.CursorKind == CXCursor_OverloadCandidate) {
                    // TODO: No parent context for CXCursor_OverloadCandidate items, hence qid is broken -> no declaration found
1163
1164
1165
                    auto ahi = new ArgumentHintItem({}, resultType, typed, arguments, argumentRange);
                    ahi->setArgumentHintDepth(1);
                    item = ahi;
1166
1167
1168
1169
                } else {
#endif
                    // still, let's trust that Clang found something useful and put it into the completion result list
                    clangDebug() << "Could not find declaration for" << qid;
1170
                    auto instance = new SimpleItem(typed + arguments, resultType, replacement, noIcon);
1171
1172
                    instance->markAsUnimportant();
                    item = CompletionTreeItemPointer(instance);
1173
1174
1175
#if CINDEX_VERSION_MINOR >= 30
                }
#endif
1176
            }
1177
1178

            if (isValidSpecialCompletionIdentifier(qid)) {
1179
1180
1181
1182
1183
                // If it's a special completion identifier e.g. "operator=(const&)" and we don't have a declaration for it, don't add it into completion list, as this item is completely useless and pollutes the test case.
                // This happens e.g. for "class A{}; a.|".  At | we have "operator=(const A&)" as a special completion identifier without a declaration.
                if(item->declaration()){
                    specialItems.append(item);
                }
1184
1185
1186
1187
            } else {
                items.append(item);
            }
            continue;
1188
1189
        }

1190
1191
        if (result.CursorKind == CXCursor_MacroDefinition) {
            // TODO: grouping of macros and built-in stuff
1192
1193
1194
1195
1196
1197
            const auto text = QString(typed + arguments);
            auto instance = new SimpleItem(text, resultType, replacement, noIcon);
            auto item = CompletionTreeItemPointer(instance);
            if ( text.startsWith(QLatin1String("_")) ) {
                instance->markAsUnimportant();
            }
1198
            macros.append(item);
Kevin Funk's avatar
Kevin Funk committed
1199
        } else if (result.CursorKind == CXCursor_NotImplemented) {
1200
            auto instance = new SimpleItem(typed, resultType, replacement, noIcon);