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

#include "context.h"

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

28
29
#include <interfaces/icore.h>
#include <interfaces/idocumentcontroller.h>
30
31
#include <interfaces/iprojectcontroller.h>
#include <interfaces/iproject.h>
32

33
34
35
#include <language/duchain/duchainlock.h>
#include <language/duchain/ducontext.h>
#include <language/duchain/topducontext.h>
36
#include <language/duchain/declaration.h>
37
#include <language/duchain/classmemberdeclaration.h>
38
#include <language/duchain/classdeclaration.h>
39
#include <language/duchain/duchainutils.h>
40
#include <language/duchain/persistentsymboltable.h>
41
#include <language/duchain/types/integraltype.h>
42
#include <language/duchain/types/functiontype.h>
43
#include <language/duchain/types/pointertype.h>
44
#include <language/duchain/types/typealiastype.h>
45
#include <language/duchain/types/typeutils.h>
46
#include <language/duchain/stringhelpers.h>
47
#include <language/codecompletion/codecompletionmodel.h>
48
#include <language/codecompletion/normaldeclarationcompletionitem.h>
49
#include <language/codegen/documentchangeset.h>
50
#include <util/foregroundlock.h>
51
52
#include <custom-definesandincludes/idefinesandincludesmanager.h>
#include <project/projectmodel.h>
53

54
#include "../util/clangdebug.h"
55
#include "../util/clangtypes.h"
56
#include "../util/clangutils.h"
57
#include "../duchain/clangdiagnosticevaluator.h"
58
#include "../duchain/parsesession.h"
59
#include "../duchain/duchainutils.h"
60
#include "../duchain/navigationwidget.h"
61
#include "../clangsettings/clangsettingsmanager.h"
62

63
#include <algorithm>
Kevin Funk's avatar
Kevin Funk committed
64
#include <functional>
65
#include <memory>
Kevin Funk's avatar
Kevin Funk committed
66

67
#include <KTextEditor/Document>
68
#include <KTextEditor/View>
69
70
71
72

using namespace KDevelop;

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

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

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

129
    ~CompletionItem() override = default;
130

131
132
133
134
135
136
137
138
    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;
            }
139
        }
140
        return {};
141
    }
142

143
144
145
146
147
    void markAsUnimportant()
    {
        m_unimportant = true;
    }

148
protected:
149
150
    QString m_display;
    QString m_prefix;
151
    bool m_unimportant;
152
153
};

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

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

    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
    {
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
        QString replacement = m_returnType + QLatin1Char(' ') + m_display.replace(QRegularExpression(QStringLiteral("\\s*=\\s*0")), QString());

        bool appendSpecifer = true;
        if (const auto* project =
            KDevelop::ICore::self()->projectController()->findProjectForUrl(view->document()->url())) {
            const auto arguments = KDevelop::IDefinesAndIncludesManager::manager()->parserArguments(
                project->filesForPath(IndexedString(view->document()->url().path())).first());
            const auto match = QRegularExpression(QStringLiteral(R"(-std=c\+\+(\w+))")).match(arguments);

            appendSpecifer = match.hasMatch(); // assume non-modern if no standard is specified
            if (appendSpecifer) {
                const auto standard = match.captured(1);
                appendSpecifer = (standard != QLatin1String("98") && standard != QLatin1String("03"));
            }
        }

        if (appendSpecifer) {
            replacement.append(QLatin1String(" override;"));
        } else {
            replacement.append(QLatin1Char(';'));
        }

201
202
203
204
205
206
207
208
        DocumentChange overrideChange(IndexedString(view->document()->url()),
                                            word,
                                            QString{},
                                            replacement);
        overrideChange.m_ignoreOldText = true;
        DocumentChangeSet changes;
        changes.addChange(overrideChange);
        changes.applyAllChanges();
209
210
211
212
    }

private:
    QString m_returnType;
213
214
};

215
216
217
218
/**
 * Specialized completion item class for items which are represented by a Declaration
 */
class DeclarationItem : public CompletionItem<NormalDeclarationCompletionItem>
219
{
220
221
public:
    DeclarationItem(Declaration* dec, const QString& display, const QString& prefix, const QString& replacement)
222
223
        : CompletionItem<NormalDeclarationCompletionItem>(display, prefix)
        , m_replacement(replacement)
224
225
    {
        m_declaration = dec;
226
    }
227
228
229

    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
Kevin Funk's avatar
Kevin Funk committed
230
231
        if (role == CodeCompletionModel::MatchQuality && m_matchQuality) {
            return m_matchQuality;
232
233
        }

234
235
236
237
238
        auto ret = CompletionItem<NormalDeclarationCompletionItem>::data(index, role, model);
        if (ret.isValid()) {
            return ret;
        }
        return NormalDeclarationCompletionItem::data(index, role, model);
239
    }
240

Kevin Funk's avatar
Kevin Funk committed
241
    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
242
243
244
245
246
247
248
249
250
    {
        QString repl = m_replacement;
        DUChainReadLocker lock;

        if(!m_declaration){
            return;
        }

        if(m_declaration->isFunctionDeclaration()) {
251
252
253
254
255
256
257
            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;

258
            auto doc = view->document();
259
260
261
262
263
264
265
266
267
268
269
270
271

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

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

274
            bool didAddParentheses = false;
275
            if ( !funcptr && doc->characterAt(word.end()) != QLatin1Char('(') ) {
276
                repl += QLatin1String("()");
277
                didAddParentheses = true;
278
            }
Kevin Funk's avatar
Kevin Funk committed
279
            view->document()->replaceText(word, repl);
280
            if (functionType->indexedArgumentsSize() && didAddParentheses) {
Kevin Funk's avatar
Kevin Funk committed
281
                view->setCursorPosition(word.start() + KTextEditor::Cursor(0, repl.size() - 1));
282
            }
283
            auto returnTypeIntegral = functionType->returnType().cast<IntegralType>();
284
285
            if ( restEmpty && !funcptr && returnTypeIntegral && returnTypeIntegral->dataType() == IntegralType::TypeVoid ) {
                // function returns void and rest of line is empty -- nothing can be done with the result
286
                if (functionType->indexedArgumentsSize() ) {
287
288
289
290
291
292
293
294
295
                    // 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});
                }
            }
296
        } else {
Kevin Funk's avatar
Kevin Funk committed
297
            view->document()->replaceText(word, repl);
298
299
300
        }
    }

301
302
303
304
305
306
307
    bool createsExpandingWidget() const override
    {
        return true;
    }

    QWidget* createExpandingWidget(const CodeCompletionModel* /*model*/) const override
    {
308
        return new ClangNavigationWidget(m_declaration, AbstractNavigationWidget::EmbeddableWidget);
309
    }
310

Kevin Funk's avatar
Kevin Funk committed
311
312
313
314
315
    int matchQuality() const
    {
        return m_matchQuality;
    }

316
    ///Sets match quality from 0 to 10. 10 is the best fit.
Kevin Funk's avatar
Kevin Funk committed
317
    void setMatchQuality(int value)
318
    {
Kevin Funk's avatar
Kevin Funk committed
319
        m_matchQuality = value;
320
321
    }

322
323
324
325
326
    void setInheritanceDepth(int depth)
    {
        m_inheritanceDepth = depth;
    }

327
328
329
330
331
332
333
334
335
336
    int argumentHintDepth() const override
    {
        return m_depth;
    }

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

337
protected:
Kevin Funk's avatar
Kevin Funk committed
338
    int m_matchQuality = 0;
339
    int m_depth = 0;
340
    QString m_replacement;
341
};
342

343
344
345
346
347
348
349
350
351
352
353
354
355
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;
    }

356
    explicit ImplementsItem(const FuncImplementInfo& item)
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
        : 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
    {
376
377
        auto* const document = view->document();

378
379
380
        DocumentChangeSet changes;
        KTextEditor::Cursor rangeStart = word.start();

381
382
383
384
385
386
387
        // 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(); });
388
            rangeStart = {word.end().line(), word.end().column() - removeSize};
389
        }
390

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
        DocumentChange change(IndexedString(view->document()->url()),
                              KTextEditor::Range(rangeStart, word.end()),
                              QString(),
                              m_replacement);
        change.m_ignoreOldText = true;
        changes.addChange(change);
        changes.applyAllChanges();

        // Place cursor after the opening brace
        // arbitrarily chose 4, as it would accomodate the template and return types on their own line
        const auto searchRange = KTextEditor::Range(rangeStart, rangeStart.line() + 4, 0);
        const auto results = view->document()->searchText(searchRange, QStringLiteral("{"));
        if (!results.isEmpty()) {
            view->setCursorPosition(results.first().end());
        }
406
407
    }
};
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

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
424
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
425
426
427
428
    {
        if (role == CodeCompletionModel::CustomHighlight && index.column() == CodeCompletionModel::Arguments && argumentHintDepth()) {
            QTextCharFormat boldFormat;
            boldFormat.setFontWeight(QFont::Bold);
429
430
431
432
433
            const QList<QVariant> highlighting {
                QVariant(m_range.start),
                QVariant(m_range.end),
                boldFormat,
            };
434
435
436
437
438
439
440
            return highlighting;
        }

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

441
        if (index.column() == CodeCompletionModel::Arguments) {
442
443
444
445
446
447
448
449
450
451
452
            return m_arguments;
        }

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

private:
    CurrentArgumentRange m_range;
    QString m_arguments;
};

453
454
455
/**
 * A minimalistic completion item for macros and such
 */
456
457
458
class SimpleItem : public CompletionItem<CompletionTreeItem>
{
public:
459
    SimpleItem(const QString& display, const QString& prefix, const QString& replacement, const QIcon& icon = QIcon())
460
461
        : CompletionItem<CompletionTreeItem>(display, prefix)
        , m_replacement(replacement)
462
        , m_icon(icon)
463
464
465
466
467
468
469
470
    {
    }

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

471
472
473
474
475
    QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const override
    {
        if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Icon) {
            return m_icon;
        }
476
        if (role == CodeCompletionModel::UnimportantItemRole) {
477
478
            return m_unimportant;
        }
479
480
481
        return CompletionItem<CompletionTreeItem>::data(index, role, model);
    }

482
483
private:
    QString m_replacement;
484
    QIcon m_icon;
485
};
486

487
488
489
/**
 * Return true in case position @p position represents a cursor inside a comment
 */
Kevin Funk's avatar
Kevin Funk committed
490
bool isInsideComment(CXTranslationUnit unit, CXFile file, const KTextEditor::Cursor& position)
491
492
493
494
495
496
497
{
    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
498
    auto end = clang_getLocation(unit, file, position.line() + 1, position.column() + 1);
499
500
501
502
    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
503
504
    const ClangTokens tokens(unit, range);
    for (CXToken token : tokens) {
505
506
507
508
509
510
        CXTokenKind tokenKind = clang_getTokenKind(token);
        if (tokenKind != CXToken_Comment) {
            continue;
        }

        auto range = ClangRange(clang_getTokenExtent(unit, token));
Kevin Funk's avatar
Kevin Funk committed
511
        if (range.toRange().contains(position)) {
512
513
514
515
516
517
            return true;
        }
    }
    return false;
}

Kevin Funk's avatar
Kevin Funk committed
518
519
520
QString& elideStringRight(QString& str, int length)
{
    if (str.size() > length + 3) {
521
        return str.replace(length, str.size() - length, QStringLiteral("..."));
Kevin Funk's avatar
Kevin Funk committed
522
523
524
525
    }
    return str;
}

Kevin Funk's avatar
Kevin Funk committed
526
/**
527
 * @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
528
529
530
531
532
533
 *
 * 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)
{
534
    return 10u - qBound(0u, completionPriority, 80u) / 8;
Kevin Funk's avatar
Kevin Funk committed
535
536
}

537
int adjustPriorityForType(const AbstractType::Ptr& type, int completionPriority)
538
{
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    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);
556
        }
557
558
    } else {
        completionPriority += modifier;
559
560
561
562
563
    }

    return completionPriority;
}

564
565
566
567
568
569
570
571
572
573
/// 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);
}

574
575
576
/**
 * @return Whether the declaration represented by identifier @p identifier qualifies as completion result
 *
Kevin Funk's avatar
Kevin Funk committed
577
578
 * 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();')
579
580
581
582
583
584
585
586
587
588
589
590
591
592
 */
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();
593
    if (idString.startsWith(QLatin1Char('~')) && scope.toString() == idString.midRef(1)) {
594
595
596
597
598
        return false; // is destructor
    }
    return true;
}

599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
/**
 * @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();
615
    if (idString.startsWith(QLatin1String("operator="))) {
616
617
618
619
620
        return true; // is assignment operator
    }
    return false;
}

621
Declaration* findDeclaration(const QualifiedIdentifier& qid, const DUContextPointer& ctx, const CursorInRevision& position, QSet<Declaration*>& handled)
622
{
623
    PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().declarations(qid);
624

625
626
627
    const auto top = ctx->topContext();
    const auto& importedContexts = top->importedParentContexts();

628
    for (auto it = decl.iterator(); it; ++it) {
629
630
631
632
633
634
635
636
637
638
639
640
        // 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;
        }

641
        auto declaration = it->declaration();
642
643
644
645
646
647
648
        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;
        }

649
        if (declaration->kind() == Declaration::Instance && !declaration->isFunctionDeclaration()) {
650
651
            break;
        }
652
653
        if (!handled.contains(declaration)) {
            handled.insert(declaration);
654
            return declaration;
655
656
657
658
659
660
661
        }
    }

    const auto foundDeclarations = ctx->findDeclarations(qid, position);
    for (auto dec : foundDeclarations) {
        if (!handled.contains(dec)) {
            handled.insert(dec);
662
            return dec;
663
664
665
        }
    }

666
    return nullptr;
667
668
}

669
/// If any parent of this context is a class, the closest class declaration is returned, nullptr otherwise
670
Declaration* classDeclarationForContext(const DUContextPointer& context, const CursorInRevision& position)
671
672
673
674
675
676
{
    auto parent = context;
    while (parent) {
        if (parent->type() == DUContext::Class) {
            break;
        }
677
678
679
680
681
682
683
684

        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;
685
                auto decl = findDeclaration(qid, context, position, tmp);
686
687
688
689
690
691
692

                if (decl && decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
                    parent = decl->internalContext();
                    break;
                }
            }
        }
693
694
695
696
697
698
        parent = parent->parentContext();
    }

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

699
700
701
class LookAheadItemMatcher
{
public:
702
    explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
        : 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;
738
        for (const auto& pair: qAsConst(possibleLookAheadDeclarations)) {
739
            auto decl = pair.first;
740
            if (matchedTypes.contains(decl->indexedType())) {
741
742
743
744
                auto parent = pair.second;
                const QString access = parent->abstractType()->whichType() == AbstractType::TypePointer
                                 ? QStringLiteral("->") : QStringLiteral(".");
                const QString text = parent->identifier().toString() + access + decl->identifier().toString();
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
                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;
                }

768
769
                const auto& localDeclarations = typeDecl->internalContext()->localDeclarations();
                for (auto localDecl : localDeclarations) {
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
                    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;
                            }
                        }
                    }

793
                    possibleLookAheadDeclarations.insert({localDecl, declaration});
794
795
796
797
798
799
                }
            }
        }
    }

    // Declaration and it's context
800
    using DeclarationContext = QPair<Declaration*, Declaration*>;
801
802
803
804
805

    /// 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
806
807
    // Second declaration represents context
    QSet<DeclarationContext> possibleLookAheadDeclarations;
808
809
810
811
812
813

    TopDUContextPointer m_topContext;

    bool m_enabled;
};

814
815
816
817
818
819
820
821
822
823
824
struct MemberAccessReplacer : public QObject
{
    Q_OBJECT

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

825
public Q_SLOTS:
826
827
828
829
830
831
832
833
834
835
836
837
    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;
838
                if (type == ArrowToDot) {
839
840
841
842
843
844
845
846
                    oldAccess = QStringLiteral("->");
                    newAccess = QStringLiteral(".");
                } else {
                    oldAccess = QStringLiteral(".");
                    newAccess = QStringLiteral("->");
                }

                auto oldRange = KTextEditor::Range(cursor - KTextEditor::Cursor(0, oldAccess.length()), cursor);
847
848
849
850
851
852
853
854

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

855
856
857
858
859
860
861
862
863
                if (oldRange.start().column() >= 0 && textDocument->text(oldRange) == oldAccess) {
                    textDocument->replaceText(oldRange, newAccess);
                }
            }
        }
    }
};
static MemberAccessReplacer s_memberAccessReplacer;

864
865
}

866
867
Q_DECLARE_METATYPE(MemberAccessReplacer::Type)

868
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context,
Kevin Funk's avatar
Kevin Funk committed
869
                                                       const ParseSessionData::Ptr& sessionData,
870
                                                       const QUrl& url,
Kevin Funk's avatar
Kevin Funk committed
871
                                                       const KTextEditor::Cursor& position,
872
873
                                                       const QString& text,
                                                       const QString& followingText
874
                                                      )
875
    : CodeCompletionContext(context, text + followingText, CursorInRevision::castFromSimpleCursor(position), 0)
876
    , m_results(nullptr, clang_disposeCodeCompleteResults)
Kevin Funk's avatar
Kevin Funk committed
877
    , m_parseSessionData(sessionData)
878
{
879
    qRegisterMetaType<MemberAccessReplacer::Type>();
880
881
    const QByteArray file = url.toLocalFile().toUtf8();
    ParseSession session(m_parseSessionData);
882
883
884
885
886
887
888
889

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

Kevin Funk's avatar
Kevin Funk committed
890
891
892
    {
        const unsigned int completeOptions = clang_defaultCodeCompleteOptions();

893
894
895
896
        CXUnsavedFile unsaved;
        unsaved.Filename = file.constData();
        const QByteArray content = m_text.toUtf8();
        unsaved.Contents = content.constData();
897
898
        unsaved.Length = content.size();

899
        allUnsaved.reserve(otherUnsavedFiles.size() + 1);
900
        for (const auto& f : qAsConst(otherUnsavedFiles)) {
901
902
903
            allUnsaved.append(f.toClangApi());
        }
        allUnsaved.append(unsaved);
904

905
906
        m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
                        position.line() + 1, position.column() + 1,
907
                        allUnsaved.data(), allUnsaved.size(),
908
                        completeOptions));
Kevin Funk's avatar
Kevin Funk committed
909
910

        if (!m_results) {
911
            qCWarning(KDEV_CLANG) << "Something went wrong during 'clang_codeCompleteAt' for file" << file;
912
            return;
Kevin Funk's avatar
Kevin Funk committed
913
        }
914

915
916
917
918
919
920
921
922
923
        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;
924
925
                } else {
                    replacementType = MemberAccessReplacer::DotToArrow;
926
927
928
929
930
931
932
933
934
935
                }

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

                m_valid = false;
                return;
            }
        }

936
937
938
939
        auto addMacros = ClangSettingsManager::self()->codeCompletionSettings().macros;
        if (!addMacros) {
            m_filters |= NoMacros;
        }
940
    }
941

942
943
944
945
946
    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 '->'
947
            m_text = trimmedText.leftRef(trimmedText.size() - 1) + QStringLiteral("->");
948
949
950
951
952

            CXUnsavedFile unsaved;
            unsaved.Filename = file.constData();
            const QByteArray content = m_text.toUtf8();
            unsaved.Contents = content.constData();
953
954
            unsaved.Length = content.size();
            allUnsaved[allUnsaved.size() - 1] = unsaved;
955
956

            m_results.reset(clang_codeCompleteAt(session.unit(), file.constData(),
957
                                                 position.line() + 1, position.column() + 1 + 1,
958
                                                 allUnsaved.data(), allUnsaved.size(),
959
960
961
962
963
964
965
966
967
968
969
970
                                                 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;
        }
    }

971
    // check 'isValidPosition' after parsing the new content
972
973
    auto clangFile = session.file(file);
    if (!isValidPosition(session.unit(), clangFile)) {
974
975
976
        m_valid = false;
        return;
    }
977

978
    m_completionHelper.computeCompletions(session, clangFile, position);
979
980
981
982
983
}

ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
984

985
bool ClangCodeCompletionContext::isValidPosition(CXTranslationUnit unit, CXFile file) const
986
{
987
    if (isInsideComment(unit, file, m_position.castToSimpleCursor())) {
988
        clangDebug() << "Invalid completion context: Inside comment";
989
990
991
        return false;
    }
    return true;
992
993
}

994
QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/)
995
{
996
997
998
999
    if (!m_valid || !m_duContext || !m_results) {
        return {};
    }

1000
1001
    const auto ctx = DUContextPointer(m_duContext->findContextAt(m_position));

Kevin Funk's avatar
Kevin Funk committed
1002
    /// Normal completion items, such as 'void Foo::foo()'
1003
    QList<CompletionTreeItemPointer> items;
Kevin Funk's avatar
Kevin Funk committed
1004
1005
1006
    /// Stuff like 'Foo& Foo::operator=(const Foo&)', etc. Not regularly used by our users.
    QList<CompletionTreeItemPointer> specialItems;
    /// Macros from the current context
1007
    QList<CompletionTreeItemPointer> macros;
Kevin Funk's avatar
Kevin Funk committed
1008
    /// Builtins reported by Clang
1009
    QList<CompletionTreeItemPointer> builtin;
1010

1011
1012
    // two sets of handled declarations to prevent duplicates and make sure we show
    // all available overloads
1013
    QSet<Declaration*> handled;
1014
1015
    // this is only used for the CXCursor_OverloadCandidate completion items
    QSet<Declaration*> overloadsHandled;
1016

1017
1018
    LookAheadItemMatcher lookAheadMatcher(TopDUContextPointer(ctx->topContext()));

1019
1020
1021
    // If ctx is/inside the Class context, this represents that context.
    const auto currentClassContext = classDeclarationForContext(ctx, m_position);

1022
    clangDebug() << "Clang found" << m_results->NumResults << "completion results";
1023

1024
    for (uint i = 0; i < m_results->NumResults; ++i) {
1025
1026
1027
1028
        if (abort) {
            return {};
        }

1029
        auto result = m_results->Results[i];
1030
1031
1032
1033
1034
        #if CINDEX_VERSION_MINOR >= 30
        const bool isOverloadCandidate = result.CursorKind == CXCursor_OverloadCandidate;
        #else
        const bool isOverloadCandidate = false;
        #endif
1035
1036

        const auto availability = clang_getCompletionAvailability(result.CompletionString);
1037
        if (availability == CXAvailability_NotAvailable) {
1038
1039
1040
            continue;
        }

Kevin Funk's avatar
Kevin Funk committed
1041
1042
1043
1044
1045
        const bool isMacroDefinition = result.CursorKind == CXCursor_MacroDefinition;
        if (isMacroDefinition && m_filters & NoMacros) {
            continue;
        }

1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
        const bool isBuiltin = (result.CursorKind == CXCursor_NotImplemented);
        if (isBuiltin && m_filters & NoBuiltins) {
            continue;
        }

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

1056
        if (availability == CXAvailability_NotAccessible && (!isDeclaration || !currentClassContext)) {
1057
1058
1059
            continue;
        }

1060
        // the string that would be needed to type, usually the identifier of something. Also we use it as name for code completion declaration items.
1061
        QString typed;
Milian Wolff's avatar
Milian Wolff committed
1062
        // the return type of a function e.g.
1063
        QString resultType;
Milian Wolff's avatar
Milian Wolff committed
1064
        // the replacement text when an item gets executed
1065
        QString replacement;
1066
1067
1068
1069

        QString arguments;

        ArgumentHintItem::CurrentArgumentRange argumentRange;
Milian Wolff's avatar
Milian Wolff committed
1070
1071
        //BEGIN function signature parsing
        // nesting depth of parentheses
1072
1073
        int parenDepth = 0;
        enum FunctionSignatureState {
Milian Wolff's avatar
Milian Wolff committed
1074
            // not yet inside the function signature
1075
            Before,
Milian Wolff's avatar
Milian Wolff committed
1076
            // any token is part of the function signature now
1077
            Inside,
Milian Wolff's avatar
Milian Wolff committed
1078
            // finished parsing the function signature
1079
1080
            After
        };
Milian Wolff's avatar
Milian Wolff committed
1081
        // current state
1082
        FunctionSignatureState signatureState = Before;
Milian Wolff's avatar
Milian Wolff committed
1083
        //END function signature parsing
Kevin Funk's avatar
Kevin Funk committed
1084

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
        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;
1095
                }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1096
1097
1098
1099

                // 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
1100
                    if (!isOverloadCandidate) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1101
1102
1103
                        break;
                    }
                }
1104

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

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1107
                switch (kind) {
1108
1109
                case CXCompletionChunk_TypedText:
                    typed = string;
1110
                    replacement += string;
1111
1112
1113
1114
1115
                    break;
                case CXCompletionChunk_ResultType:
                    resultType = string;
                    continue;
                case CXCompletionChunk_Placeholder:
1116
                    if (signatureState == Inside) {
1117
                        arguments += string;
1118
                    }
1119
                    continue;
1120
1121
1122
1123
1124
1125
1126
1127
1128
                case CXCompletionChunk_LeftParen:
                    if (signatureState == Before && !parenDepth) {
                        signatureState = Inside;
                    }
                    parenDepth++;
                    break;
                case CXCompletionChunk_RightParen:
                    --parenDepth;
                    if (signatureState == Inside && !parenDepth) {
1129
                        arguments += QLatin1Char(')');
1130
1131
1132
                        signatureState = After;
                    }
                    break;
1133
                case CXCompletionChunk_Text:
1134
                    if (isOverloadCandidate) {
1135
1136
                        typed += string;
                    }
1137
1138
1139
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1140
1141
1142
                    else if (result.CursorKind == CXCursor_EnumConstantDecl) {
                        replacement += string;
                    }
1143
                    break;
1144
1145
1146
1147
                case CXCompletionChunk_CurrentParameter:
                    argumentRange.start = arguments.size();
                    argumentRange.end = string.size();
                    break;
1148
1149
                default:
                    break;
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1150
1151
1152
1153
                }
                if (signatureState == Inside) {
                    arguments += string;
                }
1154
            }
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1155
1156
1157
        };

        processChunks(result.CompletionString);
1158

Sergey Kalinichev's avatar
Sergey Kalinichev committed
1159
        // TODO: No closing paren if default parameters present
1160
        if (isOverloadCandidate && !arguments.endsWith(QLatin1Char(')'))) {
Sergey Kalinichev's avatar
Sergey Kalinichev committed
1161
1162
            arguments += QLatin1Char(')');
        }
Kevin Funk's avatar
Kevin Funk committed
1163
1164
        // ellide text to the right for overly long result types (templates especially)
        elideStringRight(resultType, MAX_RETURN_TYPE_STRING_LENGTH);
1165

1166
1167
1168
        static const auto noIcon = QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                                QStringLiteral("kdevelop/pics/namespace.png")));

Kevin Funk's avatar
Kevin Funk committed
1169
        if (isDeclaration) {
1170
1171
1172
1173
1174
1175
1176
1177
            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);

1178
1179
1180
1181
            if (!isValidCompletionIdentifier(qid)) {
                continue;
            }

1182
1183
1184
1185
1186
1187
            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);
            }

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