context.cpp 49 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/duchain/stringhelpers.h>
45
#include <language/codecompletion/codecompletionmodel.h>
46
#include <language/codecompletion/normaldeclarationcompletionitem.h>
47
#include <util/foregroundlock.h>
48
#include <qtcompat_p.h>
49

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

59
#include <algorithm>
Kevin Funk's avatar
Kevin Funk committed
60
#include <functional>
61
#include <memory>
Kevin Funk's avatar
Kevin Funk committed
62

63
#include <KTextEditor/Document>
64
#include <KTextEditor/View>
65
66
67
68

using namespace KDevelop;

namespace {
Kevin Funk's avatar
Kevin Funk committed
69
70
/// Maximum return-type string length in completion items
const int MAX_RETURN_TYPE_STRING_LENGTH = 20;
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/// 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
};

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

125
    ~CompletionItem() override = default;
126

127
128
129
130
131
132
133
134
    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;
            }
135
        }
136
        return {};
137
    }
138

139
140
141
142
143
    void markAsUnimportant()
    {
        m_unimportant = true;
    }

144
protected:
145
146
    QString m_display;
    QString m_prefix;
147
    bool m_unimportant;
148
149
};

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

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

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

private:
    QString m_returnType;
180
181
};

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

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

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

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

        if(!m_declaration){
            return;
        }

        if(m_declaration->isFunctionDeclaration()) {
218
219
220
221
222
223
224
            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;

225
            auto doc = view->document();
226
227
228
229
230
231
232
233
234
235
236
237
238

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

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

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

268
269
270
271
272
273
274
    bool createsExpandingWidget() const override
    {
        return true;
    }

    QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override
    {
275
        return new ClangNavigationWidget(m_declaration, AbstractNavigationWidget::EmbeddableWidget);
276
    }
277

Kevin Funk's avatar
Kevin Funk committed
278
279
280
281
282
    int matchQuality() const
    {
        return m_matchQuality;
    }

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

289
290
291
292
293
    void setInheritanceDepth(int depth)
    {
        m_inheritanceDepth = depth;
    }

294
295
296
297
298
299
300
301
302
303
    int argumentHintDepth() const override
    {
        return m_depth;
    }

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

304
protected:
Kevin Funk's avatar
Kevin Funk committed
305
    int m_matchQuality = 0;
306
    int m_depth = 0;
307
    QString m_replacement;
308
};
309

310
311
312
313
314
315
316
317
318
319
320
321
322
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;
    }

323
    explicit ImplementsItem(const FuncImplementInfo& item)
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
    {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
        auto* const document = view->document();

        // try and replace leading typed text that match the proposed implementation
        const QString leading = document->line(word.end().line()).left(word.end().column());
        const QString leadingNoSpace = removeWhitespace(leading);
        if (!leadingNoSpace.isEmpty() && (removeWhitespace(m_display).startsWith(leadingNoSpace)
            || removeWhitespace(m_replacement).startsWith(leadingNoSpace))) {
            const int removeSize = leading.end() - std::find_if_not(leading.begin(), leading.end(),
                                        [](QChar c){ return c.isSpace(); });
            const KTextEditor::Cursor newStart = {word.end().line(), word.end().column() - removeSize};
            document->replaceText({newStart, word.end()}, m_replacement);
        } else {
            document->replaceText(word, m_replacement);
        }
357
358
    }
};
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

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
375
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
376
377
378
379
    {
        if (role == CodeCompletionModel::CustomHighlight && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
            QTextCharFormat boldFormat;
            boldFormat.setFontWeight(QFont::Bold);
380
381
382
383
384
            const QList<QVariant> highlighting {
                QVariant(m_range.start),
                QVariant(m_range.end),
                boldFormat,
            };
385
386
387
388
389
390
391
            return highlighting;
        }

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

392
        if (index.column() == CodeCompletionModel::Arguments) {
393
394
395
396
397
398
399
400
401
402
403
            return m_arguments;
        }

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

private:
    CurrentArgumentRange m_range;
    QString m_arguments;
};

404
405
406
/**
 * A minimalistic completion item for macros and such
 */
407
408
409
class SimpleItem : public CompletionItem<CompletionTreeItem>
{
public:
410
    SimpleItem(const QString& display, const QString& prefix, const QString& replacement, const QIcon& icon = QIcon())
411
412
        : CompletionItem<CompletionTreeItem>(display, prefix)
        , m_replacement(replacement)
413
        , m_icon(icon)
414
415
416
417
418
419
420
421
    {
    }

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

422
423
424
425
426
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
        if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) {
            return m_icon;
        }
427
        if (role == CodeCompletionModel::UnimportantItemRole) {
428
429
            return m_unimportant;
        }
430
431
432
        return CompletionItem<CompletionTreeItem>::data(index, role, model);
    }

433
434
private:
    QString m_replacement;
435
    QIcon m_icon;
436
};
437

438
439
440
/**
 * Return true in case position @p position represents a cursor inside a comment
 */
Kevin Funk's avatar
Kevin Funk committed
441
bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position)
442
443
444
445
446
447
448
{
    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
449
    auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1);
450
451
452
453
    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
454
455
    const ClangTokens tokens(unit, range);
    for (CXToken token : tokens) {
456
457
458
459
460
461
        CXTokenKind tokenKind = clang_getTokenKind(token);
        if (tokenKind != CXToken_Comment) {
            continue;
        }

        auto range = ClangRange(clang_getTokenExtent(unit, token));
Kevin Funk's avatar
Kevin Funk committed
462
        if (range.toRange().contains(position)) {
463
464
465
466
467
468
            return true;
        }
    }
    return false;
}

Kevin Funk's avatar
Kevin Funk committed
469
470
471
QString& elideStringRight(QString& str, int length)
{
    if (str.size() > length + 3) {
472
        return str.replace(length, str.size() - length, QStringLiteral("..."));
Kevin Funk's avatar
Kevin Funk committed
473
474
475
476
    }
    return str;
}

Kevin Funk's avatar
Kevin Funk committed
477
/**
478
 * @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
479
480
481
482
483
484
 *
 * 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)
{
485
    return 10u - qBound(0u, completionPriority, 80u) / 8;
Kevin Funk's avatar
Kevin Funk committed
486
487
}

488
int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority)
489
{
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
    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);
507
        }
508
509
    } else {
        completionPriority += modifier;
510
511
512
513
514
    }

    return completionPriority;
}

515
516
517
518
519
520
521
522
523
524
/// 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);
}

525
526
527
/**
 * @return Whether the declaration represented by identifier @p identifier qualifies as completion result
 *
Kevin Funk's avatar
Kevin Funk committed
528
529
 * 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();')
530
531
532
533
534
535
536
537
538
539
540
541
542
543
 */
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();
544
    if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) {
545
546
547
548
549
        return false; // is destructor
    }
    return true;
}

550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
/**
 * @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();
566
    if (idString.startsWith(QLatin1String("operator="))) {
567
568
569
570
571
        return true; // is assignment operator
    }
    return false;
}

572
Declaration* findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, QSet<Declaration*>& handled)
573
{
574
    PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().declarations(qid);
575

576
577
578
    const auto top = ctx->topContext();
    const auto& importedContexts = top->importedParentContexts();

579
    for (auto it = decl.iterator(); it; ++it) {
580
581
582
583
584
585
586
587
588
589
590
591
        // 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;
        }

592
        auto declaration = it->declaration();
593
594
595
596
597
598
599
        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;
        }

600
        if (declaration->kind() == Declaration::Instance && !declaration->isFunctionDeclaration()) {
601
602
            break;
        }
603
604
        if (!handled.contains(declaration)) {
            handled.insert(declaration);
605
            return declaration;
606
607
608
609
610
611
612
        }
    }

    const auto foundDeclarations = ctx->findDeclarations(qid, position);
    for (auto dec : foundDeclarations) {
        if (!handled.contains(dec)) {
            handled.insert(dec);
613
            return dec;
614
615
616
        }
    }

617
    return nullptr;
618
619
}

620
/// If any parent of this context is a class, the closest class declaration is returned, nullptr otherwise
621
Declaration* classDeclarationForContext(const DUContextPointer& context, const CursorInRevision& position)
622
623
624
625
626
627
{
    auto parent = context;
    while (parent) {
        if (parent->type() == DUContext::Class) {
            break;
        }
628
629
630
631
632
633
634
635

        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;
636
                auto decl = findDeclaration(qid, context, position, tmp);
637
638
639
640
641
642
643

                if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
                    parent = decl->internalContext();
                    break;
                }
            }
        }
644
645
646
647
648
649
        parent = parent->parentContext();
    }

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

650
651
652
class LookAheadItemMatcher
{
public:
653
    explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
        : 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;
689
        for (const auto& pair: qAsConst(possibleLookAheadDeclarations)) {
690
            auto decl = pair.first;
691
            if (matchedTypes.contains(decl->indexedType())) {
692
693
694
695
                auto parent = pair.second;
                const QString access = parent->abstractType()->whichType() == AbstractType::TypePointer
                                 ? QStringLiteral("->") : QStringLiteral(".");
                const QString text = parent->identifier().toString() + access + decl->identifier().toString();
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
                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;
                }

719
720
                const auto& localDeclarations = typeDecl->internalContext()->localDeclarations();
                for (auto localDecl : localDeclarations) {
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
                    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;
                            }
                        }
                    }

744
                    possibleLookAheadDeclarations.insert({localDecl, declaration});
745
746
747
748
749
750
751
752
753
754
755
756
                }
            }
        }
    }

    // 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
757
758
    // Second declaration represents context
    QSet<DeclarationContext> possibleLookAheadDeclarations;
759
760
761
762
763
764

    TopDUContextPointer m_topContext;

    bool m_enabled;
};

765
766
767
768
769
770
771
772
773
774
775
struct MemberAccessReplacer : public QObject
{
    Q_OBJECT

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

776
public Q_SLOTS:
777
778
779
780
781
782
783
784
785
786
787
788
    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;
789
                if (type == ArrowToDot) {
790
791
792
793
794
795
796
797
                    oldAccess = QStringLiteral("->");
                    newAccess = QStringLiteral(".");
                } else {
                    oldAccess = QStringLiteral(".");
                    newAccess = QStringLiteral("->");
                }

                auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor);
798
799
800
801
802
803
804
805

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

806
807
808
809
810
811
812
813
814
                if (oldRange.start().column() >= 0 && textDocument->text(oldRange) == oldAccess) {
                    textDocument->replaceText(oldRange, newAccess);
                }
            }
        }
    }
};
static MemberAccessReplacer s_memberAccessReplacer;

815
816
}

817
818
Q_DECLARE_METATYPE(MemberAccessReplacer::Type)

819
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
Kevin Funk's avatar
Kevin Funk committed
820
                                                       const ParseSessionData::Ptr& sessionData,
821
                                                       const QUrl& url,
Kevin Funk's avatar
Kevin Funk committed
822
                                                       const KTextEditor::Cursor& position,
823
824
                                                       const QString& text,
                                                       const QString& followingText
825
                                                      )
826
    : CodeCompletionContext(context, text + followingText, CursorInRevision::castFromSimpleCursor(position), 0)
827
    , m_results(nullptr, clang_disposeCodeCompleteResults)
Kevin Funk's avatar
Kevin Funk committed
828
    , m_parseSessionData(sessionData)
829
{
830
    qRegisterMetaType<MemberAccessReplacer::Type>();
831
832
    const QByteArray file = url.toLocalFile().toUtf8();
    ParseSession session(m_parseSessionData);
833
834
835
836
837
838
839
840

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

Kevin Funk's avatar
Kevin Funk committed
841
842
843
    {
        const unsigned int completeOptions = clang_defaultCodeCompleteOptions();

844
845
846
847
        CXUnsavedFile unsaved;
        unsaved.Filename = file.constData();
        const QByteArray content = m_text.toUtf8();
        unsaved.Contents = content.constData();
848
849
        unsaved.Length = content.size();

850
        allUnsaved.reserve(otherUnsavedFiles.size() + 1);
851
        for (const auto& f : qAsConst(otherUnsavedFiles)) {
852
853
854
            allUnsaved.append(f.toClangApi());
        }
        allUnsaved.append(unsaved);
855

856
857
        m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
                        position.line() + 1, position.column() + 1,
858
                        allUnsaved.data(), allUnsaved.size(),
859
                        completeOptions));
Kevin Funk's avatar
Kevin Funk committed
860
861

        if (!m_results) {
862
            qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file;
863
            return;
Kevin Funk's avatar
Kevin Funk committed
864
        }
865

866
867
868
869
870
871
872
873
874
        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;
875
876
                } else {
                    replacementType = MemberAccessReplacer::DotToArrow;
877
878
879
880
881
882
883
884
885
886
                }

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

                m_valid = false;
                return;
            }
        }

887
888
889
890
        auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros;
        if (!addMacros) {
            m_filters |= NoMacros;
        }
891
    }
892

893
894
895
896
897
    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 '->'
898
            m_text = trimmedText.leftRef(trimmedText.size() - 1) + QStringLiteral("->");
899
900
901
902
903

            CXUnsavedFile unsaved;
            unsaved.Filename = file.constData();
            const QByteArray content = m_text.toUtf8();
            unsaved.Contents = content.constData();
904
905
            unsaved.Length = content.size();
            allUnsaved[allUnsaved.size() - 1] = unsaved;
906
907

            m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
908
                                                 position.line() + 1, position.column() + 1 + 1,
909
                                                 allUnsaved.data(), allUnsaved.size(),
910
911
912
913
914
915
916
917
918
919
920
921
                                                 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;
        }
    }

922
    // check 'isValidPosition' after parsing the new content
923
924
    auto clangFile = session.file(file);
    if (!isValidPosition(session.unit(), clangFile)) {
925
926
927
        m_valid = false;
        return;
    }
928

929
    m_completionHelper.computeCompletions(session, clangFile, position);
930
931
932
933
934
}

ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
935

936
bool ClangCodeCompletionContext::isValidPosition(CXTranslationUnit unit, CXFile file) const
937
{
938
    if (isInsideComment(unit, file, m_position.castToSimpleCursor())) {
939
        clangDebug() << "Invalid completion context: Inside comment";
940
941
942
        return false;
    }
    return true;
943
944
}

945
QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/)
946
{
947
948
949
950
    if (!m_valid || !m_duContext || !m_results) {
        return {};
    }

951
952
    const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position));

Kevin Funk's avatar
Kevin Funk committed
953
    /// Normal completion items, such as 'void Foo::foo()'
954
    QList<CompletionTreeItemPointer> items;
Kevin Funk's avatar
Kevin Funk committed
955
956
957
    /// Stuff like 'Foo& Foo::operator=(const Foo&)', etc. Not regularly used by our users.
    QList<CompletionTreeItemPointer> specialItems;
    /// Macros from the current context
958
    QList<CompletionTreeItemPointer> macros;
Kevin Funk's avatar
Kevin Funk committed
959
    /// Builtins reported by Clang
960
    QList<CompletionTreeItemPointer> builtin;
961

962
963
    // two sets of handled declarations to prevent duplicates and make sure we show
    // all available overloads
964
    QSet<Declaration*> handled;
965
966
    // this is only used for the CXCursor_OverloadCandidate completion items
    QSet<Declaration*> overloadsHandled;
967

968
969
    LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));

970
971
972
    // If ctx is/inside the Class context, this represents that context.
    const auto currentClassContext = classDeclarationForContext(ctx, m_position);

973
    clangDebug() << "Clang found" << m_results->NumResults << "completion results";
974

975
    for (uint i = 0; i < m_results->NumResults; ++i) {
976
977
978
979
        if (abort) {
            return {};
        }

980
        auto result = m_results->Results[i];
981
982
983
984
985
        #if CINDEX_VERSION_MINOR >= 30
        const bool isOverloadCandidate = result.CursorKind == CXCursor_OverloadCandidate;
        #else
        const bool isOverloadCandidate = false;
        #endif
986
987

        const auto availability = clang_getCompletionAvailability(result.CompletionString);
988
        if (availability == CXAvailability_NotAvailable) {
989
990
991
            continue;
        }

Kevin Funk's avatar
Kevin Funk committed
992
993
994
995
996
        const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition;
        if (isMacroDefinition && m_filters & NoMacros) {
            continue;
        }

997
998
999
1000
1001
1002
1003
1004
1005
1006
        const bool isBuiltin = (result.CursorKind == CXCursor_NotImplemented);
        if (isBuiltin && m_filters & NoBuiltins) {
            continue;
        }

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

1007
        if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) {
1008
1009
1010
            continue;
        }

1011
        // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items.
1012
        QString typed;
Milian Wolff's avatar
Milian Wolff committed
1013
        // the return type of a function e.g.
1014
        QString resultType;
Milian Wolff's avatar
Milian Wolff committed
1015
        // the replacement text when an item gets executed
1016
        QString replacement;
1017
1018
1019
1020

        QString arguments;

        ArgumentHintItem::CurrentArgumentRange argumentRange;
Milian Wolff's avatar
Milian Wolff committed
1021
1022
        //BEGIN function signature parsing
        // nesting depth of parentheses
1023
1024
        int parenDepth = 0;
        enum FunctionSignatureState {
Milian Wolff's avatar
Milian Wolff committed
1025
            // not yet inside the function signature
1026
            Before,
Milian Wolff's avatar
Milian Wolff committed
1027
            // any token is part of the function signature now
1028
            Inside,
Milian Wolff's avatar
Milian Wolff committed
1029
            // finished parsing the function signature
1030
1031
            After
        };
Milian Wolff's avatar
Milian Wolff committed
1032
        // current state
1033
        FunctionSignatureState signatureState = Before;
Milian Wolff's avatar
Milian Wolff committed
1034
        //END function signature parsing
Kevin Funk's avatar
Kevin Funk committed
1035

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
        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;
1046
                }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1047
1048
1049
1050

                // 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
1051
                    if (!isOverloadCandidate) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1052
1053
1054
                        break;
                    }
                }
1055

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

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1058
                switch (kind) {
1059
1060
                case CXCompletionChunk_TypedText:
                    typed = string;
1061
                    replacement += string;
1062
1063
1064
1065
1066
                    break;
                case CXCompletionChunk_ResultType:
                    resultType = string;
                    continue;
                case CXCompletionChunk_Placeholder:
1067
                    if (signatureState == Inside) {
1068
                        arguments += string;
1069
                    }
1070
                    continue;
1071
1072
1073
1074
1075
1076
1077
1078
1079
                case CXCompletionChunk_LeftParen:
                    if (signatureState == Before && !parenDepth) {
                        signatureState = Inside;
                    }
                    parenDepth++;
                    break;
                case CXCompletionChunk_RightParen:
                    --parenDepth;
                    if (signatureState == Inside && !parenDepth) {
1080
                        arguments += QLatin1Char(')');
1081
1082
1083
                        signatureState = After;
                    }
                    break;
1084
                case CXCompletionChunk_Text:
1085
                    if (isOverloadCandidate) {
1086
1087
                        typed += string;
                    }
1088
1089
1090
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1091
1092
1093
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1094
                    break;
1095
1096
1097
1098
                case CXCompletionChunk_CurrentParameter:
                    argumentRange.start = arguments.size();
                    argumentRange.end = string.size();
                    break;
1099
1100
                default:
                    break;
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1101
1102
1103
1104
                }
                if (signatureState == Inside) {
                    arguments += string;
                }
1105
            }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1106
1107
1108
        };

        processChunks(result.CompletionString);
1109

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1110
        // TODO: No closing paren if default parameters present
1111
        if (isOverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1112
1113
            arguments += QLatin1Char(')');
        }
Kevin Funk's avatar
Kevin Funk committed
1114
1115
        // ellide text to the right for overly long result types (templates especially)
        elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH);
1116

1117
1118
1119
        static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                                QStringLiteral("kdevelop/pics/namespace.png")));

Kevin Funk's avatar
Kevin Funk committed
1120
        if (isDeclaration) {
1121
1122
1123
1124
1125
1126
1127
1128
            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);

1129
1130
1131
1132
            if (!isValidCompletionIdentifier(qid)) {
                continue;
            }

1133
1134
1135
1136
1137
1138
            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);
            }

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

1141
            CompletionTreeItemPointer item;
1142
            if (found) {
1143
1144
1145
1146
1147
1148
1149
                // 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;
                        }

1150
                        auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position);
1151
1152

                        uint steps = 10;
1153
                        auto inheriters = DUChainUtils::inheriters(declarationClassContext, steps);
1154
1155
1156
1157
1158
1159
1160
1161
                        if(!inheriters.contains(currentClassContext)){
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

1162
1163
1164
1165
1166
1167
1168
                DeclarationItem* declarationItem = nullptr;
                if (isOverloadCandidate) {
                    declarationItem = new ArgumentHintItem(found, resultType, typed, arguments, argumentRange);
                    declarationItem->setArgumentHintDepth(1);
                } else {
                    declarationItem = new DeclarationItem(found, typed, resultType, replacement);
                }
1169

1170
1171
                const unsigned int completionPriority = adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString));
                const bool bestMatch = completionPriority <= CCP_SuperCompletion;
1172
1173

                //don't set best match property for internal identifiers, also prefer declarations from current file
1174
1175
                const auto isInternal = found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__"));
                if (bestMatch && !isInternal ) {
Kevin Funk's avatar
Kevin Funk committed
1176
                    const int matchQuality = codeCompletionPriorityToMatchQuality(completionPriority);
1177
                    declarationItem->setMatchQuality(matchQuality);
1178
1179
1180

                    // TODO: LibClang missing API to determine expected code completion type.
                    lookAheadMatcher.addMatchedType(found->indexedType());