context.cpp 51.9 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

Olivier JG's avatar
Olivier JG committed
54
#include "../util/clangdebug.h"
55
#include "../util/clangtypes.h"
56
#include "../util/clangutils.h"
57
#include "../duchain/clangdiagnosticevaluator.h"
Olivier JG's avatar
Olivier JG committed
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
                return QIcon::fromTheme(QStringLiteral("CTparents"));
171 172 173 174
            }
        }
        return CompletionItem<CompletionTreeItem>::data(index, role, model);
    }
175 176 177

    void execute(KTextEditor::View* view, const KTextEditor::Range& word) override
    {
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
        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(';'));
        }

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

private:
    QString m_returnType;
212 213
};

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

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

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

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

        if(!m_declaration){
            return;
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
        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());
        }
405 406
    }
};
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422

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

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

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

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

private:
    CurrentArgumentRange m_range;
    QString m_arguments;
};

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

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

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

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

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

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

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

Kevin Funk's avatar
Kevin Funk committed
525
/**
526
 * @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
527
 *
528
 * See https://clang.llvm.org/doxygen/CodeCompleteConsumer_8h_source.html for list of priorities
Kevin Funk's avatar
Kevin Funk committed
529 530 531 532
 * They (currently) have a range from [-3, 80] (the lower, the better)
 */
int codeCompletionPriorityToMatchQuality(unsigned int completionPriority)
{
533
    return 10u - qBound(0u, completionPriority, 80u) / 8;
Kevin Funk's avatar
Kevin Funk committed
534 535
}

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

    return completionPriority;
}

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

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

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

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

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

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

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

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

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

665
    return nullptr;
666 667
}

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

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

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

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

698 699 700
class LookAheadItemMatcher
{
public:
701
    explicit LookAheadItemMatcher(const TopDUContextPointer& ctx)
702 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
        : 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;
737
        for (const auto& pair: qAsConst(possibleLookAheadDeclarations)) {
738
            auto decl = pair.first;
739
            if (matchedTypes.contains(decl->indexedType())) {
740 741 742 743
                auto parent = pair.second;
                const QString access = parent->abstractType()->whichType() == AbstractType::TypePointer
                                 ? QStringLiteral("->") : QStringLiteral(".");
                const QString text = parent->identifier().toString() + access + decl->identifier().toString();
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
                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;
                }

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

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

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

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

    TopDUContextPointer m_topContext;

    bool m_enabled;
};

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

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

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

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

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

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

863 864
}

865 866
Q_DECLARE_METATYPE(MemberAccessReplacer::Type)

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

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

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

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

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

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

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

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

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

                m_valid = false;
                return;
            }
        }

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

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

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

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

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

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

ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
983

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

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

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

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

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

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

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

Olivier JG's avatar
Olivier JG committed
1021
    clangDebug() << "Clang found" << m_results->NumResults << "completion results";
1022

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

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

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

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

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

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

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

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

        QString arguments;

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

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

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

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

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

        processChunks(result.CompletionString);
1157

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

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

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

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

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

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

1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
            if (found && found->type<FunctionType>() == nullptr && parent.isEmpty() && !resultType.isEmpty()) {
                // workaround: for multiple nameless structs with the same member.
                // Check the type of the member to have at least a higher probability.
                auto typeCheckedDeclaration = found;
                while (typeCheckedDeclaration != nullptr && typeCheckedDeclaration->abstractType() != nullptr) {
                    if (typeCheckedDeclaration->abstractType()->toString() == resultType) {
                        found = typeCheckedDeclaration;
                        break;
                    }
                    typeCheckedDeclaration = findDeclaration(qid, ctx, m_position, isOverloadCandidate ? overloadsHandled : handled);
                }
            }

1202
            CompletionTreeItemPointer item;
1203
            if (found) {
1204 1205 1206 1207 1208 1209 1210
                // 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;
                        }

1211
                        auto declarationClassContext = classDeclarationForContext(DUContextPointer(found->context()), m_position);
1212 1213

                        uint steps = 10;
1214
                        auto inheriters = DUChainUtils::inheriters(declarationClassContext, steps);
1215 1216 1217 1218 1219 1220 1221 1222
                        if(!inheriters.contains(currentClassContext)){
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

1223 1224 1225 1226 1227 1228 1229
                DeclarationItem* declarationItem = nullptr;
                if (isOverloadCandidate) {
                    declarationItem = new ArgumentHintItem(found, resultType, typed, arguments, argumentRange);
                    declarationItem->setArgumentHintDepth(1);
                } else {
                    declarationItem = new DeclarationItem(found, typed, resultType, replacement);
                }
1230

1231 1232
                const unsigned int completionPriority = adjustPriorityForDeclaration(found, clang_getCompletionPriority(result.CompletionString));
                const bool bestMatch = completionPriority <= CCP_SuperCompletion;
1233 1234

                //don't set best match property for internal identifiers, also prefer declarations from current file
1235 1236
                const auto isInternal = found->indexedIdentifier().identifier().toString().startsWith(QLatin1String("__"));
                if (bestMatch && !isInternal ) {