context.cpp 48 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
        : CompletionItem<CompletionTreeItem>(
152
              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
219
220
221
            const auto functionType = m_declaration->type<FunctionType>();

            // protect against buggy code that created the m_declaration,
            // to mark it as a function but not assign a function type
            if (!functionType)
                return;

222
            auto doc = view->document();
223
224
225
226
227
228
229
230
231
232
233
234
235

            // 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;
                }
            }

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

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

265
266
267
268
269
270
271
    bool createsExpandingWidget() const override
    {
        return true;
    }

    QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override
    {
272
        return new ClangNavigationWidget(m_declaration, AbstractNavigationWidget::EmbeddableWidget);
273
    }
274

Kevin Funk's avatar
Kevin Funk committed
275
276
277
278
279
    int matchQuality() const
    {
        return m_matchQuality;
    }

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

286
287
288
289
290
    void setInheritanceDepth(int depth)
    {
        m_inheritanceDepth = depth;
    }

291
292
293
294
295
296
297
298
299
300
    int argumentHintDepth() const override
    {
        return m_depth;
    }

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

301
protected:
Kevin Funk's avatar
Kevin Funk committed
302
    int m_matchQuality = 0;
303
    int m_depth = 0;
304
    QString m_replacement;
305
};
306

307
308
309
310
311
312
313
314
315
316
317
318
319
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;
    }

320
    explicit ImplementsItem(const FuncImplementInfo& item)
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
        : 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);
    }
};
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

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
359
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    {
        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);
        }

375
        if (index.column() == CodeCompletionModel::Arguments) {
376
377
378
379
380
381
382
383
384
385
386
            return m_arguments;
        }

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

private:
    CurrentArgumentRange m_range;
    QString m_arguments;
};

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

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

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

416
417
private:
    QString m_replacement;
418
    QIcon m_icon;
419
};
420

421
422
423
/**
 * Return true in case position @p position represents a cursor inside a comment
 */
Kevin Funk's avatar
Kevin Funk committed
424
bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position)
425
426
427
428
429
430
431
{
    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
432
    auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1);
433
434
435
436
    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
437
438
    const ClangTokens tokens(unit, range);
    for (CXToken token : tokens) {
439
440
441
442
443
444
        CXTokenKind tokenKind = clang_getTokenKind(token);
        if (tokenKind != CXToken_Comment) {
            continue;
        }

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

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

Kevin Funk's avatar
Kevin Funk committed
460
/**
461
 * @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
462
463
464
465
466
467
 *
 * 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)
{
468
    return 10u - qBound(0u, completionPriority, 80u) / 8;
Kevin Funk's avatar
Kevin Funk committed
469
470
}

471
int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority)
472
{
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    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);
490
        }
491
492
    } else {
        completionPriority += modifier;
493
494
495
496
497
    }

    return completionPriority;
}

498
499
500
501
502
503
504
505
506
507
/// 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);
}

508
509
510
/**
 * @return Whether the declaration represented by identifier @p identifier qualifies as completion result
 *
Kevin Funk's avatar
Kevin Funk committed
511
512
 * 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();')
513
514
515
516
517
518
519
520
521
522
523
524
525
526
 */
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();
527
    if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) {
528
529
530
531
532
        return false; // is destructor
    }
    return true;
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
/**
 * @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();
549
    if (idString.startsWith(QLatin1String("operator="))) {
550
551
552
553
554
        return true; // is assignment operator
    }
    return false;
}

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

559
560
561
    const auto top = ctx->topContext();
    const auto& importedContexts = top->importedParentContexts();

562
    for (auto it = decl.iterator(); it; ++it) {
563
564
565
566
567
568
569
570
571
572
573
574
        // 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;
        }

575
        auto declaration = it->declaration();
576
577
578
579
580
581
582
        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;
        }

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

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

600
    return nullptr;
601
602
}

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

        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;
619
                auto decl = findDeclaration(qid, context, position, tmp);
620
621
622
623
624
625
626

                if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
                    parent = decl->internalContext();
                    break;
                }
            }
        }
627
628
629
630
631
632
        parent = parent->parentContext();
    }

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

633
634
635
class LookAheadItemMatcher
{
public:
636
    explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
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
669
670
671
        : 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;
672
673
        for (const auto& pair: possibleLookAheadDeclarations) {
            auto decl = pair.first;
674
            if (matchedTypes.contains(decl->indexedType())) {
675
676
677
678
                auto parent = pair.second;
                const QString access = parent->abstractType()->whichType() == AbstractType::TypePointer
                                 ? QStringLiteral("->") : QStringLiteral(".");
                const QString text = parent->identifier().toString() + access + decl->identifier().toString();
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
723
724
725
                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;
                            }
                        }
                    }

726
                    possibleLookAheadDeclarations.insert({localDecl, declaration});
727
728
729
730
731
732
733
734
735
736
737
738
                }
            }
        }
    }

    // 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
739
740
    // Second declaration represents context
    QSet<DeclarationContext> possibleLookAheadDeclarations;
741
742
743
744
745
746

    TopDUContextPointer m_topContext;

    bool m_enabled;
};

747
748
749
750
751
752
753
754
755
756
757
struct MemberAccessReplacer : public QObject
{
    Q_OBJECT

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

758
public Q_SLOTS:
759
760
761
762
763
764
765
766
767
768
769
770
    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;
771
                if (type == ArrowToDot) {
772
773
774
775
776
777
778
779
                    oldAccess = QStringLiteral("->");
                    newAccess = QStringLiteral(".");
                } else {
                    oldAccess = QStringLiteral(".");
                    newAccess = QStringLiteral("->");
                }

                auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor);
780
781
782
783
784
785
786
787

                // 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});
                }

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

797
798
}

799
800
Q_DECLARE_METATYPE(MemberAccessReplacer::Type)

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

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

Kevin Funk's avatar
Kevin Funk committed
823
824
825
    {
        const unsigned int completeOptions = clang_defaultCodeCompleteOptions();

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

832
        allUnsaved.reserve(otherUnsavedFiles.size() + 1);
833
834
835
836
        for ( const auto& f : otherUnsavedFiles ) {
            allUnsaved.append(f.toClangApi());
        }
        allUnsaved.append(unsaved);
837

838
839
        m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
                        position.line() + 1, position.column() + 1,
840
                        allUnsaved.data(), allUnsaved.size(),
841
                        completeOptions));
Kevin Funk's avatar
Kevin Funk committed
842
843

        if (!m_results) {
844
            qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file;
845
            return;
Kevin Funk's avatar
Kevin Funk committed
846
        }
847

848
849
850
851
852
853
854
855
856
        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;
857
858
                } else {
                    replacementType = MemberAccessReplacer::DotToArrow;
859
860
861
862
863
864
865
866
867
868
                }

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

                m_valid = false;
                return;
            }
        }

869
870
871
872
        auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros;
        if (!addMacros) {
            m_filters |= NoMacros;
        }
873
    }
874

875
876
877
878
879
    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 '->'
880
            m_text = trimmedText.leftRef(trimmedText.size() - 1) + QStringLiteral("->");
881
882
883
884
885

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

            m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
890
                                                 position.line() + 1, position.column() + 1 + 1,
891
                                                 allUnsaved.data(), allUnsaved.size(),
892
893
894
895
896
897
898
899
900
901
902
903
                                                 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;
        }
    }

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

911
    m_completionHelper.computeCompletions(session, clangFile, position);
912
913
914
915
916
}

ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
917

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

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

933
934
    const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position));

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

944
945
    // two sets of handled declarations to prevent duplicates and make sure we show
    // all available overloads
946
    QSet<Declaration*> handled;
947
948
    // this is only used for the CXCursor_OverloadCandidate completion items
    QSet<Declaration*> overloadsHandled;
949

950
951
    LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));

952
953
954
    // If ctx is/inside the Class context, this represents that context.
    const auto currentClassContext = classDeclarationForContext(ctx, m_position);

955
    clangDebug() << "Clang found" << m_results->NumResults << "completion results";
956

957
    for (uint i = 0; i < m_results->NumResults; ++i) {
958
959
960
961
        if (abort) {
            return {};
        }

962
        auto result = m_results->Results[i];
963
964
965
966
967
        #if CINDEX_VERSION_MINOR >= 30
        const bool isOverloadCandidate = result.CursorKind == CXCursor_OverloadCandidate;
        #else
        const bool isOverloadCandidate = false;
        #endif
968
969

        const auto availability = clang_getCompletionAvailability(result.CompletionString);
970
        if (availability == CXAvailability_NotAvailable) {
971
972
973
            continue;
        }

Kevin Funk's avatar
Kevin Funk committed
974
975
976
977
978
        const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition;
        if (isMacroDefinition && m_filters & NoMacros) {
            continue;
        }

979
980
981
982
983
984
985
986
987
988
        const bool isBuiltin = (result.CursorKind == CXCursor_NotImplemented);
        if (isBuiltin && m_filters & NoBuiltins) {
            continue;
        }

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

989
        if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) {
990
991
992
            continue;
        }

993
        // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items.
994
        QString typed;
Milian Wolff's avatar
Milian Wolff committed
995
        // the return type of a function e.g.
996
        QString resultType;
Milian Wolff's avatar
Milian Wolff committed
997
        // the replacement text when an item gets executed
998
        QString replacement;
999
1000
1001
1002

        QString arguments;

        ArgumentHintItem::CurrentArgumentRange argumentRange;
Milian Wolff's avatar
Milian Wolff committed
1003
1004
        //BEGIN function signature parsing
        // nesting depth of parentheses
1005
1006
        int parenDepth = 0;
        enum FunctionSignatureState {
Milian Wolff's avatar
Milian Wolff committed
1007
            // not yet inside the function signature
1008
            Before,
Milian Wolff's avatar
Milian Wolff committed
1009
            // any token is part of the function signature now
1010
            Inside,
Milian Wolff's avatar
Milian Wolff committed
1011
            // finished parsing the function signature
1012
1013
            After
        };
Milian Wolff's avatar
Milian Wolff committed
1014
        // current state
1015
        FunctionSignatureState signatureState = Before;
Milian Wolff's avatar
Milian Wolff committed
1016
        //END function signature parsing
Kevin Funk's avatar
Kevin Funk committed
1017

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
        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;
1028
                }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1029
1030
1031
1032

                // 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()) {
                    // TODO: When parent context for CXCursor_OverloadCandidate is fixed remove this check
1033
                    if (!isOverloadCandidate) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1034
1035
1036
                        break;
                    }
                }
1037

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

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1040
                switch (kind) {
1041
1042
                case CXCompletionChunk_TypedText:
                    typed = string;
1043
                    replacement += string;
1044
1045
1046
1047
1048
                    break;
                case CXCompletionChunk_ResultType:
                    resultType = string;
                    continue;
                case CXCompletionChunk_Placeholder:
1049
                    if (signatureState == Inside) {
1050
                        arguments += string;
1051
                    }
1052
                    continue;
1053
1054
1055
1056
1057
1058
1059
1060
1061
                case CXCompletionChunk_LeftParen:
                    if (signatureState == Before && !parenDepth) {
                        signatureState = Inside;
                    }
                    parenDepth++;
                    break;
                case CXCompletionChunk_RightParen:
                    --parenDepth;
                    if (signatureState == Inside && !parenDepth) {
1062
                        arguments += QLatin1Char(')');
1063
1064
1065
                        signatureState = After;
                    }
                    break;
1066
                case CXCompletionChunk_Text:
1067
                    if (isOverloadCandidate) {
1068
1069
                        typed += string;
                    }
1070
1071
1072
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1073
1074
1075
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1076
                    break;
1077
1078
1079
1080
                case CXCompletionChunk_CurrentParameter:
                    argumentRange.start = arguments.size();
                    argumentRange.end = string.size();
                    break;
1081
1082
                default:
                    break;
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1083
1084
1085
1086
                }
                if (signatureState == Inside) {
                    arguments += string;
                }
1087
            }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1088
1089
1090
        };

        processChunks(result.CompletionString);
1091

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1092
        // TODO: No closing paren if default parameters present
1093
        if (isOverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1094
1095
            arguments += QLatin1Char(')');
        }
Kevin Funk's avatar
Kevin Funk committed
1096
1097
        // ellide text to the right for overly long result types (templates especially)
        elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH);
1098

1099
1100
1101
        static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                                QStringLiteral("kdevelop/pics/namespace.png")));

Kevin Funk's avatar
Kevin Funk committed
1102
        if (isDeclaration) {
1103
1104
1105
1106
1107
1108
1109
1110
            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);

1111
1112
1113
1114
            if (!isValidCompletionIdentifier(qid)) {
                continue;
            }

1115
1116
1117
1118
1119
1120
            if (isOverloadCandidate && resultType.isEmpty() && parent.isEmpty()) {
                // workaround: find constructor calls for non-namespaced classes
                // TODO: return the namespaced class as parent in libclang
                qid.push(id);
            }

1121
            auto found = findDeclaration(qid, ctx, m_position, isOverloadCandidate ? overloadsHandled : handled);
1122

1123
            CompletionTreeItemPointer item;
1124
            if (found) {
1125
1126
1127
1128
1129
1130
1131
                // 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;
                        }

1132
                        auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position);
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143

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

1144
1145
1146
1147
1148
1149
1150
                DeclarationItem* declarationItem = nullptr;
                if (isOverloadCandidate) {
                    declarationItem = new ArgumentHintItem(found, resultType, typed, arguments, argumentRange);
                    declarationItem->setArgumentHintDepth(1);
                } else {
                    declarationItem = new DeclarationItem(found, typed, resultType, replacement);
                }
1151

1152
1153
                const unsigned int completionPriority = adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString));
                const bool bestMatch = completionPriority <= CCP_SuperCompletion;
1154
1155

                //don't set best match property for internal identifiers, also prefer declarations from current file
1156
1157
                const auto isInternal = found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__"));
                if (bestMatch && !isInternal ) {
Kevin Funk's avatar
Kevin Funk committed
1158
                    const int matchQuality = codeCompletionPriorityToMatchQuality(completionPriority);
1159
                    declarationItem->setMatchQuality(matchQuality);
1160
1161
1162

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

1166
1167
                    lookAheadMatcher.addDeclarations(found);
                }
1168
1169
1170
                if ( isInternal ) {
                    declarationItem->markAsUnimportant();
                }
1171

1172
                item = declarationItem;
1173
            } else {
1174
                if (isOverloadCandidate) {
1175
                    // TODO: No parent context for CXCursor_OverloadCandidate items, hence qid is broken -> no declaration found
1176
1177
1178
                    auto ahi = new ArgumentHintItem({}, resultType, typed, arguments, argumentRange);
                    ahi->setArgumentHintDepth(1);
                    item = ahi;
1179
1180
1181
                } else {
                    // still, let's trust that Clang found something useful and put it into the completion result list
                    clangDebug() << "Could not find declaration for" << qid;
1182
                    auto instance = new SimpleItem(typed + arguments, resultType, replacement, noIcon);
1183
1184
                    instance->markAsUnimportant();
                    item = CompletionTreeItemPointer(instance);